yew_stdweb/services/
keyboard.rs

1//! Service to register key press event listeners on elements.
2
3use crate::callback::Callback;
4use cfg_if::cfg_if;
5use cfg_match::cfg_match;
6use std::fmt;
7cfg_if! {
8    if #[cfg(feature = "std_web")] {
9        use stdweb::web::event::{ConcreteEvent, KeyDownEvent, KeyPressEvent, KeyUpEvent};
10        use stdweb::web::{EventListenerHandle, IEventTarget};
11    } else if #[cfg(feature = "web_sys")] {
12        use gloo::events::{EventListener, EventListenerOptions};
13        use wasm_bindgen::JsCast;
14        use web_sys::{Event, EventTarget, KeyboardEvent};
15    }
16}
17
18/// Service for registering callbacks on elements to get keystrokes from the user.
19///
20/// # Note
21/// Elements which natively support keyboard input (such as `<input/>` or `<textarea/>`) can use the
22/// `onkeypress` or `oninput` attributes from within the html macro. You **should use those events
23/// instead** of locating the element and registering an event listener using this service.
24///
25/// This service is for adding key event listeners to elements which don't support these attributes,
26/// (for example the `document` and `<canvas>` elements).
27#[derive(Debug)]
28pub struct KeyboardService {}
29
30/// Handle for the key event listener.
31///
32/// When the handle goes out of scope, the listener will be removed from the element.
33#[must_use = "the listener is only active until the handle is dropped"]
34pub struct KeyListenerHandle(
35    #[cfg(feature = "std_web")] Option<EventListenerHandle>,
36    #[cfg(feature = "web_sys")] EventListener,
37);
38
39impl fmt::Debug for KeyListenerHandle {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        f.write_str("KeyListenerHandle")
42    }
43}
44
45impl KeyboardService {
46    /// Registers a callback which listens to KeyPressEvents on a provided element.
47    ///
48    /// # Documentation
49    /// [keypress event](https://developer.mozilla.org/en-US/docs/Web/API/Document/keypress_event)
50    ///
51    /// # Warning
52    /// This API has been deprecated in the HTML standard and it is not recommended for use in new projects.
53    /// Consult the browser compatibility chart in the linked MDN documentation.
54    pub fn register_key_press<
55        #[cfg(feature = "std_web")] T: IEventTarget,
56        #[cfg(feature = "web_sys")] T: AsRef<EventTarget>,
57    >(
58        element: &T,
59        #[cfg(feature = "std_web")] callback: Callback<KeyPressEvent>,
60        #[cfg(feature = "web_sys")] callback: Callback<KeyboardEvent>,
61    ) -> KeyListenerHandle {
62        cfg_match! {
63            feature = "std_web" => register_key_impl(element, callback),
64            feature = "web_sys" => register_key_impl(element, callback, "keypress"),
65        }
66    }
67
68    /// Registers a callback which listens to KeyDownEvents on a provided element.
69    ///
70    /// # Documentation
71    /// [keydown event](https://developer.mozilla.org/en-US/docs/Web/API/Document/keydown_event)
72    ///
73    /// # Note
74    /// This browser feature is relatively new and is set to replace the `keypress` event.
75    /// It may not be fully supported in all browsers.
76    /// Consult the browser compatibility chart in the linked MDN documentation.
77    pub fn register_key_down<
78        #[cfg(feature = "std_web")] T: IEventTarget,
79        #[cfg(feature = "web_sys")] T: AsRef<EventTarget>,
80    >(
81        element: &T,
82        #[cfg(feature = "std_web")] callback: Callback<KeyDownEvent>,
83        #[cfg(feature = "web_sys")] callback: Callback<KeyboardEvent>,
84    ) -> KeyListenerHandle {
85        cfg_match! {
86            feature = "std_web" => register_key_impl(element, callback),
87            feature = "web_sys" => register_key_impl(element, callback, "keydown"),
88        }
89    }
90
91    /// Registers a callback that listens to KeyUpEvents on a provided element.
92    ///
93    /// # Documentation
94    /// [keyup event](https://developer.mozilla.org/en-US/docs/Web/API/Document/keyup_event)
95    ///
96    /// # Note
97    /// This browser feature is relatively new and is set to replace keypress events.
98    /// It may not be fully supported in all browsers.
99    /// Consult the browser compatibility chart in the linked MDN documentation.
100    pub fn register_key_up<
101        #[cfg(feature = "std_web")] T: IEventTarget,
102        #[cfg(feature = "web_sys")] T: AsRef<EventTarget>,
103    >(
104        element: &T,
105        #[cfg(feature = "std_web")] callback: Callback<KeyUpEvent>,
106        #[cfg(feature = "web_sys")] callback: Callback<KeyboardEvent>,
107    ) -> KeyListenerHandle {
108        cfg_match! {
109            feature = "std_web" => register_key_impl(element, callback),
110            feature = "web_sys" => register_key_impl(element, callback, "keyup"),
111        }
112    }
113}
114
115#[cfg(feature = "std_web")]
116fn register_key_impl<T: IEventTarget, E: 'static + ConcreteEvent>(
117    element: &T,
118    callback: Callback<E>,
119) -> KeyListenerHandle {
120    let handle = element.add_event_listener(move |event: E| {
121        callback.emit(event);
122    });
123    cfg_match! {
124        feature = "std_web" => KeyListenerHandle(Some(handle)),
125        feature = "web_sys" => KeyListenerHandle(handle),
126    }
127}
128
129#[cfg(feature = "web_sys")]
130fn register_key_impl<T: AsRef<EventTarget>>(
131    element: &T,
132    callback: Callback<KeyboardEvent>,
133    event: &'static str,
134) -> KeyListenerHandle {
135    let listener = move |event: &Event| {
136        let event = event
137            .dyn_ref::<KeyboardEvent>()
138            .expect("wrong event type")
139            .clone();
140        callback.emit(event);
141    };
142    let options = EventListenerOptions::enable_prevent_default();
143    KeyListenerHandle(EventListener::new_with_options(
144        element.as_ref(),
145        event,
146        options,
147        listener,
148    ))
149}
150
151#[cfg(feature = "std_web")]
152impl Drop for KeyListenerHandle {
153    fn drop(&mut self) {
154        if let Some(handle) = self.0.take() {
155            handle.remove()
156        } else {
157            panic!("Tried to drop KeyListenerHandle twice")
158        }
159    }
160}