use crate::{EventCtx, Line, ScreenDims, ScreenPt, TextSpan};
use geom::Duration;
use winit::event::{
ElementState, KeyboardInput, MouseButton, MouseScrollDelta, VirtualKeyCode, WindowEvent,
};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Event {
NoOp,
LeftMouseButtonDown,
LeftMouseButtonUp,
RightMouseButtonDown,
RightMouseButtonUp,
KeyPress(Key),
KeyRelease(Key),
Update(Duration),
MouseMovedTo(ScreenPt),
WindowLostCursor,
WindowGainedCursor,
MouseWheelScroll(f64, f64),
WindowResized(ScreenDims),
}
impl Event {
pub fn from_winit_event(ev: WindowEvent, scale_factor: f64) -> Option<Event> {
match ev {
WindowEvent::MouseInput { state, button, .. } => match (button, state) {
(MouseButton::Left, ElementState::Pressed) => Some(Event::LeftMouseButtonDown),
(MouseButton::Left, ElementState::Released) => Some(Event::LeftMouseButtonUp),
(MouseButton::Right, ElementState::Pressed) => Some(Event::RightMouseButtonDown),
(MouseButton::Right, ElementState::Released) => Some(Event::RightMouseButtonUp),
_ => None,
},
WindowEvent::KeyboardInput { input, .. } => {
if let Some(key) = Key::from_winit_key(input) {
if input.state == ElementState::Pressed {
Some(Event::KeyPress(key))
} else {
Some(Event::KeyRelease(key))
}
} else {
None
}
}
WindowEvent::CursorMoved { position, .. } => Some(Event::MouseMovedTo(
position.to_logical(scale_factor).into(),
)),
WindowEvent::MouseWheel { delta, .. } => match delta {
MouseScrollDelta::LineDelta(dx, dy) => {
if dx == 0.0 && dy == 0.0 {
None
} else {
Some(Event::MouseWheelScroll(f64::from(dx), f64::from(dy)))
}
}
MouseScrollDelta::PixelDelta(pos) => {
let scale_factor = 0.1;
Some(Event::MouseWheelScroll(
scale_factor * pos.x,
scale_factor * pos.y,
))
}
},
WindowEvent::Resized(size) => {
Some(Event::WindowResized(size.to_logical(scale_factor).into()))
}
WindowEvent::Focused(gained) => Some(if gained {
Event::WindowGainedCursor
} else {
Event::WindowLostCursor
}),
_ => None,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Key {
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
Num1,
Num2,
Num3,
Num4,
Num5,
Num6,
Num7,
Num8,
Num9,
Num0,
LeftBracket,
RightBracket,
Space,
Slash,
Dot,
Comma,
Semicolon,
Colon,
Equals,
SingleQuote,
Escape,
Enter,
Tab,
Backspace,
LeftShift,
LeftControl,
LeftAlt,
RightAlt,
LeftArrow,
RightArrow,
UpArrow,
DownArrow,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
}
impl Key {
pub const NUM_KEYS: [Key; 9] = [
Key::Num1,
Key::Num2,
Key::Num3,
Key::Num4,
Key::Num5,
Key::Num6,
Key::Num7,
Key::Num8,
Key::Num9,
];
pub fn to_char(self, shift_pressed: bool) -> Option<char> {
match self {
Key::A => Some(if shift_pressed { 'A' } else { 'a' }),
Key::B => Some(if shift_pressed { 'B' } else { 'b' }),
Key::C => Some(if shift_pressed { 'C' } else { 'c' }),
Key::D => Some(if shift_pressed { 'D' } else { 'd' }),
Key::E => Some(if shift_pressed { 'E' } else { 'e' }),
Key::F => Some(if shift_pressed { 'F' } else { 'f' }),
Key::G => Some(if shift_pressed { 'G' } else { 'g' }),
Key::H => Some(if shift_pressed { 'H' } else { 'h' }),
Key::I => Some(if shift_pressed { 'I' } else { 'i' }),
Key::J => Some(if shift_pressed { 'J' } else { 'j' }),
Key::K => Some(if shift_pressed { 'K' } else { 'k' }),
Key::L => Some(if shift_pressed { 'L' } else { 'l' }),
Key::M => Some(if shift_pressed { 'M' } else { 'm' }),
Key::N => Some(if shift_pressed { 'N' } else { 'n' }),
Key::O => Some(if shift_pressed { 'O' } else { 'o' }),
Key::P => Some(if shift_pressed { 'P' } else { 'p' }),
Key::Q => Some(if shift_pressed { 'Q' } else { 'q' }),
Key::R => Some(if shift_pressed { 'R' } else { 'r' }),
Key::S => Some(if shift_pressed { 'S' } else { 's' }),
Key::T => Some(if shift_pressed { 'T' } else { 't' }),
Key::U => Some(if shift_pressed { 'U' } else { 'u' }),
Key::V => Some(if shift_pressed { 'V' } else { 'v' }),
Key::W => Some(if shift_pressed { 'W' } else { 'w' }),
Key::X => Some(if shift_pressed { 'X' } else { 'x' }),
Key::Y => Some(if shift_pressed { 'Y' } else { 'y' }),
Key::Z => Some(if shift_pressed { 'Z' } else { 'z' }),
Key::Num1 => Some(if shift_pressed { '!' } else { '1' }),
Key::Num2 => Some(if shift_pressed { '@' } else { '2' }),
Key::Num3 => Some(if shift_pressed { '#' } else { '3' }),
Key::Num4 => Some(if shift_pressed { '$' } else { '4' }),
Key::Num5 => Some(if shift_pressed { '%' } else { '5' }),
Key::Num6 => Some(if shift_pressed { '^' } else { '6' }),
Key::Num7 => Some(if shift_pressed { '&' } else { '7' }),
Key::Num8 => Some(if shift_pressed { '*' } else { '8' }),
Key::Num9 => Some(if shift_pressed { '(' } else { '9' }),
Key::Num0 => Some(if shift_pressed { ')' } else { '0' }),
Key::LeftBracket => Some(if shift_pressed { '{' } else { '[' }),
Key::RightBracket => Some(if shift_pressed { '}' } else { ']' }),
Key::Space => Some(' '),
Key::Slash => Some(if shift_pressed { '?' } else { '/' }),
Key::Dot => Some(if shift_pressed { '>' } else { '.' }),
Key::Comma => Some(if shift_pressed { '<' } else { ',' }),
Key::Semicolon => Some(';'),
Key::Colon => Some(':'),
Key::Equals => Some(if shift_pressed { '+' } else { '=' }),
Key::SingleQuote => Some(if shift_pressed { '"' } else { '\'' }),
Key::Escape
| Key::Enter
| Key::Tab
| Key::Backspace
| Key::LeftShift
| Key::LeftControl
| Key::LeftAlt
| Key::RightAlt
| Key::LeftArrow
| Key::RightArrow
| Key::UpArrow
| Key::DownArrow
| Key::F1
| Key::F2
| Key::F3
| Key::F4
| Key::F5
| Key::F6
| Key::F7
| Key::F8
| Key::F9
| Key::F10
| Key::F11
| Key::F12 => None,
}
}
pub fn describe(self) -> String {
match self {
Key::Escape => "Escape".to_string(),
Key::Enter => "Enter".to_string(),
Key::Tab => "Tab".to_string(),
Key::Backspace => "Backspace".to_string(),
Key::LeftShift => "Shift".to_string(),
Key::LeftControl => "left Control".to_string(),
Key::LeftAlt => "left Alt".to_string(),
Key::RightAlt => "right Alt".to_string(),
Key::LeftArrow => "← arrow".to_string(),
Key::RightArrow => "→ arrow".to_string(),
Key::UpArrow => "↑".to_string(),
Key::DownArrow => "↓".to_string(),
Key::F1 => "F1".to_string(),
Key::F2 => "F2".to_string(),
Key::F3 => "F3".to_string(),
Key::F4 => "F4".to_string(),
Key::F5 => "F5".to_string(),
Key::F6 => "F6".to_string(),
Key::F7 => "F7".to_string(),
Key::F8 => "F8".to_string(),
Key::F9 => "F9".to_string(),
Key::F10 => "F10".to_string(),
Key::F11 => "F11".to_string(),
Key::F12 => "F12".to_string(),
Key::Space => "Space".to_string(),
_ => self.to_char(false).unwrap().to_string(),
}
}
fn from_winit_key(input: KeyboardInput) -> Option<Key> {
let key = input.virtual_keycode?;
Some(match key {
VirtualKeyCode::A => Key::A,
VirtualKeyCode::B => Key::B,
VirtualKeyCode::C => Key::C,
VirtualKeyCode::D => Key::D,
VirtualKeyCode::E => Key::E,
VirtualKeyCode::F => Key::F,
VirtualKeyCode::G => Key::G,
VirtualKeyCode::H => Key::H,
VirtualKeyCode::I => Key::I,
VirtualKeyCode::J => Key::J,
VirtualKeyCode::K => Key::K,
VirtualKeyCode::L => Key::L,
VirtualKeyCode::M => Key::M,
VirtualKeyCode::N => Key::N,
VirtualKeyCode::O => Key::O,
VirtualKeyCode::P => Key::P,
VirtualKeyCode::Q => Key::Q,
VirtualKeyCode::R => Key::R,
VirtualKeyCode::S => Key::S,
VirtualKeyCode::T => Key::T,
VirtualKeyCode::U => Key::U,
VirtualKeyCode::V => Key::V,
VirtualKeyCode::W => Key::W,
VirtualKeyCode::X => Key::X,
VirtualKeyCode::Y => Key::Y,
VirtualKeyCode::Z => Key::Z,
VirtualKeyCode::Key1 | VirtualKeyCode::Numpad1 => Key::Num1,
VirtualKeyCode::Key2 | VirtualKeyCode::Numpad2 => Key::Num2,
VirtualKeyCode::Key3 | VirtualKeyCode::Numpad3 => Key::Num3,
VirtualKeyCode::Key4 | VirtualKeyCode::Numpad4 => Key::Num4,
VirtualKeyCode::Key5 | VirtualKeyCode::Numpad5 => Key::Num5,
VirtualKeyCode::Key6 | VirtualKeyCode::Numpad6 => Key::Num6,
VirtualKeyCode::Key7 | VirtualKeyCode::Numpad7 => Key::Num7,
VirtualKeyCode::Key8 | VirtualKeyCode::Numpad8 => Key::Num8,
VirtualKeyCode::Key9 | VirtualKeyCode::Numpad9 => Key::Num9,
VirtualKeyCode::Key0 | VirtualKeyCode::Numpad0 => Key::Num0,
VirtualKeyCode::LBracket => Key::LeftBracket,
VirtualKeyCode::RBracket => Key::RightBracket,
VirtualKeyCode::Space => Key::Space,
VirtualKeyCode::Slash => Key::Slash,
VirtualKeyCode::Period => Key::Dot,
VirtualKeyCode::Comma => Key::Comma,
VirtualKeyCode::Semicolon => Key::Semicolon,
VirtualKeyCode::Colon => Key::Colon,
VirtualKeyCode::Equals => Key::Equals,
VirtualKeyCode::Apostrophe => Key::SingleQuote,
VirtualKeyCode::Escape => Key::Escape,
VirtualKeyCode::Return => Key::Enter,
VirtualKeyCode::Tab => Key::Tab,
VirtualKeyCode::Back => Key::Backspace,
VirtualKeyCode::LShift => Key::LeftShift,
VirtualKeyCode::LControl => Key::LeftControl,
VirtualKeyCode::LAlt => Key::LeftAlt,
VirtualKeyCode::RAlt => Key::RightAlt,
VirtualKeyCode::Left => Key::LeftArrow,
VirtualKeyCode::Right => Key::RightArrow,
VirtualKeyCode::Up => Key::UpArrow,
VirtualKeyCode::Down => Key::DownArrow,
VirtualKeyCode::F1 => Key::F1,
VirtualKeyCode::F2 => Key::F2,
VirtualKeyCode::F3 => Key::F3,
VirtualKeyCode::F4 => Key::F4,
VirtualKeyCode::F5 => Key::F5,
VirtualKeyCode::F6 => Key::F6,
VirtualKeyCode::F7 => Key::F7,
VirtualKeyCode::F8 => Key::F8,
VirtualKeyCode::F9 => Key::F9,
VirtualKeyCode::F10 => Key::F10,
VirtualKeyCode::F11 => Key::F11,
VirtualKeyCode::F12 => Key::F12,
_ => {
println!("Unknown winit key {:?}", key);
return None;
}
})
}
pub fn txt(self, ctx: &EventCtx) -> TextSpan {
Line(self.describe()).fg(ctx.style().hotkey_color)
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum MultiKey {
Normal(Key),
LCtrl(Key),
Any(Vec<Key>),
}
impl MultiKey {
pub fn describe(&self) -> String {
match self {
MultiKey::Normal(key) => key.describe(),
MultiKey::LCtrl(key) => format!("Ctrl+{}", key.describe()),
MultiKey::Any(ref keys) => keys
.iter()
.map(|k| k.describe())
.collect::<Vec<_>>()
.join(", "),
}
}
}
pub fn lctrl(key: Key) -> Option<MultiKey> {
Some(MultiKey::LCtrl(key))
}
pub fn hotkeys(keys: Vec<Key>) -> Option<MultiKey> {
Some(MultiKey::Any(keys))
}
impl std::convert::From<Key> for Option<MultiKey> {
fn from(key: Key) -> Option<MultiKey> {
Some(MultiKey::Normal(key))
}
}