Skip to main content

euv_ui/hook/use_keyboard/
impl.rs

1use super::*;
2
3/// Inherent implementation of [`UseKeyboard`].
4impl UseKeyboard {
5    /// Creates a handle with no key held down.
6    ///
7    /// # Returns
8    ///
9    /// - `UseKeyboard` - The fresh handle.
10    pub(crate) fn new() -> Self {
11        Self {
12            pressed: Signal::create(String::new()),
13            teardown: Rc::new(RefCell::new(None)),
14        }
15    }
16
17    /// Returns the key currently held down as a snapshot.
18    ///
19    /// # Returns
20    ///
21    /// - `String` - The key, or an empty string when no key is down.
22    pub fn current_key(&self) -> String {
23        self.get_pressed().get()
24    }
25
26    /// Reports whether `key` is the key currently held down.
27    ///
28    /// # Arguments
29    ///
30    /// - `&str` - The key to test.
31    ///
32    /// # Returns
33    ///
34    /// - `bool` - `true` when that key is down.
35    pub fn is_pressed(&self, key: &str) -> bool {
36        self.get_pressed().get() == key
37    }
38
39    /// Subscribes to the window's `keydown` and `keyup` events.
40    ///
41    /// On a host without a browser this is a no-op and the key stays
42    /// empty.
43    pub(crate) fn attach(&self) {
44        #[cfg(target_arch = "wasm32")]
45        {
46            let Some(window) = browser_window() else {
47                return;
48            };
49            let pressed: Signal<String> = self.get_pressed();
50            let on_down: Closure<dyn FnMut(Event)> =
51                Closure::wrap(Box::new(move |event: Event| {
52                    let Ok(keyboard) = event.dyn_into::<KeyboardEvent>() else {
53                        return;
54                    };
55                    pressed.set(keyboard.key());
56                }));
57            let on_up: Closure<dyn FnMut(Event)> = Closure::wrap(Box::new(move |event: Event| {
58                let Ok(keyboard) = event.dyn_into::<KeyboardEvent>() else {
59                    return;
60                };
61                let released: String = keyboard.key();
62                if pressed.get() == released {
63                    pressed.set(String::new());
64                }
65            }));
66            if window
67                .add_event_listener_with_callback(EVENT_KEYDOWN, on_down.as_ref().unchecked_ref())
68                .is_err()
69            {
70                return;
71            }
72            if window
73                .add_event_listener_with_callback(EVENT_KEYUP, on_up.as_ref().unchecked_ref())
74                .is_err()
75            {
76                return;
77            }
78            *self.get_teardown().borrow_mut() = Some(Box::new(move || {
79                let _: Result<(), JsValue> = window.remove_event_listener_with_callback(
80                    EVENT_KEYDOWN,
81                    on_down.as_ref().unchecked_ref(),
82                );
83                let _: Result<(), JsValue> = window.remove_event_listener_with_callback(
84                    EVENT_KEYUP,
85                    on_up.as_ref().unchecked_ref(),
86                );
87            }));
88        }
89    }
90
91    /// Removes the window listeners.
92    pub(crate) fn detach(&self) {
93        let taken: Option<Box<dyn Fn()>> = self.get_teardown().borrow_mut().take();
94        if let Some(teardown) = taken {
95            teardown();
96        }
97    }
98}