diff --git a/Cargo.toml b/Cargo.toml index d65620d493..5b15d56f9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -104,6 +104,7 @@ features = [ 'console', "AddEventListenerOptions", 'CssStyleDeclaration', + 'CompositionEvent', 'BeforeUnloadEvent', 'Document', 'DomRect', diff --git a/src/event.rs b/src/event.rs index 7e172cad47..22806162a2 100644 --- a/src/event.rs +++ b/src/event.rs @@ -618,7 +618,7 @@ pub enum DeviceEvent { /// repeat or the initial keypress. An application may emulate this by, for /// example keeping a Map/Set of pressed keys and determining whether a keypress /// corresponds to an already pressed key. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct RawKeyEvent { pub physical_key: keyboard::KeyCode, diff --git a/src/keyboard.rs b/src/keyboard.rs index 28983cfaf4..21086557b0 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -169,17 +169,18 @@ mod modifiers_serde { } /// Contains the platform-native physical key identifier (aka scancode) -#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum NativeKeyCode { Unidentified, Windows(u16), MacOS(u32), XKB(u32), + Web(String), } impl std::fmt::Debug for NativeKeyCode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use NativeKeyCode::{MacOS, Unidentified, Windows, XKB}; + use NativeKeyCode::{MacOS, Unidentified, Web, Windows, XKB}; let mut debug_tuple; match self { Unidentified => { @@ -197,6 +198,10 @@ impl std::fmt::Debug for NativeKeyCode { debug_tuple = f.debug_tuple(name_of!(XKB)); debug_tuple.field(v); } + Web(v) => { + debug_tuple = f.debug_tuple(name_of!(Web)); + debug_tuple.field(v); + } } debug_tuple.finish() } @@ -213,7 +218,7 @@ impl std::fmt::Debug for NativeKeyCode { /// /// [`KeyboardEvent.code`]: https://w3c.github.io/uievents-code/#code-value-tables #[non_exhaustive] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum KeyCode { /// This variant is used when the key cannot be translated to any @@ -673,7 +678,7 @@ pub enum KeyCode { /// /// [`KeyboardEvent.key`]: https://w3c.github.io/uievents-key/ #[non_exhaustive] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Key<'a> { /// A key string that corresponds to the character typed by the user, taking into account the diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index 367556a7a1..d6578b0ff0 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -1,24 +1,49 @@ -use super::{super::monitor, backend, device, proxy::Proxy, runner, window}; -use crate::dpi::{PhysicalSize, Size}; -use crate::event::{ - DeviceEvent, DeviceId, ElementState, Event, KeyboardInput, TouchPhase, WindowEvent, +use super::{ + super::{monitor, KeyEventExtra}, + backend, device, + proxy::Proxy, + runner, window, }; +use crate::dpi::{PhysicalSize, Size}; +use crate::event::{DeviceEvent, DeviceId, ElementState, Event, KeyEvent, TouchPhase, WindowEvent}; use crate::event_loop::ControlFlow; +use crate::keyboard::ModifiersState; use crate::monitor::MonitorHandle as RootMH; use crate::window::{Theme, WindowId}; -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::clone::Clone; use std::collections::{vec_deque::IntoIter as VecDequeIter, VecDeque}; use std::rc::Rc; +#[derive(Default)] +struct ModifiersShared(Rc>); + +impl ModifiersShared { + fn set(&self, new: ModifiersState) { + self.0.set(new) + } + + fn get(&self) -> ModifiersState { + self.0.get() + } +} + +impl Clone for ModifiersShared { + fn clone(&self) -> Self { + Self(Rc::clone(&self.0)) + } +} + pub struct WindowTarget { pub(crate) runner: runner::Shared, + modifiers: ModifiersShared, } impl Clone for WindowTarget { fn clone(&self) -> Self { WindowTarget { runner: self.runner.clone(), + modifiers: self.modifiers.clone(), } } } @@ -27,6 +52,7 @@ impl WindowTarget { pub fn new() -> Self { WindowTarget { runner: runner::Shared::new(), + modifiers: ModifiersShared::default(), } } @@ -68,47 +94,87 @@ impl WindowTarget { }); let runner = self.runner.clone(); - canvas.on_keyboard_press(move |scancode, virtual_keycode, modifiers| { - #[allow(deprecated)] - runner.send_event(Event::WindowEvent { - window_id: WindowId(id), - event: WindowEvent::KeyboardInput { - device_id: DeviceId(unsafe { device::Id::dummy() }), - input: KeyboardInput { - scancode, - state: ElementState::Pressed, - virtual_keycode, - modifiers, - }, - is_synthetic: false, - }, - }); - }); + let modifiers = self.modifiers.clone(); + canvas.on_keyboard_press( + move |physical_key, logical_key, text, location, repeat, new_modifiers| { + let active_modifiers = modifiers.get() | new_modifiers; + let modifiers_changed = if modifiers.get() != active_modifiers { + modifiers.set(active_modifiers); + Some(Event::WindowEvent { + window_id: WindowId(id), + event: WindowEvent::ModifiersChanged(active_modifiers), + }) + } else { + None + }; + + runner.send_events( + std::iter::once(Event::WindowEvent { + window_id: WindowId(id), + event: WindowEvent::KeyboardInput { + device_id: DeviceId(unsafe { device::Id::dummy() }), + event: KeyEvent { + physical_key, + logical_key, + text, + location, + state: ElementState::Pressed, + repeat, + platform_specific: KeyEventExtra, + }, + is_synthetic: false, + }, + }) + .chain(modifiers_changed), + ); + }, + ); let runner = self.runner.clone(); - canvas.on_keyboard_release(move |scancode, virtual_keycode, modifiers| { - #[allow(deprecated)] - runner.send_event(Event::WindowEvent { - window_id: WindowId(id), - event: WindowEvent::KeyboardInput { - device_id: DeviceId(unsafe { device::Id::dummy() }), - input: KeyboardInput { - scancode, - state: ElementState::Released, - virtual_keycode, - modifiers, - }, - is_synthetic: false, - }, - }); - }); + let modifiers = self.modifiers.clone(); + canvas.on_keyboard_release( + move |physical_key, logical_key, text, location, repeat, new_modifiers| { + let active_modifiers = modifiers.get() & !new_modifiers; + let modifiers_changed = if modifiers.get() != active_modifiers { + modifiers.set(active_modifiers); + Some(Event::WindowEvent { + window_id: WindowId(id), + event: WindowEvent::ModifiersChanged(active_modifiers), + }) + } else { + None + }; + + runner.send_events( + std::iter::once(Event::WindowEvent { + window_id: WindowId(id), + event: WindowEvent::KeyboardInput { + device_id: DeviceId(unsafe { device::Id::dummy() }), + event: KeyEvent { + physical_key, + logical_key, + text, + location, + state: ElementState::Released, + repeat, + platform_specific: KeyEventExtra, + }, + is_synthetic: false, + }, + }) + .chain(modifiers_changed), + ) + }, + ); let runner = self.runner.clone(); - canvas.on_received_character(move |char_code| { - runner.send_event(Event::WindowEvent { - window_id: WindowId(id), - event: WindowEvent::ReceivedCharacter(char_code), - }); + canvas.on_composition_end(move |data| { + if let Some(data) = data { + runner.send_event(Event::WindowEvent { + window_id: WindowId(id), + event: WindowEvent::ReceivedImeText(data), + }); + } }); let runner = self.runner.clone(); diff --git a/src/platform_impl/web/keyboard.rs b/src/platform_impl/web/keyboard.rs new file mode 100644 index 0000000000..6938f1c4a7 --- /dev/null +++ b/src/platform_impl/web/keyboard.rs @@ -0,0 +1,520 @@ +use crate::keyboard::{Key, KeyCode, NativeKeyCode}; + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub(crate) struct KeyEventExtra; + +impl<'a> Key<'a> { + pub(crate) fn from_key_attribute_value(kav: &'a str) -> Self { + match kav { + "Unidentified" => Key::Unidentified(NativeKeyCode::Web(kav.to_string())), + "Dead" => Key::Dead(None), + "Alt" => Key::Alt, + "AltGraph" => Key::AltGraph, + "CapsLock" => Key::CapsLock, + "Control" => Key::Control, + "Fn" => Key::Fn, + "FnLock" => Key::FnLock, + "NumLock" => Key::NumLock, + "ScrollLock" => Key::ScrollLock, + "Shift" => Key::Shift, + "Symbol" => Key::Symbol, + "SymbolLock" => Key::SymbolLock, + "Hyper" => Key::Hyper, + "Meta" => Key::Super, + "Enter" => Key::Enter, + "Tab" => Key::Tab, + " " => Key::Space, + "ArrowDown" => Key::ArrowDown, + "ArrowLeft" => Key::ArrowLeft, + "ArrowRight" => Key::ArrowRight, + "ArrowUp" => Key::ArrowUp, + "End" => Key::End, + "Home" => Key::Home, + "PageDown" => Key::PageDown, + "PageUp" => Key::PageUp, + "Backspace" => Key::Backspace, + "Clear" => Key::Clear, + "Copy" => Key::Copy, + "CrSel" => Key::CrSel, + "Cut" => Key::Cut, + "Delete" => Key::Delete, + "EraseEof" => Key::EraseEof, + "ExSel" => Key::ExSel, + "Insert" => Key::Insert, + "Paste" => Key::Paste, + "Redo" => Key::Redo, + "Undo" => Key::Undo, + "Accept" => Key::Accept, + "Again" => Key::Again, + "Attn" => Key::Attn, + "Cancel" => Key::Cancel, + "ContextMenu" => Key::ContextMenu, + "Escape" => Key::Escape, + "Execute" => Key::Execute, + "Find" => Key::Find, + "Help" => Key::Help, + "Pause" => Key::Pause, + "Play" => Key::Play, + "Props" => Key::Props, + "Select" => Key::Select, + "ZoomIn" => Key::ZoomIn, + "ZoomOut" => Key::ZoomOut, + "BrightnessDown" => Key::BrightnessDown, + "BrightnessUp" => Key::BrightnessUp, + "Eject" => Key::Eject, + "LogOff" => Key::LogOff, + "Power" => Key::Power, + "PowerOff" => Key::PowerOff, + "PrintScreen" => Key::PrintScreen, + "Hibernate" => Key::Hibernate, + "Standby" => Key::Standby, + "WakeUp" => Key::WakeUp, + "AllCandidates" => Key::AllCandidates, + "Alphanumeric" => Key::Alphanumeric, + "CodeInput" => Key::CodeInput, + "Compose" => Key::Compose, + "Convert" => Key::Convert, + "FinalMode" => Key::FinalMode, + "GroupFirst" => Key::GroupFirst, + "GroupLast" => Key::GroupLast, + "GroupNext" => Key::GroupNext, + "GroupPrevious" => Key::GroupPrevious, + "ModeChange" => Key::ModeChange, + "NextCandidate" => Key::NextCandidate, + "NonConvert" => Key::NonConvert, + "PreviousCandidate" => Key::PreviousCandidate, + "Process" => Key::Process, + "SingleCandidate" => Key::SingleCandidate, + "HangulMode" => Key::HangulMode, + "HanjaMode" => Key::HanjaMode, + "JunjaMode" => Key::JunjaMode, + "Eisu" => Key::Eisu, + "Hankaku" => Key::Hankaku, + "Hiragana" => Key::Hiragana, + "HiraganaKatakana" => Key::HiraganaKatakana, + "KanaMode" => Key::KanaMode, + "KanjiMode" => Key::KanjiMode, + "Katakana" => Key::Katakana, + "Romaji" => Key::Romaji, + "Zenkaku" => Key::Zenkaku, + "ZenkakuHankaku" => Key::ZenkakuHankaku, + "Soft1" => Key::Soft1, + "Soft2" => Key::Soft2, + "Soft3" => Key::Soft3, + "Soft4" => Key::Soft4, + "ChannelDown" => Key::ChannelDown, + "ChannelUp" => Key::ChannelUp, + "Close" => Key::Close, + "MailForward" => Key::MailForward, + "MailReply" => Key::MailReply, + "MailSend" => Key::MailSend, + "MediaClose" => Key::MediaClose, + "MediaFastForward" => Key::MediaFastForward, + "MediaPause" => Key::MediaPause, + "MediaPlay" => Key::MediaPlay, + "MediaPlayPause" => Key::MediaPlayPause, + "MediaRecord" => Key::MediaRecord, + "MediaRewind" => Key::MediaRewind, + "MediaStop" => Key::MediaStop, + "MediaTrackNext" => Key::MediaTrackNext, + "MediaTrackPrevious" => Key::MediaTrackPrevious, + "New" => Key::New, + "Open" => Key::Open, + "Print" => Key::Print, + "Save" => Key::Save, + "SpellCheck" => Key::SpellCheck, + "Key11" => Key::Key11, + "Key12" => Key::Key12, + "AudioBalanceLeft" => Key::AudioBalanceLeft, + "AudioBalanceRight" => Key::AudioBalanceRight, + "AudioBassBoostDown" => Key::AudioBassBoostDown, + "AudioBassBoostToggle" => Key::AudioBassBoostToggle, + "AudioBassBoostUp" => Key::AudioBassBoostUp, + "AudioFaderFront" => Key::AudioFaderFront, + "AudioFaderRear" => Key::AudioFaderRear, + "AudioSurroundModeNext" => Key::AudioSurroundModeNext, + "AudioTrebleDown" => Key::AudioTrebleDown, + "AudioTrebleUp" => Key::AudioTrebleUp, + "AudioVolumeDown" => Key::AudioVolumeDown, + "AudioVolumeUp" => Key::AudioVolumeUp, + "AudioVolumeMute" => Key::AudioVolumeMute, + "MicrophoneToggle" => Key::MicrophoneToggle, + "MicrophoneVolumeDown" => Key::MicrophoneVolumeDown, + "MicrophoneVolumeUp" => Key::MicrophoneVolumeUp, + "MicrophoneVolumeMute" => Key::MicrophoneVolumeMute, + "SpeechCorrectionList" => Key::SpeechCorrectionList, + "SpeechInputToggle" => Key::SpeechInputToggle, + "LaunchApplication1" => Key::LaunchApplication1, + "LaunchApplication2" => Key::LaunchApplication2, + "LaunchCalendar" => Key::LaunchCalendar, + "LaunchContacts" => Key::LaunchContacts, + "LaunchMail" => Key::LaunchMail, + "LaunchMediaPlayer" => Key::LaunchMediaPlayer, + "LaunchMusicPlayer" => Key::LaunchMusicPlayer, + "LaunchPhone" => Key::LaunchPhone, + "LaunchScreenSaver" => Key::LaunchScreenSaver, + "LaunchSpreadsheet" => Key::LaunchSpreadsheet, + "LaunchWebBrowser" => Key::LaunchWebBrowser, + "LaunchWebCam" => Key::LaunchWebCam, + "LaunchWordProcessor" => Key::LaunchWordProcessor, + "BrowserBack" => Key::BrowserBack, + "BrowserFavorites" => Key::BrowserFavorites, + "BrowserForward" => Key::BrowserForward, + "BrowserHome" => Key::BrowserHome, + "BrowserRefresh" => Key::BrowserRefresh, + "BrowserSearch" => Key::BrowserSearch, + "BrowserStop" => Key::BrowserStop, + "AppSwitch" => Key::AppSwitch, + "Call" => Key::Call, + "Camera" => Key::Camera, + "CameraFocus" => Key::CameraFocus, + "EndCall" => Key::EndCall, + "GoBack" => Key::GoBack, + "GoHome" => Key::GoHome, + "HeadsetHook" => Key::HeadsetHook, + "LastNumberRedial" => Key::LastNumberRedial, + "Notification" => Key::Notification, + "MannerMode" => Key::MannerMode, + "VoiceDial" => Key::VoiceDial, + "TV" => Key::TV, + "TV3DMode" => Key::TV3DMode, + "TVAntennaCable" => Key::TVAntennaCable, + "TVAudioDescription" => Key::TVAudioDescription, + "TVAudioDescriptionMixDown" => Key::TVAudioDescriptionMixDown, + "TVAudioDescriptionMixUp" => Key::TVAudioDescriptionMixUp, + "TVContentsMenu" => Key::TVContentsMenu, + "TVDataService" => Key::TVDataService, + "TVInput" => Key::TVInput, + "TVInputComponent1" => Key::TVInputComponent1, + "TVInputComponent2" => Key::TVInputComponent2, + "TVInputComposite1" => Key::TVInputComposite1, + "TVInputComposite2" => Key::TVInputComposite2, + "TVInputHDMI1" => Key::TVInputHDMI1, + "TVInputHDMI2" => Key::TVInputHDMI2, + "TVInputHDMI3" => Key::TVInputHDMI3, + "TVInputHDMI4" => Key::TVInputHDMI4, + "TVInputVGA1" => Key::TVInputVGA1, + "TVMediaContext" => Key::TVMediaContext, + "TVNetwork" => Key::TVNetwork, + "TVNumberEntry" => Key::TVNumberEntry, + "TVPower" => Key::TVPower, + "TVRadioService" => Key::TVRadioService, + "TVSatellite" => Key::TVSatellite, + "TVSatelliteBS" => Key::TVSatelliteBS, + "TVSatelliteCS" => Key::TVSatelliteCS, + "TVSatelliteToggle" => Key::TVSatelliteToggle, + "TVTerrestrialAnalog" => Key::TVTerrestrialAnalog, + "TVTerrestrialDigital" => Key::TVTerrestrialDigital, + "TVTimer" => Key::TVTimer, + "AVRInput" => Key::AVRInput, + "AVRPower" => Key::AVRPower, + "ColorF0Red" => Key::ColorF0Red, + "ColorF1Green" => Key::ColorF1Green, + "ColorF2Yellow" => Key::ColorF2Yellow, + "ColorF3Blue" => Key::ColorF3Blue, + "ColorF4Grey" => Key::ColorF4Grey, + "ColorF5Brown" => Key::ColorF5Brown, + "ClosedCaptionToggle" => Key::ClosedCaptionToggle, + "Dimmer" => Key::Dimmer, + "DisplaySwap" => Key::DisplaySwap, + "DVR" => Key::DVR, + "Exit" => Key::Exit, + "FavoriteClear0" => Key::FavoriteClear0, + "FavoriteClear1" => Key::FavoriteClear1, + "FavoriteClear2" => Key::FavoriteClear2, + "FavoriteClear3" => Key::FavoriteClear3, + "FavoriteRecall0" => Key::FavoriteRecall0, + "FavoriteRecall1" => Key::FavoriteRecall1, + "FavoriteRecall2" => Key::FavoriteRecall2, + "FavoriteRecall3" => Key::FavoriteRecall3, + "FavoriteStore0" => Key::FavoriteStore0, + "FavoriteStore1" => Key::FavoriteStore1, + "FavoriteStore2" => Key::FavoriteStore2, + "FavoriteStore3" => Key::FavoriteStore3, + "Guide" => Key::Guide, + "GuideNextDay" => Key::GuideNextDay, + "GuidePreviousDay" => Key::GuidePreviousDay, + "Info" => Key::Info, + "InstantReplay" => Key::InstantReplay, + "Link" => Key::Link, + "ListProgram" => Key::ListProgram, + "LiveContent" => Key::LiveContent, + "Lock" => Key::Lock, + "MediaApps" => Key::MediaApps, + "MediaAudioTrack" => Key::MediaAudioTrack, + "MediaLast" => Key::MediaLast, + "MediaSkipBackward" => Key::MediaSkipBackward, + "MediaSkipForward" => Key::MediaSkipForward, + "MediaStepBackward" => Key::MediaStepBackward, + "MediaStepForward" => Key::MediaStepForward, + "MediaTopMenu" => Key::MediaTopMenu, + "NavigateIn" => Key::NavigateIn, + "NavigateNext" => Key::NavigateNext, + "NavigateOut" => Key::NavigateOut, + "NavigatePrevious" => Key::NavigatePrevious, + "NextFavoriteChannel" => Key::NextFavoriteChannel, + "NextUserProfile" => Key::NextUserProfile, + "OnDemand" => Key::OnDemand, + "Pairing" => Key::Pairing, + "PinPDown" => Key::PinPDown, + "PinPMove" => Key::PinPMove, + "PinPToggle" => Key::PinPToggle, + "PinPUp" => Key::PinPUp, + "PlaySpeedDown" => Key::PlaySpeedDown, + "PlaySpeedReset" => Key::PlaySpeedReset, + "PlaySpeedUp" => Key::PlaySpeedUp, + "RandomToggle" => Key::RandomToggle, + "RcLowBattery" => Key::RcLowBattery, + "RecordSpeedNext" => Key::RecordSpeedNext, + "RfBypass" => Key::RfBypass, + "ScanChannelsToggle" => Key::ScanChannelsToggle, + "ScreenModeNext" => Key::ScreenModeNext, + "Settings" => Key::Settings, + "SplitScreenToggle" => Key::SplitScreenToggle, + "STBInput" => Key::STBInput, + "STBPower" => Key::STBPower, + "Subtitle" => Key::Subtitle, + "Teletext" => Key::Teletext, + "VideoModeNext" => Key::VideoModeNext, + "Wink" => Key::Wink, + "ZoomToggle" => Key::ZoomToggle, + "F1" => Key::F1, + "F2" => Key::F2, + "F3" => Key::F3, + "F4" => Key::F4, + "F5" => Key::F5, + "F6" => Key::F6, + "F7" => Key::F7, + "F8" => Key::F8, + "F9" => Key::F9, + "F10" => Key::F10, + "F11" => Key::F11, + "F12" => Key::F12, + "F13" => Key::F13, + "F14" => Key::F14, + "F15" => Key::F15, + "F16" => Key::F16, + "F17" => Key::F17, + "F18" => Key::F18, + "F19" => Key::F19, + "F20" => Key::F20, + "F21" => Key::F21, + "F22" => Key::F22, + "F23" => Key::F23, + "F24" => Key::F24, + "F25" => Key::F25, + "F26" => Key::F26, + "F27" => Key::F27, + "F28" => Key::F28, + "F29" => Key::F29, + "F30" => Key::F30, + "F31" => Key::F31, + "F32" => Key::F32, + "F33" => Key::F33, + "F34" => Key::F34, + "F35" => Key::F35, + string @ _ => Key::Character(string), + } + } +} + +impl KeyCode { + pub fn from_key_code_attribute_value(kcav: &str) -> Self { + match kcav { + "Backquote" => KeyCode::Backquote, + "Backslash" => KeyCode::Backslash, + "BracketLeft" => KeyCode::BracketLeft, + "BracketRight" => KeyCode::BracketRight, + "Comma" => KeyCode::Comma, + "Digit0" => KeyCode::Digit0, + "Digit1" => KeyCode::Digit1, + "Digit2" => KeyCode::Digit2, + "Digit3" => KeyCode::Digit3, + "Digit4" => KeyCode::Digit4, + "Digit5" => KeyCode::Digit5, + "Digit6" => KeyCode::Digit6, + "Digit7" => KeyCode::Digit7, + "Digit8" => KeyCode::Digit8, + "Digit9" => KeyCode::Digit9, + "Equal" => KeyCode::Equal, + "IntlBackslash" => KeyCode::IntlBackslash, + "IntlRo" => KeyCode::IntlRo, + "IntlYen" => KeyCode::IntlYen, + "KeyA" => KeyCode::KeyA, + "KeyB" => KeyCode::KeyB, + "KeyC" => KeyCode::KeyC, + "KeyD" => KeyCode::KeyD, + "KeyE" => KeyCode::KeyE, + "KeyF" => KeyCode::KeyF, + "KeyG" => KeyCode::KeyG, + "KeyH" => KeyCode::KeyH, + "KeyI" => KeyCode::KeyI, + "KeyJ" => KeyCode::KeyJ, + "KeyK" => KeyCode::KeyK, + "KeyL" => KeyCode::KeyL, + "KeyM" => KeyCode::KeyM, + "KeyN" => KeyCode::KeyN, + "KeyO" => KeyCode::KeyO, + "KeyP" => KeyCode::KeyP, + "KeyQ" => KeyCode::KeyQ, + "KeyR" => KeyCode::KeyR, + "KeyS" => KeyCode::KeyS, + "KeyT" => KeyCode::KeyT, + "KeyU" => KeyCode::KeyU, + "KeyV" => KeyCode::KeyV, + "KeyW" => KeyCode::KeyW, + "KeyX" => KeyCode::KeyX, + "KeyY" => KeyCode::KeyY, + "KeyZ" => KeyCode::KeyZ, + "Minus" => KeyCode::Minus, + "Period" => KeyCode::Period, + "Quote" => KeyCode::Quote, + "Semicolon" => KeyCode::Semicolon, + "Slash" => KeyCode::Slash, + "AltLeft" => KeyCode::AltLeft, + "AltRight" => KeyCode::AltRight, + "Backspace" => KeyCode::Backspace, + "CapsLock" => KeyCode::CapsLock, + "ContextMenu" => KeyCode::ContextMenu, + "ControlLeft" => KeyCode::ControlLeft, + "ControlRight" => KeyCode::ControlRight, + "Enter" => KeyCode::Enter, + "MetaLeft" => KeyCode::SuperLeft, + "MetaRight" => KeyCode::SuperRight, + "ShiftLeft" => KeyCode::ShiftLeft, + "ShiftRight" => KeyCode::ShiftRight, + "Space" => KeyCode::Space, + "Tab" => KeyCode::Tab, + "Convert" => KeyCode::Convert, + "KanaMode" => KeyCode::KanaMode, + "Lang1" => KeyCode::Lang1, + "Lang2" => KeyCode::Lang2, + "Lang3" => KeyCode::Lang3, + "Lang4" => KeyCode::Lang4, + "Lang5" => KeyCode::Lang5, + "NonConvert" => KeyCode::NonConvert, + "Delete" => KeyCode::Delete, + "End" => KeyCode::End, + "Help" => KeyCode::Help, + "Home" => KeyCode::Home, + "Insert" => KeyCode::Insert, + "PageDown" => KeyCode::PageDown, + "PageUp" => KeyCode::PageUp, + "ArrowDown" => KeyCode::ArrowDown, + "ArrowLeft" => KeyCode::ArrowLeft, + "ArrowRight" => KeyCode::ArrowRight, + "ArrowUp" => KeyCode::ArrowUp, + "NumLock" => KeyCode::NumLock, + "Numpad0" => KeyCode::Numpad0, + "Numpad1" => KeyCode::Numpad1, + "Numpad2" => KeyCode::Numpad2, + "Numpad3" => KeyCode::Numpad3, + "Numpad4" => KeyCode::Numpad4, + "Numpad5" => KeyCode::Numpad5, + "Numpad6" => KeyCode::Numpad6, + "Numpad7" => KeyCode::Numpad7, + "Numpad8" => KeyCode::Numpad8, + "Numpad9" => KeyCode::Numpad9, + "NumpadAdd" => KeyCode::NumpadAdd, + "NumpadBackspace" => KeyCode::NumpadBackspace, + "NumpadClear" => KeyCode::NumpadClear, + "NumpadClearEntry" => KeyCode::NumpadClearEntry, + "NumpadComma" => KeyCode::NumpadComma, + "NumpadDecimal" => KeyCode::NumpadDecimal, + "NumpadDivide" => KeyCode::NumpadDivide, + "NumpadEnter" => KeyCode::NumpadEnter, + "NumpadEqual" => KeyCode::NumpadEqual, + "NumpadHash" => KeyCode::NumpadHash, + "NumpadMemoryAdd" => KeyCode::NumpadMemoryAdd, + "NumpadMemoryClear" => KeyCode::NumpadMemoryClear, + "NumpadMemoryRecall" => KeyCode::NumpadMemoryRecall, + "NumpadMemoryStore" => KeyCode::NumpadMemoryStore, + "NumpadMemorySubtract" => KeyCode::NumpadMemorySubtract, + "NumpadMultiply" => KeyCode::NumpadMultiply, + "NumpadParenLeft" => KeyCode::NumpadParenLeft, + "NumpadParenRight" => KeyCode::NumpadParenRight, + "NumpadStar" => KeyCode::NumpadStar, + "NumpadSubtract" => KeyCode::NumpadSubtract, + "Escape" => KeyCode::Escape, + "Fn" => KeyCode::Fn, + "FnLock" => KeyCode::FnLock, + "PrintScreen" => KeyCode::PrintScreen, + "ScrollLock" => KeyCode::ScrollLock, + "Pause" => KeyCode::Pause, + "BrowserBack" => KeyCode::BrowserBack, + "BrowserFavorites" => KeyCode::BrowserFavorites, + "BrowserForward" => KeyCode::BrowserForward, + "BrowserHome" => KeyCode::BrowserHome, + "BrowserRefresh" => KeyCode::BrowserRefresh, + "BrowserSearch" => KeyCode::BrowserSearch, + "BrowserStop" => KeyCode::BrowserStop, + "Eject" => KeyCode::Eject, + "LaunchApp1" => KeyCode::LaunchApp1, + "LaunchApp2" => KeyCode::LaunchApp2, + "LaunchMail" => KeyCode::LaunchMail, + "MediaPlayPause" => KeyCode::MediaPlayPause, + "MediaSelect" => KeyCode::MediaSelect, + "MediaStop" => KeyCode::MediaStop, + "MediaTrackNext" => KeyCode::MediaTrackNext, + "MediaTrackPrevious" => KeyCode::MediaTrackPrevious, + "Power" => KeyCode::Power, + "Sleep" => KeyCode::Sleep, + "AudioVolumeDown" => KeyCode::AudioVolumeDown, + "AudioVolumeMute" => KeyCode::AudioVolumeMute, + "AudioVolumeUp" => KeyCode::AudioVolumeUp, + "WakeUp" => KeyCode::WakeUp, + "Hyper" => KeyCode::Hyper, + "Turbo" => KeyCode::Turbo, + "Abort" => KeyCode::Abort, + "Resume" => KeyCode::Resume, + "Suspend" => KeyCode::Suspend, + "Again" => KeyCode::Again, + "Copy" => KeyCode::Copy, + "Cut" => KeyCode::Cut, + "Find" => KeyCode::Find, + "Open" => KeyCode::Open, + "Paste" => KeyCode::Paste, + "Props" => KeyCode::Props, + "Select" => KeyCode::Select, + "Undo" => KeyCode::Undo, + "Hiragana" => KeyCode::Hiragana, + "Katakana" => KeyCode::Katakana, + "F1" => KeyCode::F1, + "F2" => KeyCode::F2, + "F3" => KeyCode::F3, + "F4" => KeyCode::F4, + "F5" => KeyCode::F5, + "F6" => KeyCode::F6, + "F7" => KeyCode::F7, + "F8" => KeyCode::F8, + "F9" => KeyCode::F9, + "F10" => KeyCode::F10, + "F11" => KeyCode::F11, + "F12" => KeyCode::F12, + "F13" => KeyCode::F13, + "F14" => KeyCode::F14, + "F15" => KeyCode::F15, + "F16" => KeyCode::F16, + "F17" => KeyCode::F17, + "F18" => KeyCode::F18, + "F19" => KeyCode::F19, + "F20" => KeyCode::F20, + "F21" => KeyCode::F21, + "F22" => KeyCode::F22, + "F23" => KeyCode::F23, + "F24" => KeyCode::F24, + "F25" => KeyCode::F25, + "F26" => KeyCode::F26, + "F27" => KeyCode::F27, + "F28" => KeyCode::F28, + "F29" => KeyCode::F29, + "F30" => KeyCode::F30, + "F31" => KeyCode::F31, + "F32" => KeyCode::F32, + "F33" => KeyCode::F33, + "F34" => KeyCode::F34, + "F35" => KeyCode::F35, + _ => KeyCode::Unidentified(NativeKeyCode::Web(kcav.to_string())), + } + } +} diff --git a/src/platform_impl/web/mod.rs b/src/platform_impl/web/mod.rs index 8c7f993fb0..c7a69b5bf9 100644 --- a/src/platform_impl/web/mod.rs +++ b/src/platform_impl/web/mod.rs @@ -22,6 +22,7 @@ mod error; mod event_loop; mod monitor; mod window; +mod keyboard; #[path = "web_sys/mod.rs"] mod backend; @@ -37,6 +38,7 @@ pub use self::window::{ Window, }; +pub(crate) use self::keyboard::KeyEventExtra; pub(crate) use crate::icon::NoIcon as PlatformIcon; #[derive(Clone, Copy)] diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index 8d68e5aaab..bcc901cfd1 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -3,7 +3,8 @@ use super::event_handle::EventListenerHandle; use super::media_query_handle::MediaQueryListHandle; use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; use crate::error::OsError as RootOE; -use crate::event::{ModifiersState, MouseButton, MouseScrollDelta, ScanCode, VirtualKeyCode}; +use crate::event::{MouseButton, MouseScrollDelta}; +use crate::keyboard::{Key, KeyCode, KeyLocation, ModifiersState}; use crate::platform_impl::{OsError, PlatformSpecificWindowBuilderAttributes}; use std::cell::RefCell; @@ -11,7 +12,7 @@ use std::rc::Rc; use wasm_bindgen::{closure::Closure, JsCast}; use web_sys::{ - AddEventListenerOptions, Event, FocusEvent, HtmlCanvasElement, KeyboardEvent, + AddEventListenerOptions, CompositionEvent, Event, FocusEvent, HtmlCanvasElement, KeyboardEvent, MediaQueryListEvent, MouseEvent, WheelEvent, }; @@ -25,7 +26,7 @@ pub struct Canvas { on_blur: Option>, on_keyboard_release: Option>, on_keyboard_press: Option>, - on_received_character: Option>, + on_composition_end: Option>, on_mouse_wheel: Option>, on_fullscreen_change: Option>, on_dark_mode: Option, @@ -81,7 +82,7 @@ impl Canvas { on_focus: None, on_keyboard_release: None, on_keyboard_press: None, - on_received_character: None, + on_composition_end: None, on_mouse_wheel: None, on_fullscreen_change: None, on_dark_mode: None, @@ -150,16 +151,22 @@ impl Canvas { pub fn on_keyboard_release(&mut self, mut handler: F) where - F: 'static + FnMut(ScanCode, Option, ModifiersState), + F: 'static + + FnMut(KeyCode, Key<'static>, Option<&'static str>, KeyLocation, bool, ModifiersState), { self.on_keyboard_release = Some(self.common.add_user_event( "keyup", move |event: KeyboardEvent| { event.prevent_default(); + let key = event::key(&event); + let modifiers = event::keyboard_modifiers(&key); handler( - event::scan_code(&event), - event::virtual_key_code(&event), - event::keyboard_modifiers(&event), + event::key_code(&event), + key, + event::key_text(&event), + event::key_location(&event), + event.repeat(), + modifiers, ); }, )); @@ -167,46 +174,35 @@ impl Canvas { pub fn on_keyboard_press(&mut self, mut handler: F) where - F: 'static + FnMut(ScanCode, Option, ModifiersState), + F: 'static + + FnMut(KeyCode, Key<'static>, Option<&'static str>, KeyLocation, bool, ModifiersState), { self.on_keyboard_press = Some(self.common.add_user_event( "keydown", move |event: KeyboardEvent| { - // event.prevent_default() would suppress subsequent on_received_character() calls. That - // supression is correct for key sequences like Tab/Shift-Tab, Ctrl+R, PgUp/Down to - // scroll, etc. We should not do it for key sequences that result in meaningful character - // input though. - let event_key = &event.key(); - let is_key_string = event_key.len() == 1 || !event_key.is_ascii(); - let is_shortcut_modifiers = - (event.ctrl_key() || event.alt_key()) && !event.get_modifier_state("AltGr"); - if !is_key_string || is_shortcut_modifiers { - event.prevent_default(); - } + event.prevent_default(); + let key = event::key(&event); + let modifiers = event::keyboard_modifiers(&key); handler( - event::scan_code(&event), - event::virtual_key_code(&event), - event::keyboard_modifiers(&event), + event::key_code(&event), + key, + event::key_text(&event), + event::key_location(&event), + event.repeat(), + modifiers, ); }, )); } - pub fn on_received_character(&mut self, mut handler: F) + pub fn on_composition_end(&mut self, mut handler: F) where - F: 'static + FnMut(char), + F: 'static + FnMut(Option), { - // TODO: Use `beforeinput`. - // - // The `keypress` event is deprecated, but there does not seem to be a - // viable/compatible alternative as of now. `beforeinput` is still widely - // unsupported. - self.on_received_character = Some(self.common.add_user_event( - "keypress", - move |event: KeyboardEvent| { - // Supress further handling to stop keys like the space key from scrolling the page. - event.prevent_default(); - handler(event::codepoint(&event)); + self.on_composition_end = Some(self.common.add_user_event( + "compositionend", + move |event: CompositionEvent| { + handler(event.data()); }, )); } @@ -308,7 +304,6 @@ impl Canvas { self.on_blur = None; self.on_keyboard_release = None; self.on_keyboard_press = None; - self.on_received_character = None; self.on_mouse_wheel = None; self.on_fullscreen_change = None; self.on_dark_mode = None; diff --git a/src/platform_impl/web/web_sys/canvas/mouse_handler.rs b/src/platform_impl/web/web_sys/canvas/mouse_handler.rs index 81e2aead02..a2939c351b 100644 --- a/src/platform_impl/web/web_sys/canvas/mouse_handler.rs +++ b/src/platform_impl/web/web_sys/canvas/mouse_handler.rs @@ -1,7 +1,8 @@ use super::event; use super::EventListenerHandle; use crate::dpi::PhysicalPosition; -use crate::event::{ModifiersState, MouseButton}; +use crate::event::MouseButton; +use crate::keyboard::ModifiersState; use std::cell::RefCell; use std::rc::Rc; diff --git a/src/platform_impl/web/web_sys/canvas/pointer_handler.rs b/src/platform_impl/web/web_sys/canvas/pointer_handler.rs index 85a99eb8ab..8d15d1cbb6 100644 --- a/src/platform_impl/web/web_sys/canvas/pointer_handler.rs +++ b/src/platform_impl/web/web_sys/canvas/pointer_handler.rs @@ -1,7 +1,8 @@ use super::event; use super::EventListenerHandle; use crate::dpi::PhysicalPosition; -use crate::event::{ModifiersState, MouseButton}; +use crate::event::MouseButton; +use crate::keyboard::ModifiersState; use web_sys::PointerEvent; diff --git a/src/platform_impl/web/web_sys/event.rs b/src/platform_impl/web/web_sys/event.rs index fb6c52d3b0..4e193fec57 100644 --- a/src/platform_impl/web/web_sys/event.rs +++ b/src/platform_impl/web/web_sys/event.rs @@ -1,5 +1,6 @@ use crate::dpi::LogicalPosition; -use crate::event::{ModifiersState, MouseButton, MouseScrollDelta, ScanCode, VirtualKeyCode}; +use crate::event::{MouseButton, MouseScrollDelta}; +use crate::keyboard::{Key, KeyCode, KeyLocation, ModifiersState}; use std::convert::TryInto; use web_sys::{HtmlCanvasElement, KeyboardEvent, MouseEvent, WheelEvent}; @@ -16,9 +17,9 @@ pub fn mouse_button(event: &MouseEvent) -> MouseButton { pub fn mouse_modifiers(event: &MouseEvent) -> ModifiersState { let mut m = ModifiersState::empty(); m.set(ModifiersState::SHIFT, event.shift_key()); - m.set(ModifiersState::CTRL, event.ctrl_key()); + m.set(ModifiersState::CONTROL, event.ctrl_key()); m.set(ModifiersState::ALT, event.alt_key()); - m.set(ModifiersState::LOGO, event.meta_key()); + m.set(ModifiersState::SUPER, event.meta_key()); m } @@ -61,188 +62,78 @@ pub fn mouse_scroll_delta(event: &WheelEvent) -> Option { } } -pub fn scan_code(event: &KeyboardEvent) -> ScanCode { - match event.key_code() { - 0 => event.char_code(), - i => i, +pub fn key_code(event: &KeyboardEvent) -> KeyCode { + let code = event.code(); + KeyCode::from_key_code_attribute_value(&code) +} + +pub fn key(event: &KeyboardEvent) -> Key<'static> { + let key = event.key(); + let key = cached_string(key); + Key::from_key_attribute_value(key) +} + +pub fn key_text(event: &KeyboardEvent) -> Option<&'static str> { + let key = event.key(); + let key = Key::from_key_attribute_value(&key); + match &key { + Key::Character(text) => Some(*text), + Key::Tab => Some("\t"), + Key::Enter => Some("\r"), + Key::Space => Some(" "), + _ => None, } + .map(cached_string) } -pub fn virtual_key_code(event: &KeyboardEvent) -> Option { - Some(match &event.code()[..] { - "Digit1" => VirtualKeyCode::Key1, - "Digit2" => VirtualKeyCode::Key2, - "Digit3" => VirtualKeyCode::Key3, - "Digit4" => VirtualKeyCode::Key4, - "Digit5" => VirtualKeyCode::Key5, - "Digit6" => VirtualKeyCode::Key6, - "Digit7" => VirtualKeyCode::Key7, - "Digit8" => VirtualKeyCode::Key8, - "Digit9" => VirtualKeyCode::Key9, - "Digit0" => VirtualKeyCode::Key0, - "KeyA" => VirtualKeyCode::A, - "KeyB" => VirtualKeyCode::B, - "KeyC" => VirtualKeyCode::C, - "KeyD" => VirtualKeyCode::D, - "KeyE" => VirtualKeyCode::E, - "KeyF" => VirtualKeyCode::F, - "KeyG" => VirtualKeyCode::G, - "KeyH" => VirtualKeyCode::H, - "KeyI" => VirtualKeyCode::I, - "KeyJ" => VirtualKeyCode::J, - "KeyK" => VirtualKeyCode::K, - "KeyL" => VirtualKeyCode::L, - "KeyM" => VirtualKeyCode::M, - "KeyN" => VirtualKeyCode::N, - "KeyO" => VirtualKeyCode::O, - "KeyP" => VirtualKeyCode::P, - "KeyQ" => VirtualKeyCode::Q, - "KeyR" => VirtualKeyCode::R, - "KeyS" => VirtualKeyCode::S, - "KeyT" => VirtualKeyCode::T, - "KeyU" => VirtualKeyCode::U, - "KeyV" => VirtualKeyCode::V, - "KeyW" => VirtualKeyCode::W, - "KeyX" => VirtualKeyCode::X, - "KeyY" => VirtualKeyCode::Y, - "KeyZ" => VirtualKeyCode::Z, - "Escape" => VirtualKeyCode::Escape, - "F1" => VirtualKeyCode::F1, - "F2" => VirtualKeyCode::F2, - "F3" => VirtualKeyCode::F3, - "F4" => VirtualKeyCode::F4, - "F5" => VirtualKeyCode::F5, - "F6" => VirtualKeyCode::F6, - "F7" => VirtualKeyCode::F7, - "F8" => VirtualKeyCode::F8, - "F9" => VirtualKeyCode::F9, - "F10" => VirtualKeyCode::F10, - "F11" => VirtualKeyCode::F11, - "F12" => VirtualKeyCode::F12, - "F13" => VirtualKeyCode::F13, - "F14" => VirtualKeyCode::F14, - "F15" => VirtualKeyCode::F15, - "F16" => VirtualKeyCode::F16, - "F17" => VirtualKeyCode::F17, - "F18" => VirtualKeyCode::F18, - "F19" => VirtualKeyCode::F19, - "F20" => VirtualKeyCode::F20, - "F21" => VirtualKeyCode::F21, - "F22" => VirtualKeyCode::F22, - "F23" => VirtualKeyCode::F23, - "F24" => VirtualKeyCode::F24, - "PrintScreen" => VirtualKeyCode::Snapshot, - "ScrollLock" => VirtualKeyCode::Scroll, - "Pause" => VirtualKeyCode::Pause, - "Insert" => VirtualKeyCode::Insert, - "Home" => VirtualKeyCode::Home, - "Delete" => VirtualKeyCode::Delete, - "End" => VirtualKeyCode::End, - "PageDown" => VirtualKeyCode::PageDown, - "PageUp" => VirtualKeyCode::PageUp, - "ArrowLeft" => VirtualKeyCode::Left, - "ArrowUp" => VirtualKeyCode::Up, - "ArrowRight" => VirtualKeyCode::Right, - "ArrowDown" => VirtualKeyCode::Down, - "Backspace" => VirtualKeyCode::Back, - "Enter" => VirtualKeyCode::Return, - "Space" => VirtualKeyCode::Space, - "Compose" => VirtualKeyCode::Compose, - "Caret" => VirtualKeyCode::Caret, - "NumLock" => VirtualKeyCode::Numlock, - "Numpad0" => VirtualKeyCode::Numpad0, - "Numpad1" => VirtualKeyCode::Numpad1, - "Numpad2" => VirtualKeyCode::Numpad2, - "Numpad3" => VirtualKeyCode::Numpad3, - "Numpad4" => VirtualKeyCode::Numpad4, - "Numpad5" => VirtualKeyCode::Numpad5, - "Numpad6" => VirtualKeyCode::Numpad6, - "Numpad7" => VirtualKeyCode::Numpad7, - "Numpad8" => VirtualKeyCode::Numpad8, - "Numpad9" => VirtualKeyCode::Numpad9, - "AbntC1" => VirtualKeyCode::AbntC1, - "AbntC2" => VirtualKeyCode::AbntC2, - "NumpadAdd" => VirtualKeyCode::NumpadAdd, - "Quote" => VirtualKeyCode::Apostrophe, - "Apps" => VirtualKeyCode::Apps, - "At" => VirtualKeyCode::At, - "Ax" => VirtualKeyCode::Ax, - "Backslash" => VirtualKeyCode::Backslash, - "Calculator" => VirtualKeyCode::Calculator, - "Capital" => VirtualKeyCode::Capital, - "Semicolon" => VirtualKeyCode::Semicolon, - "Comma" => VirtualKeyCode::Comma, - "Convert" => VirtualKeyCode::Convert, - "NumpadDecimal" => VirtualKeyCode::NumpadDecimal, - "NumpadDivide" => VirtualKeyCode::NumpadDivide, - "Equal" => VirtualKeyCode::Equals, - "Backquote" => VirtualKeyCode::Grave, - "Kana" => VirtualKeyCode::Kana, - "Kanji" => VirtualKeyCode::Kanji, - "AltLeft" => VirtualKeyCode::LAlt, - "BracketLeft" => VirtualKeyCode::LBracket, - "ControlLeft" => VirtualKeyCode::LControl, - "ShiftLeft" => VirtualKeyCode::LShift, - "MetaLeft" => VirtualKeyCode::LWin, - "Mail" => VirtualKeyCode::Mail, - "MediaSelect" => VirtualKeyCode::MediaSelect, - "MediaStop" => VirtualKeyCode::MediaStop, - "Minus" => VirtualKeyCode::Minus, - "NumpadMultiply" => VirtualKeyCode::NumpadMultiply, - "Mute" => VirtualKeyCode::Mute, - "LaunchMyComputer" => VirtualKeyCode::MyComputer, - "NavigateForward" => VirtualKeyCode::NavigateForward, - "NavigateBackward" => VirtualKeyCode::NavigateBackward, - "NextTrack" => VirtualKeyCode::NextTrack, - "NoConvert" => VirtualKeyCode::NoConvert, - "NumpadComma" => VirtualKeyCode::NumpadComma, - "NumpadEnter" => VirtualKeyCode::NumpadEnter, - "NumpadEquals" => VirtualKeyCode::NumpadEquals, - "OEM102" => VirtualKeyCode::OEM102, - "Period" => VirtualKeyCode::Period, - "PlayPause" => VirtualKeyCode::PlayPause, - "Power" => VirtualKeyCode::Power, - "PrevTrack" => VirtualKeyCode::PrevTrack, - "AltRight" => VirtualKeyCode::RAlt, - "BracketRight" => VirtualKeyCode::RBracket, - "ControlRight" => VirtualKeyCode::RControl, - "ShiftRight" => VirtualKeyCode::RShift, - "MetaRight" => VirtualKeyCode::RWin, - "Slash" => VirtualKeyCode::Slash, - "Sleep" => VirtualKeyCode::Sleep, - "Stop" => VirtualKeyCode::Stop, - "NumpadSubtract" => VirtualKeyCode::NumpadSubtract, - "Sysrq" => VirtualKeyCode::Sysrq, - "Tab" => VirtualKeyCode::Tab, - "Underline" => VirtualKeyCode::Underline, - "Unlabeled" => VirtualKeyCode::Unlabeled, - "AudioVolumeDown" => VirtualKeyCode::VolumeDown, - "AudioVolumeUp" => VirtualKeyCode::VolumeUp, - "Wake" => VirtualKeyCode::Wake, - "WebBack" => VirtualKeyCode::WebBack, - "WebFavorites" => VirtualKeyCode::WebFavorites, - "WebForward" => VirtualKeyCode::WebForward, - "WebHome" => VirtualKeyCode::WebHome, - "WebRefresh" => VirtualKeyCode::WebRefresh, - "WebSearch" => VirtualKeyCode::WebSearch, - "WebStop" => VirtualKeyCode::WebStop, - "Yen" => VirtualKeyCode::Yen, - _ => return None, - }) +fn cached_string>(string: S) -> &'static str { + use std::collections::HashSet; + use std::sync::Mutex; + + lazy_static::lazy_static! { + static ref STRING_CACHE: Mutex> = Mutex::new(HashSet::new()); + }; + + let string = string.as_ref(); + let mut cache = STRING_CACHE.lock().unwrap(); + if let Some(string) = cache.get(string) { + *string + } else { + // borrowck couldn't quite figure out this one on its own + let string: &'static str = Box::leak(String::from(string).into_boxed_str()); + cache.insert(string); + string + } } -pub fn keyboard_modifiers(event: &KeyboardEvent) -> ModifiersState { - let mut m = ModifiersState::empty(); - m.set(ModifiersState::SHIFT, event.shift_key()); - m.set(ModifiersState::CTRL, event.ctrl_key()); - m.set(ModifiersState::ALT, event.alt_key()); - m.set(ModifiersState::LOGO, event.meta_key()); - m +pub fn key_location(event: &KeyboardEvent) -> KeyLocation { + let location = event.location(); + // As defined in the UIEvents specification + // https://w3c.github.io/uievents/#idl-keyboardevent + match location { + 0 => KeyLocation::Standard, + 1 => KeyLocation::Left, + 2 => KeyLocation::Right, + 3 => KeyLocation::Numpad, + _ => KeyLocation::Standard, + } } -pub fn codepoint(event: &KeyboardEvent) -> char { - // `event.key()` always returns a non-empty `String`. Therefore, this should - // never panic. - // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key - event.key().chars().next().unwrap() +// TODO: What should be done about `KeyboardEvent.isComposing`? + +pub fn keyboard_modifiers(key: &Key<'_>) -> ModifiersState { + match key { + Key::Shift => ModifiersState::SHIFT, + Key::Control => ModifiersState::CONTROL, + Key::Alt => ModifiersState::ALT, + Key::Super => ModifiersState::SUPER, + _ => ModifiersState::empty(), + } } + +// pub fn codepoint(event: &KeyboardEvent) -> char { +// // `event.key()` always returns a non-empty `String`. Therefore, this should +// // never panic. +// // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key +// event.key().chars().next().unwrap() +// } diff --git a/src/platform_impl/web/window.rs b/src/platform_impl/web/window.rs index 19fac1b93b..a579bcfa82 100644 --- a/src/platform_impl/web/window.rs +++ b/src/platform_impl/web/window.rs @@ -330,6 +330,10 @@ impl Window { handle.id = self.id.0; RawWindowHandle::Web(handle) } + + pub fn reset_dead_keys(&self) { + // Not supported + } } impl Drop for Window {