use smudge_core::geometry::Point;
use smudge_core::input::{InputEvent, Key, KeyEvent, Modifiers, PointerButton};
use winit::dpi::PhysicalPosition;
use winit::event::{
ElementState, Ime, KeyEvent as WinitKeyEvent, MouseButton, MouseScrollDelta, WindowEvent,
};
use winit::keyboard::{Key as WinitKey, ModifiersState, NamedKey};
const LINE_HEIGHT: f32 = 40.0;
pub(crate) struct EventTranslator {
modifiers: ModifiersState,
position: Option<Point>,
}
impl EventTranslator {
pub(crate) fn new() -> EventTranslator {
EventTranslator {
modifiers: ModifiersState::empty(),
position: None,
}
}
pub(crate) fn translate(&mut self, event: &WindowEvent, scale: f32) -> Option<InputEvent> {
match event {
WindowEvent::CursorMoved { position, .. } => {
let position = logical_point(*position, scale);
self.position = Some(position);
Some(InputEvent::PointerMoved { position })
}
WindowEvent::CursorLeft { .. } => Some(InputEvent::PointerLeft),
WindowEvent::MouseInput { state, button, .. } => {
let button = pointer_button(*button)?;
let position = self.position?;
Some(match state {
ElementState::Pressed => InputEvent::PointerDown { position, button },
ElementState::Released => InputEvent::PointerUp { position, button },
})
}
WindowEvent::MouseWheel { delta, .. } => Some(InputEvent::Wheel {
delta: wheel_delta(*delta, scale),
}),
WindowEvent::KeyboardInput { event, .. } => Some(InputEvent::Key(key_event(
event,
translate_modifiers(self.modifiers),
))),
WindowEvent::ModifiersChanged(modifiers) => {
self.modifiers = modifiers.state();
Some(InputEvent::ModifiersChanged(translate_modifiers(
self.modifiers,
)))
}
WindowEvent::Ime(Ime::Preedit(text, cursor)) => Some(InputEvent::ImePreedit {
text: text.clone(),
cursor: *cursor,
}),
WindowEvent::Ime(Ime::Commit(text)) => {
Some(InputEvent::ImeCommit { text: text.clone() })
}
WindowEvent::Ime(Ime::Enabled | Ime::Disabled) => None,
WindowEvent::Focused(focused) => Some(InputEvent::WindowFocus(*focused)),
_ => None,
}
}
}
fn logical_point(position: PhysicalPosition<f64>, scale: f32) -> Point {
Point {
x: position.x as f32 / scale,
y: position.y as f32 / scale,
}
}
fn wheel_delta(delta: MouseScrollDelta, scale: f32) -> Point {
let moved = match delta {
MouseScrollDelta::LineDelta(x, y) => Point {
x: x * LINE_HEIGHT,
y: y * LINE_HEIGHT,
},
MouseScrollDelta::PixelDelta(position) => logical_point(position, scale),
};
Point {
x: -moved.x,
y: -moved.y,
}
}
fn pointer_button(button: MouseButton) -> Option<PointerButton> {
match button {
MouseButton::Left => Some(PointerButton::Primary),
MouseButton::Right => Some(PointerButton::Secondary),
MouseButton::Middle => Some(PointerButton::Middle),
MouseButton::Other(_) | MouseButton::Back | MouseButton::Forward => None,
}
}
fn key_name(key: &WinitKey) -> Key {
match key {
WinitKey::Character(text) => Key::Character(text.to_string()),
WinitKey::Named(named) => match named {
NamedKey::Enter => Key::Enter,
NamedKey::Tab => Key::Tab,
NamedKey::Escape => Key::Escape,
NamedKey::Backspace => Key::Backspace,
NamedKey::Delete => Key::Delete,
NamedKey::ArrowLeft => Key::ArrowLeft,
NamedKey::ArrowRight => Key::ArrowRight,
NamedKey::ArrowUp => Key::ArrowUp,
NamedKey::ArrowDown => Key::ArrowDown,
NamedKey::Home => Key::Home,
NamedKey::End => Key::End,
NamedKey::PageUp => Key::PageUp,
NamedKey::PageDown => Key::PageDown,
NamedKey::Space => Key::Space,
_ => Key::Other,
},
WinitKey::Dead(_) | WinitKey::Unidentified(_) => Key::Other,
}
}
fn translate_modifiers(state: ModifiersState) -> Modifiers {
Modifiers {
shift: state.contains(ModifiersState::SHIFT),
control: state.contains(ModifiersState::CONTROL),
alt: state.contains(ModifiersState::ALT),
meta: state.contains(ModifiersState::SUPER),
}
}
fn key_event(event: &WinitKeyEvent, modifiers: Modifiers) -> KeyEvent {
KeyEvent {
key: key_name(&event.logical_key),
pressed: event.state == ElementState::Pressed,
repeat: event.repeat,
modifiers,
text: event.text.as_ref().map(|text| text.to_string()),
}
}
#[cfg(test)]
#[path = "events_tests.rs"]
mod tests;