zng_ext_input/
keyboard.rs

1//! Keyboard manager.
2
3use std::{collections::HashSet, time::Duration};
4
5use zng_app::{
6    AppExtension, DInstant, HeadlessApp,
7    event::{event, event_args},
8    shortcut::ModifiersState,
9    update::EventUpdate,
10    view_process::{
11        VIEW_PROCESS_INITED_EVENT,
12        raw_device_events::DeviceId,
13        raw_events::{
14            RAW_ANIMATIONS_CONFIG_CHANGED_EVENT, RAW_KEY_INPUT_EVENT, RAW_KEY_REPEAT_CONFIG_CHANGED_EVENT, RAW_WINDOW_FOCUS_EVENT,
15            RawKeyInputArgs,
16        },
17    },
18    widget::{WidgetId, info::InteractionPath},
19    window::WindowId,
20};
21use zng_app_context::app_local;
22use zng_clone_move::clmv;
23use zng_ext_window::WINDOWS;
24use zng_layout::unit::{Factor, FactorUnits};
25use zng_txt::Txt;
26use zng_var::{ArcVar, ReadOnlyArcVar, Var, types::ArcCowVar, var, var_default};
27use zng_view_api::config::AnimationsConfig;
28pub use zng_view_api::{
29    config::KeyRepeatConfig,
30    keyboard::{Key, KeyCode, KeyLocation, KeyState, NativeKeyCode},
31};
32
33use crate::focus::FOCUS;
34
35event_args! {
36    /// Arguments for [`KEY_INPUT_EVENT`].
37    pub struct KeyInputArgs {
38        /// Window that received the event.
39        pub window_id: WindowId,
40
41        /// Device that generated the event.
42        pub device_id: DeviceId,
43
44        /// Physical key.
45        pub key_code: KeyCode,
46
47        /// The location of the key on the keyboard.
48        pub key_location: KeyLocation,
49
50        /// If the key was pressed or released.
51        pub state: KeyState,
52
53        /// Semantic key.
54        ///
55        /// Pressing `Shift+A` key will produce `Key::Char('a')` in QWERTY keyboards, the modifiers are not applied.
56        pub key: Key,
57        /// Semantic key modified by the current active modifiers.
58        ///
59        /// Pressing `Shift+A` key will produce `Key::Char('A')` in QWERTY keyboards, the modifiers are applied.
60        pub key_modified: Key,
61
62        /// Text typed.
63        ///
64        /// This is only set during [`KeyState::Pressed`] of a key that generates text.
65        ///
66        /// This is usually the `key_modified` char, but is also `'\r'` for `Key::Enter`. On Windows when a dead key was
67        /// pressed earlier but cannot be combined with the character from this key press, the produced text
68        /// will consist of two characters: the dead-key-character followed by the character resulting from this key press.
69        pub text: Txt,
70
71        /// What modifier keys where pressed when this event happened.
72        pub modifiers: ModifiersState,
73
74        /// Number of repeats generated by holding the key pressed.
75        ///
76        /// This is zero for the first key press, increments by one for each event while the key is held pressed.
77        pub repeat_count: u32,
78
79        /// The focused element at the time of the key input.
80        pub target: InteractionPath,
81
82        ..
83
84        /// The [`target`](Self::target).
85        fn delivery_list(&self, list: &mut UpdateDeliveryList) {
86            list.insert_wgt(&self.target)
87        }
88    }
89
90    /// Arguments for [`MODIFIERS_CHANGED_EVENT`].
91    pub struct ModifiersChangedArgs {
92        /// Previous modifiers state.
93        pub prev_modifiers: ModifiersState,
94
95        /// Current modifiers state.
96        pub modifiers: ModifiersState,
97
98        ..
99
100        /// Broadcast to all.
101        fn delivery_list(&self, list: &mut UpdateDeliveryList) {
102            list.search_all()
103        }
104    }
105}
106impl KeyInputArgs {
107    /// Returns `true` if the widget is enabled in [`target`].
108    ///
109    /// [`target`]: Self::target
110    pub fn is_enabled(&self, widget_id: WidgetId) -> bool {
111        self.target.interactivity_of(widget_id).map(|i| i.is_enabled()).unwrap_or(false)
112    }
113
114    /// Returns `true` if the widget is disabled in [`target`].
115    ///
116    /// [`target`]: Self::target
117    pub fn is_disabled(&self, widget_id: WidgetId) -> bool {
118        self.target.interactivity_of(widget_id).map(|i| i.is_disabled()).unwrap_or(false)
119    }
120
121    /// Gets the modified key for Numpad keys and the unmodified key for the rest.
122    pub fn shortcut_key(&self) -> &Key {
123        if matches!(self.key_location, KeyLocation::Numpad) {
124            &self.key_modified
125        } else {
126            &self.key
127        }
128    }
129}
130
131/// Text input methods.
132///
133/// The [`text`] field contains the raw text associated with the key-press by the operating system,
134/// these methods normalize and filter this text.
135///
136/// [`text`]: KeyInputArgs::text
137impl KeyInputArgs {
138    /// Returns `true` if the character is the backspace and `CTRL` is not pressed.
139    pub fn is_backspace(&self) -> bool {
140        !self.modifiers.contains(ModifiersState::CTRL) && self.text.contains('\u{8}')
141    }
142
143    /// Returns `true` if the character is delete and `CTRL` is not pressed.
144    pub fn is_delete(&self) -> bool {
145        !self.modifiers.contains(ModifiersState::CTRL) && self.text.contains('\u{7F}')
146    }
147
148    /// Returns `true` if the character is a tab space and `CTRL` is not pressed.
149    pub fn is_tab(&self) -> bool {
150        !self.modifiers.contains(ModifiersState::CTRL) && self.text.chars().any(|c| "\t\u{B}\u{1F}".contains(c))
151    }
152
153    /// Returns `true` if the character is a line-break and `CTRL` is not pressed.
154    pub fn is_line_break(&self) -> bool {
155        !self.modifiers.contains(ModifiersState::CTRL) && self.text.chars().any(|c| "\r\n\u{85}".contains(c))
156    }
157
158    /// Gets the characters to insert in a typed text.
159    ///
160    /// Replaces all [`is_tab`] with `\t` and all [`is_line_break`] with `\n`.
161    /// Returns `""` if there is no text or it contains ASCII control characters or `CTRL` is pressed.
162    ///
163    /// [`is_tab`]: Self::is_tab
164    /// [`is_line_break`]: Self::is_line_break
165    pub fn insert_str(&self) -> &str {
166        if self.modifiers.contains(ModifiersState::CTRL) {
167            // ignore legacy ASCII control combinators like `ctrl+i` generated `\t`.
168            ""
169        } else if self.is_tab() {
170            "\t"
171        } else if self.is_line_break() {
172            "\n"
173        } else if self.text.chars().any(|c| c.is_ascii_control()) {
174            ""
175        } else {
176            &self.text
177        }
178    }
179}
180
181event! {
182    /// Key pressed, repeat pressed or released event.
183    ///
184    /// # Provider
185    ///
186    /// This event is provided by the [`KeyboardManager`] extension.
187    pub static KEY_INPUT_EVENT: KeyInputArgs;
188
189    /// Modifiers key state changed event.
190    ///
191    /// # Provider
192    ///
193    /// This event is provided by the [`KeyboardManager`] extension.
194    pub static MODIFIERS_CHANGED_EVENT: ModifiersChangedArgs;
195}
196
197/// Application extension that provides keyboard events targeting the focused widget.
198///
199/// This [extension] processes the raw keyboard events retargeting then to the focused widget, generating derived events and variables.
200///
201/// # Events
202///
203/// Events this extension provides.
204///
205/// * [`KEY_INPUT_EVENT`]
206/// * [`MODIFIERS_CHANGED_EVENT`]
207///
208/// # Services
209///
210/// Services this extension provides.
211///
212/// * [`KEYBOARD`]
213///
214/// # Dependencies
215///
216/// This extension requires the [`FOCUS`] and [`WINDOWS`] services before the first raw key input event. It does not
217/// require anything for initialization.
218///
219/// [extension]: AppExtension
220/// [`FOCUS`]: crate::focus::FOCUS
221/// [`WINDOWS`]: zng_ext_window::WINDOWS
222#[derive(Default)]
223pub struct KeyboardManager {}
224impl AppExtension for KeyboardManager {
225    fn event_preview(&mut self, update: &mut EventUpdate) {
226        if let Some(args) = RAW_KEY_INPUT_EVENT.on(update) {
227            let focused = FOCUS.focused().get();
228            KEYBOARD_SV.write().key_input(args, focused);
229        } else if let Some(args) = RAW_KEY_REPEAT_CONFIG_CHANGED_EVENT.on(update) {
230            let mut kb = KEYBOARD_SV.write();
231            kb.repeat_config.set(args.config);
232            kb.last_key_down = None;
233        } else if let Some(args) = RAW_ANIMATIONS_CONFIG_CHANGED_EVENT.on(update) {
234            let kb = KEYBOARD_SV.read();
235            kb.caret_animation_config
236                .set((args.config.caret_blink_interval, args.config.caret_blink_timeout));
237        } else if let Some(args) = RAW_WINDOW_FOCUS_EVENT.on(update) {
238            if args.new_focus.is_none() {
239                let mut kb = KEYBOARD_SV.write();
240                kb.clear_modifiers();
241                kb.codes.set(vec![]);
242                kb.keys.set(vec![]);
243
244                kb.last_key_down = None;
245            }
246        } else if let Some(args) = VIEW_PROCESS_INITED_EVENT.on(update) {
247            let mut kb = KEYBOARD_SV.write();
248            kb.repeat_config.set(args.key_repeat_config);
249            kb.caret_animation_config.set((
250                args.animations_config.caret_blink_interval,
251                args.animations_config.caret_blink_timeout,
252            ));
253
254            if args.is_respawn {
255                kb.clear_modifiers();
256                kb.codes.set(vec![]);
257                kb.keys.set(vec![]);
258
259                kb.last_key_down = None;
260            }
261        }
262    }
263}
264
265/// Keyboard service.
266///
267/// # Provider
268///
269/// This service is provided by the [`KeyboardManager`] extension.
270pub struct KEYBOARD;
271impl KEYBOARD {
272    /// Returns a read-only variable that tracks the currently pressed modifier keys.
273    pub fn modifiers(&self) -> ReadOnlyArcVar<ModifiersState> {
274        KEYBOARD_SV.read().modifiers.read_only()
275    }
276
277    /// Returns a read-only variable that tracks the [`KeyCode`] of the keys currently pressed.
278    pub fn codes(&self) -> ReadOnlyArcVar<Vec<KeyCode>> {
279        KEYBOARD_SV.read().codes.read_only()
280    }
281
282    /// Returns a read-only variable that tracks the [`Key`] identifier of the keys currently pressed.
283    pub fn keys(&self) -> ReadOnlyArcVar<Vec<Key>> {
284        KEYBOARD_SV.read().keys.read_only()
285    }
286
287    /// Returns a variable that defines key press repeat start delay and repeat speed on the app.
288    ///
289    /// This delay is roughly the time the user must hold a key pressed to start repeating. When a second key press
290    /// happens without any other keyboard event and within twice this value it increments the [`repeat_count`] by the [`KeyboardManager`].
291    ///
292    /// The value is the same as [`sys_repeat_config`], if set the variable disconnects from system config.
293    ///
294    /// [`sys_repeat_config`]: KEYBOARD::sys_repeat_config
295    /// [`repeat_count`]: KeyInputArgs::repeat_count
296    pub fn repeat_config(&self) -> ArcCowVar<KeyRepeatConfig, ArcVar<KeyRepeatConfig>> {
297        KEYBOARD_SV.read().repeat_config.clone()
298    }
299
300    /// Returns a read-only variable that tracks the operating system key press repeat start delay and repeat speed.
301    ///
302    /// The variable updates every time the system config changes and on view-process (re)init.
303    pub fn sys_repeat_config(&self) -> ReadOnlyArcVar<KeyRepeatConfig> {
304        KEYBOARD_SV.read().sys_repeat_config.read_only()
305    }
306
307    /// Returns a variable that defines the system config for the caret blink speed and timeout for the app.
308    ///
309    /// The first value defines the blink speed interval, the caret is visible for the duration, then not visible for the duration. The
310    /// second value defines the animation total duration, the caret stops animating and sticks to visible after this timeout is reached.
311    ///
312    /// You can use the [`caret_animation`] method to generate a new animation.
313    ///
314    /// The value is the same as [`sys_repeat_config`], if set the variable disconnects from system config.
315    ///
316    /// [`caret_animation`]: Self::caret_animation
317    /// [`sys_repeat_config`]: Self::sys_repeat_config
318    pub fn caret_animation_config(&self) -> ArcCowVar<(Duration, Duration), ArcVar<(Duration, Duration)>> {
319        KEYBOARD_SV.read().caret_animation_config.clone()
320    }
321
322    /// Returns a read-only variable that tracks the operating system caret blink speed and timeout.
323    ///
324    /// The variable updates every time the system config changes and on view-process (re)init.
325    pub fn sys_caret_animation_config(&self) -> ReadOnlyArcVar<(Duration, Duration)> {
326        KEYBOARD_SV.read().sys_caret_animation_config.read_only()
327    }
328
329    /// Returns a new read-only variable that animates the caret opacity.
330    ///
331    /// A new animation must be started after each key press. The value is always 1 or 0, no easing is used by default,
332    /// it can be added using the [`Var::easing`] method.
333    ///
334    /// [`Var::easing`]: zng_var::Var::easing
335    pub fn caret_animation(&self) -> ReadOnlyArcVar<Factor> {
336        KEYBOARD_SV.read().caret_animation()
337    }
338}
339
340app_local! {
341    static KEYBOARD_SV: KeyboardService = {
342        let sys_repeat_config = var_default();
343        let cfg = AnimationsConfig::default();
344        let sys_caret_animation_config = var((cfg.caret_blink_interval, cfg.caret_blink_timeout));
345        KeyboardService {
346            current_modifiers: HashSet::default(),
347            modifiers: var(ModifiersState::empty()),
348            codes: var(vec![]),
349            keys: var(vec![]),
350            repeat_config: sys_repeat_config.cow(),
351            sys_repeat_config,
352            caret_animation_config: sys_caret_animation_config.cow(),
353            sys_caret_animation_config,
354            last_key_down: None,
355        }
356    };
357}
358
359struct KeyboardService {
360    current_modifiers: HashSet<Key>,
361
362    modifiers: ArcVar<ModifiersState>,
363    codes: ArcVar<Vec<KeyCode>>,
364    keys: ArcVar<Vec<Key>>,
365    repeat_config: ArcCowVar<KeyRepeatConfig, ArcVar<KeyRepeatConfig>>,
366    sys_repeat_config: ArcVar<KeyRepeatConfig>,
367    caret_animation_config: ArcCowVar<(Duration, Duration), ArcVar<(Duration, Duration)>>,
368    sys_caret_animation_config: ArcVar<(Duration, Duration)>,
369
370    last_key_down: Option<(DeviceId, KeyCode, DInstant, u32)>,
371}
372impl KeyboardService {
373    fn key_input(&mut self, args: &RawKeyInputArgs, focused: Option<InteractionPath>) {
374        let mut repeat = 0;
375
376        // update state and vars
377        match args.state {
378            KeyState::Pressed => {
379                if let Some((d_id, code, time, count)) = &mut self.last_key_down {
380                    let max_t = self.repeat_config.get().start_delay * 2;
381                    if args.key_code == *code && args.device_id == *d_id && (args.timestamp - *time) < max_t {
382                        *count = (*count).saturating_add(1);
383                        repeat = *count;
384                    } else {
385                        *d_id = args.device_id;
386                        *code = args.key_code;
387                        *count = 0;
388                    }
389                    *time = args.timestamp;
390                } else {
391                    self.last_key_down = Some((args.device_id, args.key_code, args.timestamp, 0));
392                }
393
394                let key_code = args.key_code;
395                if !self.codes.with(|c| c.contains(&key_code)) {
396                    self.codes.modify(move |cs| {
397                        cs.to_mut().push(key_code);
398                    });
399                }
400
401                let key = &args.key;
402                if !matches!(&key, Key::Unidentified) {
403                    if !self.keys.with(|c| c.contains(key)) {
404                        self.keys.modify(clmv!(key, |ks| {
405                            ks.to_mut().push(key);
406                        }));
407                    }
408
409                    if key.is_modifier() {
410                        self.set_modifiers(key.clone(), true);
411                    }
412                }
413            }
414            KeyState::Released => {
415                self.last_key_down = None;
416
417                let key = args.key_code;
418                if self.codes.with(|c| c.contains(&key)) {
419                    self.codes.modify(move |cs| {
420                        if let Some(i) = cs.as_ref().iter().position(|c| *c == key) {
421                            cs.to_mut().swap_remove(i);
422                        }
423                    });
424                }
425
426                let key = &args.key;
427                if !matches!(&key, Key::Unidentified) {
428                    if self.keys.with(|c| c.contains(key)) {
429                        self.keys.modify(clmv!(key, |ks| {
430                            if let Some(i) = ks.as_ref().iter().position(|k| k == &key) {
431                                ks.to_mut().swap_remove(i);
432                            }
433                        }));
434                    }
435
436                    if key.is_modifier() {
437                        self.set_modifiers(key.clone(), false);
438                    }
439                }
440            }
441        }
442
443        // notify events
444        if let Some(target) = focused {
445            if target.window_id() == args.window_id || WINDOWS.nest_parent(target.window_id()).map(|(p, _)| p) == Some(args.window_id) {
446                let args = KeyInputArgs::now(
447                    target.window_id(),
448                    args.device_id,
449                    args.key_code,
450                    args.key_location,
451                    args.state,
452                    args.key.clone(),
453                    args.key_modified.clone(),
454                    args.text.clone(),
455                    self.current_modifiers(),
456                    repeat,
457                    target,
458                );
459                KEY_INPUT_EVENT.notify(args);
460            }
461        }
462    }
463    fn set_modifiers(&mut self, key: Key, pressed: bool) {
464        let prev_modifiers = self.current_modifiers();
465
466        if pressed {
467            self.current_modifiers.insert(key);
468        } else {
469            self.current_modifiers.remove(&key);
470        }
471
472        let new_modifiers = self.current_modifiers();
473
474        if prev_modifiers != new_modifiers {
475            self.modifiers.set(new_modifiers);
476            MODIFIERS_CHANGED_EVENT.notify(ModifiersChangedArgs::now(prev_modifiers, new_modifiers));
477        }
478    }
479
480    fn clear_modifiers(&mut self) {
481        let prev_modifiers = self.current_modifiers();
482        self.current_modifiers.clear();
483        let new_modifiers = self.current_modifiers();
484
485        if prev_modifiers != new_modifiers {
486            self.modifiers.set(new_modifiers);
487            MODIFIERS_CHANGED_EVENT.notify(ModifiersChangedArgs::now(prev_modifiers, new_modifiers));
488        }
489    }
490
491    fn current_modifiers(&self) -> ModifiersState {
492        let mut state = ModifiersState::empty();
493        for key in &self.current_modifiers {
494            state |= ModifiersState::from_key(key.clone());
495        }
496        state
497    }
498
499    fn caret_animation(&self) -> ReadOnlyArcVar<Factor> {
500        let var = var(1.fct());
501        let cfg = self.caret_animation_config.clone();
502
503        let zero = 0.fct();
504        let one = 1.fct();
505        let mut init = true;
506
507        var.animate(move |anim, vm| {
508            let (interval, timeout) = cfg.get();
509            if anim.start_time().elapsed() >= timeout || interval == Duration::MAX {
510                if **vm != one {
511                    vm.set(one);
512                }
513                anim.stop();
514            } else {
515                if **vm == one {
516                    if !std::mem::take(&mut init) {
517                        vm.set(zero);
518                    }
519                } else {
520                    vm.set(one);
521                }
522                anim.sleep(interval);
523            }
524        })
525        .perm();
526
527        var.read_only()
528    }
529}
530
531/// Extension trait that adds keyboard simulation methods to [`HeadlessApp`].
532///
533/// [`HeadlessApp`]: zng_app::HeadlessApp
534pub trait HeadlessAppKeyboardExt {
535    /// Notifies keyboard input event.
536    ///
537    /// Note that the app is not updated so the event is pending after this call.
538    fn on_keyboard_input(&mut self, window_id: WindowId, code: KeyCode, location: KeyLocation, key: Key, state: KeyState);
539
540    /// Does a key-down, key-up and updates.
541    fn press_key(&mut self, window_id: WindowId, code: KeyCode, location: KeyLocation, key: Key);
542
543    /// Does a modifiers changed, key-down, key-up, reset modifiers and updates.
544    fn press_modified_key(&mut self, window_id: WindowId, modifiers: ModifiersState, code: KeyCode, location: KeyLocation, key: Key);
545}
546impl HeadlessAppKeyboardExt for HeadlessApp {
547    fn on_keyboard_input(&mut self, window_id: WindowId, code: KeyCode, location: KeyLocation, key: Key, state: KeyState) {
548        use zng_app::view_process::raw_events::*;
549
550        let args = RawKeyInputArgs::now(window_id, DeviceId::virtual_keyboard(), code, location, state, key.clone(), key, "");
551        RAW_KEY_INPUT_EVENT.notify(args);
552    }
553
554    fn press_key(&mut self, window_id: WindowId, code: KeyCode, location: KeyLocation, key: Key) {
555        self.on_keyboard_input(window_id, code, location, key.clone(), KeyState::Pressed);
556        self.on_keyboard_input(window_id, code, location, key, KeyState::Released);
557        let _ = self.update(false);
558    }
559
560    fn press_modified_key(&mut self, window_id: WindowId, modifiers: ModifiersState, code: KeyCode, location: KeyLocation, key: Key) {
561        if modifiers.is_empty() {
562            self.press_key(window_id, code, location, key);
563        } else {
564            let modifiers = modifiers.keys();
565            for key in &modifiers {
566                self.on_keyboard_input(window_id, code, location, key.clone(), KeyState::Pressed);
567            }
568
569            // pressed the modifiers.
570            let _ = self.update(false);
571
572            self.on_keyboard_input(window_id, code, location, key.clone(), KeyState::Pressed);
573            self.on_keyboard_input(window_id, code, location, key.clone(), KeyState::Released);
574
575            // pressed the key.
576            let _ = self.update(false);
577
578            for key in modifiers {
579                self.on_keyboard_input(window_id, code, location, key, KeyState::Released);
580            }
581
582            // released the modifiers.
583            let _ = self.update(false);
584        }
585    }
586}