use crate::{Key, KeyInput, Mods};
use winit::event::{ElementState, Event, KeyEvent, Modifiers, WindowEvent};
use winit::keyboard::{Key as WinitKey, ModifiersState, NamedKey};
impl From<&WinitKey> for Key {
fn from(key: &WinitKey) -> Self {
match key {
WinitKey::Named(named) => match named {
NamedKey::Space => Self::Char(' '),
NamedKey::ArrowUp => Self::Up,
NamedKey::ArrowRight => Self::Right,
NamedKey::ArrowDown => Self::Down,
NamedKey::ArrowLeft => Self::Left,
NamedKey::Enter => Self::Enter,
NamedKey::Backspace => Self::Backspace,
NamedKey::Delete => Self::Delete,
NamedKey::Home => Self::Home,
NamedKey::End => Self::End,
NamedKey::PageUp => Self::PageUp,
NamedKey::PageDown => Self::PageDown,
NamedKey::Escape => Self::Esc,
NamedKey::Tab => Self::Tab,
NamedKey::Insert => Self::Insert,
NamedKey::Copy => Self::Copy,
NamedKey::Cut => Self::Cut,
NamedKey::Paste => Self::Paste,
NamedKey::Clear => Self::Clear,
NamedKey::Undo => Self::Undo,
NamedKey::Redo => Self::Redo,
NamedKey::Help => Self::Help,
NamedKey::ZoomIn => Self::ZoomIn,
NamedKey::ZoomOut => Self::ZoomOut,
NamedKey::ZoomToggle => Self::ZoomToggle,
NamedKey::ScrollLock => Self::ScrollLock,
NamedKey::NumLock => Self::NumLock,
NamedKey::PrintScreen => Self::PrintScreen,
NamedKey::ContextMenu => Self::Menu,
NamedKey::MediaPlay => Self::Play,
NamedKey::MediaPause => Self::Pause,
NamedKey::MediaPlayPause => Self::PlayPause,
NamedKey::MediaStop => Self::Stop,
NamedKey::MediaRewind => Self::Rewind,
NamedKey::MediaTrackNext => Self::NextTrack,
NamedKey::MediaTrackPrevious => Self::PrevTrack,
NamedKey::AudioVolumeUp => Self::VolumeUp,
NamedKey::AudioVolumeDown => Self::VolumeDown,
NamedKey::AudioVolumeMute => Self::Mute,
NamedKey::F1 => Self::F1,
NamedKey::F2 => Self::F2,
NamedKey::F3 => Self::F3,
NamedKey::F4 => Self::F4,
NamedKey::F5 => Self::F5,
NamedKey::F6 => Self::F6,
NamedKey::F7 => Self::F7,
NamedKey::F8 => Self::F8,
NamedKey::F9 => Self::F9,
NamedKey::F10 => Self::F10,
NamedKey::F11 => Self::F11,
NamedKey::F12 => Self::F12,
NamedKey::F13 => Self::F13,
NamedKey::F14 => Self::F14,
NamedKey::F15 => Self::F15,
NamedKey::F16 => Self::F16,
NamedKey::F17 => Self::F17,
NamedKey::F18 => Self::F18,
NamedKey::F19 => Self::F19,
NamedKey::F20 => Self::F20,
NamedKey::F21 => Self::F21,
NamedKey::F22 => Self::F22,
NamedKey::F23 => Self::F23,
NamedKey::F24 => Self::F24,
NamedKey::F25 => Self::F25,
NamedKey::F26 => Self::F26,
NamedKey::F27 => Self::F27,
NamedKey::F28 => Self::F28,
NamedKey::F29 => Self::F29,
NamedKey::F30 => Self::F30,
NamedKey::F31 => Self::F31,
NamedKey::F32 => Self::F32,
NamedKey::F33 => Self::F33,
NamedKey::F34 => Self::F34,
NamedKey::F35 => Self::F35,
NamedKey::Alt
| NamedKey::Control
| NamedKey::Shift
| NamedKey::Super
| NamedKey::Hyper
| NamedKey::Meta
| NamedKey::Symbol => Self::Ignored,
_ => Self::Unidentified,
},
WinitKey::Character(s) => {
let mut chars = s.chars();
if let (Some(c), None) = (chars.next(), chars.next()) {
Self::Char(c)
} else {
Self::Unidentified
}
}
_ => Self::Unidentified,
}
}
}
impl From<WinitKey> for Key {
fn from(key: WinitKey) -> Self {
Self::from(&key)
}
}
impl From<&ModifiersState> for Mods {
fn from(state: &ModifiersState) -> Self {
let mut mods = Mods::NONE;
if state.contains(ModifiersState::CONTROL) {
mods |= Mods::CTRL;
}
if state.contains(ModifiersState::ALT) {
mods |= Mods::ALT;
}
if state.contains(ModifiersState::SUPER) {
mods |= Mods::SUPER;
}
if state.contains(ModifiersState::SHIFT) {
mods |= Mods::SHIFT;
}
mods
}
}
impl From<ModifiersState> for Mods {
fn from(state: ModifiersState) -> Self {
Self::from(&state)
}
}
impl From<&Modifiers> for Mods {
fn from(mods: &Modifiers) -> Self {
Self::from(mods.state())
}
}
pub trait WinitEvent {
fn to_key_input(&self, conv: &mut WinitEventConverter) -> KeyInput;
}
impl WinitEvent for KeyEvent {
fn to_key_input(&self, conv: &mut WinitEventConverter) -> KeyInput {
KeyInput::new(Key::from(&self.logical_key), conv.mods)
}
}
impl WinitEvent for WindowEvent {
fn to_key_input(&self, conv: &mut WinitEventConverter) -> KeyInput {
match self {
WindowEvent::ModifiersChanged(mods) => {
conv.on_modifiers_changed(mods);
Key::Ignored.into()
}
WindowEvent::KeyboardInput { event, .. } if event.state == ElementState::Pressed => {
event.to_key_input(conv)
}
_ => Key::Ignored.into(),
}
}
}
impl<T> WinitEvent for Event<T> {
fn to_key_input(&self, conv: &mut WinitEventConverter) -> KeyInput {
if let Event::WindowEvent { event, .. } = self {
event.to_key_input(conv)
} else {
Key::Ignored.into()
}
}
}
#[derive(Default)]
pub struct WinitEventConverter {
mods: Mods,
}
impl WinitEventConverter {
pub fn mods(&self) -> Mods {
self.mods
}
pub fn on_modifiers_changed(&mut self, mods: &Modifiers) {
self.mods = mods.into();
}
pub fn convert<E: WinitEvent>(&mut self, event: &E) -> KeyInput {
event.to_key_input(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use winit::keyboard::NativeKey;
use NamedKey::*;
use WinitKey::*;
#[test]
fn convert_key() {
assert_eq!(Key::from(Named(Space)), Key::Char(' '));
assert_eq!(Key::from(Named(ArrowUp)), Key::Up);
assert_eq!(Key::from(Named(F1)), Key::F1);
assert_eq!(Key::from(Named(Control)), Key::Ignored);
assert_eq!(Key::from(Named(TVInput)), Key::Unidentified);
assert_eq!(Key::from(Character("a".into())), Key::Char('a'));
assert_eq!(Key::from(Character("A".into())), Key::Char('A'));
assert_eq!(Key::from(Character("foo".into())), Key::Unidentified);
assert_eq!(
Key::from(Unidentified(NativeKey::Unidentified)),
Key::Unidentified,
);
assert_eq!(Key::from(Dead(None)), Key::Unidentified);
}
#[test]
fn convert_modifiers_state() {
assert_eq!(Mods::from(ModifiersState::CONTROL), Mods::CTRL);
assert_eq!(
Mods::from(ModifiersState::CONTROL | ModifiersState::ALT | ModifiersState::SHIFT),
Mods::CTRL | Mods::ALT | Mods::SHIFT,
);
assert_eq!(Mods::from(ModifiersState::SUPER), Mods::SUPER);
}
impl WinitEvent for WinitKey {
fn to_key_input(&self, conv: &mut WinitEventConverter) -> KeyInput {
let key: Key = self.into();
KeyInput::new(key, conv.mods)
}
}
#[test]
fn converter_convert_keys() {
let mut conv = WinitEventConverter::default();
assert_eq!(conv.mods(), Mods::NONE);
assert_eq!(conv.convert(&Named(Space)), KeyInput::new(' ', Mods::NONE));
assert_eq!(
conv.convert(&Character("(".into())),
KeyInput::new('(', Mods::NONE),
);
assert_eq!(
conv.convert(&Character("A".into())),
KeyInput::new('A', Mods::NONE),
);
assert_eq!(
conv.convert(&Named(TVInputHDMI1)),
KeyInput::new(Key::Unidentified, Mods::NONE),
);
assert_eq!(
conv.convert(&Named(Control)),
KeyInput::new(Key::Ignored, Mods::NONE),
);
conv.on_modifiers_changed(&ModifiersState::CONTROL.into());
assert_eq!(
conv.convert(&Character("x".into())),
KeyInput::new('x', Mods::CTRL),
);
}
}