11use crate :: { ConsumePreference , Hotkey , KeyCode , Modifiers , Result } ;
22use js_sys:: { Function , Promise , Reflect } ;
3- use wasm_bindgen:: { prelude:: * , JsCast } ;
4- use web_sys:: { window , Event , Gamepad , GamepadButton , KeyboardEvent } ;
3+ use wasm_bindgen:: { JsCast , prelude:: * } ;
4+ use web_sys:: { Event , Gamepad , GamepadButton , KeyboardEvent , window } ;
55
66use std:: {
77 cell:: { Cell , RefCell } ,
@@ -27,7 +27,7 @@ impl fmt::Display for Error {
2727
2828pub struct Hook {
2929 hotkeys : Arc < Mutex < HashMap < Hotkey , Box < dyn FnMut ( ) + Send + ' static > > > > ,
30- keyboard_callback : Closure < dyn FnMut ( Event ) > ,
30+ keyboard_callback : Closure < dyn FnMut ( MaybeKeyboardEvent ) > ,
3131 gamepad_callback : Closure < dyn FnMut ( ) > ,
3232 interval_id : Cell < Option < i32 > > ,
3333 keyboard_layout_resolver : Rc < RefCell < Option < ( JsValue , Function ) > > > ,
@@ -87,49 +87,53 @@ impl Hook {
8787 let window = window ( ) . ok_or ( crate :: Error :: Platform ( Error :: FailedToCreateHook ) ) ?;
8888
8989 let hotkey_map = hotkeys. clone ( ) ;
90- let keyboard_callback = Closure :: wrap ( Box :: new ( move |event : Event | {
90+ let keyboard_callback = Closure :: wrap ( Box :: new ( move |event : MaybeKeyboardEvent | {
9191 // Despite all sorts of documentation claiming that `keydown` events
9292 // pass you a `KeyboardEvent`, this is not actually always the case
9393 // in browsers. At least in Chrome selecting an element of an
94- // `input` sends a `keydown` event that is not a `KeyboardEvent`.
95- if let Ok ( event) = event. dyn_into :: < KeyboardEvent > ( ) {
96- if !event. repeat ( ) {
97- if let Ok ( code) = event. code ( ) . parse :: < KeyCode > ( ) {
98- let mut modifiers = Modifiers :: empty ( ) ;
99- if event. shift_key ( )
100- && !matches ! ( code, KeyCode :: ShiftLeft | KeyCode :: ShiftRight )
101- {
102- modifiers. insert ( Modifiers :: SHIFT ) ;
103- }
104- if event. ctrl_key ( )
105- && !matches ! ( code, KeyCode :: ControlLeft | KeyCode :: ControlRight )
106- {
107- modifiers. insert ( Modifiers :: CONTROL ) ;
108- }
109- if event. alt_key ( ) && !matches ! ( code, KeyCode :: AltLeft | KeyCode :: AltRight )
110- {
111- modifiers. insert ( Modifiers :: ALT ) ;
112- }
113- if event. meta_key ( )
114- && !matches ! ( code, KeyCode :: MetaLeft | KeyCode :: MetaRight )
115- {
116- modifiers. insert ( Modifiers :: META ) ;
117- }
94+ // `input` sends a `keydown` event that is not a `KeyboardEvent` and
95+ // instead just an `Event`. On top of that, child windows all have
96+ // their own `KeyboardEvent` class that is not the same as the
97+ // `KeyboardEvent` class of the parent window. So we can't use
98+ // `dyn_into` and instead define our own `MaybeKeyboardEvent` type
99+ // that optionally supports the `repeat` method, allowing us to both
100+ // check if the event even looks like a `KeyboardEvent` and then we
101+ // proceed based on that.
102+ if event. repeat ( ) == Some ( false ) {
103+ let event = event. unchecked_into :: < KeyboardEvent > ( ) ;
118104
119- if let Some ( callback) = hotkey_map
120- . lock ( )
121- . unwrap ( )
122- . get_mut ( & code. with_modifiers ( modifiers) )
123- {
124- callback ( ) ;
125- if prevent_default {
126- event. prevent_default ( ) ;
127- }
105+ if let Ok ( code) = event. code ( ) . parse :: < KeyCode > ( ) {
106+ let mut modifiers = Modifiers :: empty ( ) ;
107+ if event. shift_key ( )
108+ && !matches ! ( code, KeyCode :: ShiftLeft | KeyCode :: ShiftRight )
109+ {
110+ modifiers. insert ( Modifiers :: SHIFT ) ;
111+ }
112+ if event. ctrl_key ( )
113+ && !matches ! ( code, KeyCode :: ControlLeft | KeyCode :: ControlRight )
114+ {
115+ modifiers. insert ( Modifiers :: CONTROL ) ;
116+ }
117+ if event. alt_key ( ) && !matches ! ( code, KeyCode :: AltLeft | KeyCode :: AltRight ) {
118+ modifiers. insert ( Modifiers :: ALT ) ;
119+ }
120+ if event. meta_key ( ) && !matches ! ( code, KeyCode :: MetaLeft | KeyCode :: MetaRight ) {
121+ modifiers. insert ( Modifiers :: META ) ;
122+ }
123+
124+ if let Some ( callback) = hotkey_map
125+ . lock ( )
126+ . unwrap ( )
127+ . get_mut ( & code. with_modifiers ( modifiers) )
128+ {
129+ callback ( ) ;
130+ if prevent_default {
131+ event. prevent_default ( ) ;
128132 }
129133 }
130134 }
131135 }
132- } ) as Box < dyn FnMut ( Event ) > ) ;
136+ } ) as Box < dyn FnMut ( MaybeKeyboardEvent ) > ) ;
133137
134138 window
135139 . add_event_listener_with_callback ( "keydown" , keyboard_callback. as_ref ( ) . unchecked_ref ( ) )
@@ -250,4 +254,22 @@ impl Hook {
250254 . ok ( ) ?
251255 . as_string ( )
252256 }
257+
258+ pub fn add_window ( & self , window : web_sys:: Window ) -> Result < ( ) > {
259+ window
260+ . add_event_listener_with_callback (
261+ "keydown" ,
262+ self . keyboard_callback . as_ref ( ) . unchecked_ref ( ) ,
263+ )
264+ . map_err ( |_| crate :: Error :: Platform ( Error :: FailedToCreateHook ) )
265+ }
266+ }
267+
268+ #[ wasm_bindgen]
269+ extern "C" {
270+ # [ wasm_bindgen ( extends = Event , extends = :: js_sys :: Object , js_name = KeyboardEvent , typescript_type = "KeyboardEvent" ) ]
271+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
272+ pub type MaybeKeyboardEvent ;
273+ # [ wasm_bindgen ( structural , method , getter , js_class = "KeyboardEvent" , js_name = repeat) ]
274+ pub fn repeat ( this : & MaybeKeyboardEvent ) -> Option < bool > ;
253275}
0 commit comments