zng_ext_input/
keyboard.rs

1//! Keyboard manager.
2
3use std::{collections::HashSet, time::Duration};
4
5use zng_app::{
6    APP, AppExtension, DInstant, HeadlessApp,
7    event::{event, event_args},
8    shortcut::{GestureKey, KeyGesture, ModifierGesture, ModifiersState, Shortcut},
9    update::EventUpdate,
10    view_process::{
11        VIEW_PROCESS_INITED_EVENT,
12        raw_device_events::InputDeviceId,
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::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::{Var, 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: InputDeviceId,
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}
106/// Shortcut methods.
107impl KeyInputArgs {
108    /// Gets the modified key for Numpad keys and the unmodified key for the rest.
109    pub fn shortcut_key(&self) -> &Key {
110        if matches!(self.key_location, KeyLocation::Numpad) {
111            &self.key_modified
112        } else {
113            &self.key
114        }
115    }
116
117    /// Gets the best shortcut approximation of the current pressed state.
118    ///
119    /// Note that the returned shortcut may be [invalid], don't use this to match shortcut presses, use
120    /// the [`GESTURES`] service for that. This method is for key binding editors.
121    /// This helper also does not support chords, only single key and modifier gestures.
122    ///
123    /// Returns `None` if this event if for a key release.
124    ///
125    /// [`GESTURES`]: crate::gesture::GESTURES
126    /// [invalid]: Shortcut::is_valid
127    pub fn editing_shortcut(&self) -> Option<Shortcut> {
128        if matches!(self.state, KeyState::Released) {
129            return None;
130        }
131
132        let source_key = self.shortcut_key();
133        if let Ok(modifier) = ModifierGesture::try_from(source_key)
134            && (self.modifiers.is_empty() || self.modifiers == modifier.modifiers_state())
135        {
136            return Some(Shortcut::Modifier(modifier));
137        }
138        let key = match source_key {
139            Key::Unidentified => GestureKey::try_from(self.key_code).unwrap_or(GestureKey::Key(Key::Unidentified)),
140            key => GestureKey::try_from(key.clone()).unwrap_or(GestureKey::Key(Key::Unidentified)),
141        };
142        Some(Shortcut::Gesture(KeyGesture {
143            modifiers: self.modifiers,
144            key,
145        }))
146    }
147}
148
149/// Text input methods.
150///
151/// The [`text`] field contains the raw text associated with the key-press by the operating system,
152/// these methods normalize and filter this text.
153///
154/// [`text`]: KeyInputArgs::text
155impl KeyInputArgs {
156    /// Returns `true` if the character is the backspace and `CTRL` is not pressed.
157    pub fn is_backspace(&self) -> bool {
158        !self.modifiers.contains(ModifiersState::CTRL) && self.text.contains('\u{8}')
159    }
160
161    /// Returns `true` if the character is delete and `CTRL` is not pressed.
162    pub fn is_delete(&self) -> bool {
163        !self.modifiers.contains(ModifiersState::CTRL) && self.text.contains('\u{7F}')
164    }
165
166    /// Returns `true` if the character is a tab space and `CTRL` is not pressed.
167    pub fn is_tab(&self) -> bool {
168        !self.modifiers.contains(ModifiersState::CTRL) && self.text.chars().any(|c| "\t\u{B}\u{1F}".contains(c))
169    }
170
171    /// Returns `true` if the character is a line-break and `CTRL` is not pressed.
172    pub fn is_line_break(&self) -> bool {
173        !self.modifiers.contains(ModifiersState::CTRL) && self.text.chars().any(|c| "\r\n\u{85}".contains(c))
174    }
175
176    /// Gets the characters to insert in a typed text.
177    ///
178    /// Replaces all [`is_tab`] with `\t` and all [`is_line_break`] with `\n`.
179    /// Returns `""` if there is no text or it contains ASCII control characters or `CTRL` is pressed.
180    ///
181    /// [`is_tab`]: Self::is_tab
182    /// [`is_line_break`]: Self::is_line_break
183    pub fn insert_str(&self) -> &str {
184        if self.modifiers.contains(ModifiersState::CTRL) {
185            // ignore legacy ASCII control combinators like `ctrl+i` generated `\t`.
186            ""
187        } else if self.is_tab() {
188            "\t"
189        } else if self.is_line_break() {
190            "\n"
191        } else if self.text.chars().any(|c| c.is_ascii_control()) {
192            ""
193        } else {
194            &self.text
195        }
196    }
197}
198
199event! {
200    /// Key pressed, repeat pressed or released event.
201    ///
202    /// # Provider
203    ///
204    /// This event is provided by the [`KeyboardManager`] extension.
205    pub static KEY_INPUT_EVENT: KeyInputArgs;
206
207    /// Modifiers key state changed event.
208    ///
209    /// # Provider
210    ///
211    /// This event is provided by the [`KeyboardManager`] extension.
212    pub static MODIFIERS_CHANGED_EVENT: ModifiersChangedArgs;
213}
214
215/// Application extension that provides keyboard events targeting the focused widget.
216///
217/// This [extension] processes the raw keyboard events retargeting then to the focused widget, generating derived events and variables.
218///
219/// # Events
220///
221/// Events this extension provides.
222///
223/// * [`KEY_INPUT_EVENT`]
224/// * [`MODIFIERS_CHANGED_EVENT`]
225///
226/// # Services
227///
228/// Services this extension provides.
229///
230/// * [`KEYBOARD`]
231///
232/// # Dependencies
233///
234/// This extension requires the [`FOCUS`] and [`WINDOWS`] services before the first raw key input event. It does not
235/// require anything for initialization.
236///
237/// [extension]: AppExtension
238/// [`FOCUS`]: crate::focus::FOCUS
239/// [`WINDOWS`]: zng_ext_window::WINDOWS
240#[derive(Default)]
241pub struct KeyboardManager {}
242impl AppExtension for KeyboardManager {
243    fn event_preview(&mut self, update: &mut EventUpdate) {
244        if let Some(args) = RAW_KEY_INPUT_EVENT.on(update) {
245            let focused = FOCUS.focused().get();
246            KEYBOARD_SV.write().key_input(args, focused);
247        } else if let Some(args) = RAW_KEY_REPEAT_CONFIG_CHANGED_EVENT.on(update) {
248            let mut kb = KEYBOARD_SV.write();
249            kb.repeat_config.set(args.config);
250            kb.last_key_down = None;
251        } else if let Some(args) = RAW_ANIMATIONS_CONFIG_CHANGED_EVENT.on(update) {
252            let kb = KEYBOARD_SV.read();
253            kb.caret_animation_config
254                .set((args.config.caret_blink_interval, args.config.caret_blink_timeout));
255        } else if let Some(args) = RAW_WINDOW_FOCUS_EVENT.on(update) {
256            if args.new_focus.is_none() {
257                let mut kb = KEYBOARD_SV.write();
258                kb.clear_modifiers();
259                kb.codes.set(vec![]);
260                kb.keys.set(vec![]);
261
262                kb.last_key_down = None;
263            }
264        } else if let Some(args) = VIEW_PROCESS_INITED_EVENT.on(update) {
265            let mut kb = KEYBOARD_SV.write();
266
267            if args.is_respawn {
268                kb.clear_modifiers();
269                kb.codes.set(vec![]);
270                kb.keys.set(vec![]);
271
272                kb.last_key_down = None;
273            }
274        }
275    }
276}
277
278/// Keyboard service.
279///
280/// # Provider
281///
282/// This service is provided by the [`KeyboardManager`] extension, it will panic if used in an app not extended.
283pub struct KEYBOARD;
284impl KEYBOARD {
285    /// Returns a read-only variable that tracks the currently pressed modifier keys.
286    pub fn modifiers(&self) -> Var<ModifiersState> {
287        KEYBOARD_SV.read().modifiers.read_only()
288    }
289
290    /// Returns a read-only variable that tracks the [`KeyCode`] of the keys currently pressed.
291    pub fn codes(&self) -> Var<Vec<KeyCode>> {
292        KEYBOARD_SV.read().codes.read_only()
293    }
294
295    /// Returns a read-only variable that tracks the [`Key`] identifier of the keys currently pressed.
296    pub fn keys(&self) -> Var<Vec<Key>> {
297        KEYBOARD_SV.read().keys.read_only()
298    }
299
300    /// Returns a variable that defines key press repeat start delay and repeat speed on the app.
301    ///
302    /// This delay is roughly the time the user must hold a key pressed to start repeating. When a second key press
303    /// happens without any other keyboard event and within twice this value it increments the [`repeat_count`] by the [`KeyboardManager`].
304    ///
305    /// The value is the same as [`sys_repeat_config`], if set the variable disconnects from system config.
306    ///
307    /// [`sys_repeat_config`]: KEYBOARD::sys_repeat_config
308    /// [`repeat_count`]: KeyInputArgs::repeat_count
309    pub fn repeat_config(&self) -> Var<KeyRepeatConfig> {
310        KEYBOARD_SV.read().repeat_config.clone()
311    }
312
313    /// Returns a read-only variable that tracks the operating system key press repeat start delay and repeat speed.
314    ///
315    /// The variable updates every time the system config changes and on view-process (re)init.
316    pub fn sys_repeat_config(&self) -> Var<KeyRepeatConfig> {
317        KEYBOARD_SV.read().sys_repeat_config.read_only()
318    }
319
320    /// Returns a variable that defines the system config for the caret blink speed and timeout for the app.
321    ///
322    /// The first value defines the blink speed interval, the caret is visible for the duration, then not visible for the duration. The
323    /// second value defines the animation total duration, the caret stops animating and sticks to visible after this timeout is reached.
324    ///
325    /// You can use the [`caret_animation`] method to generate a new animation.
326    ///
327    /// The value is the same as [`sys_repeat_config`], if set the variable disconnects from system config.
328    ///
329    /// [`caret_animation`]: Self::caret_animation
330    /// [`sys_repeat_config`]: Self::sys_repeat_config
331    pub fn caret_animation_config(&self) -> Var<(Duration, Duration)> {
332        KEYBOARD_SV.read().caret_animation_config.clone()
333    }
334
335    /// Returns a read-only variable that tracks the operating system caret blink speed and timeout.
336    ///
337    /// The variable updates every time the system config changes and on view-process (re)init.
338    pub fn sys_caret_animation_config(&self) -> Var<(Duration, Duration)> {
339        KEYBOARD_SV.read().sys_caret_animation_config.read_only()
340    }
341
342    /// Returns a new read-only variable that animates the caret opacity.
343    ///
344    /// A new animation must be started after each key press. The value is always 1 or 0, no easing is used by default,
345    /// it can be added using the [`Var::easing`] method.
346    ///
347    /// [`Var::easing`]: zng_var::Var::easing
348    pub fn caret_animation(&self) -> Var<Factor> {
349        KEYBOARD_SV.read().caret_animation()
350    }
351}
352
353app_local! {
354    static KEYBOARD_SV: KeyboardService = {
355        APP.extensions().require::<KeyboardManager>();
356        let sys_repeat_config = var_default();
357        let cfg = AnimationsConfig::default();
358        let sys_caret_animation_config = var((cfg.caret_blink_interval, cfg.caret_blink_timeout));
359        KeyboardService {
360            current_modifiers: HashSet::default(),
361            modifiers: var(ModifiersState::empty()),
362            codes: var(vec![]),
363            keys: var(vec![]),
364            repeat_config: sys_repeat_config.cow(),
365            sys_repeat_config,
366            caret_animation_config: sys_caret_animation_config.cow(),
367            sys_caret_animation_config,
368            last_key_down: None,
369        }
370    };
371}
372
373struct KeyboardService {
374    current_modifiers: HashSet<Key>,
375
376    modifiers: Var<ModifiersState>,
377    codes: Var<Vec<KeyCode>>,
378    keys: Var<Vec<Key>>,
379    repeat_config: Var<KeyRepeatConfig>,
380    sys_repeat_config: Var<KeyRepeatConfig>,
381    caret_animation_config: Var<(Duration, Duration)>,
382    sys_caret_animation_config: Var<(Duration, Duration)>,
383
384    last_key_down: Option<(InputDeviceId, KeyCode, DInstant, u32)>,
385}
386impl KeyboardService {
387    fn key_input(&mut self, args: &RawKeyInputArgs, focused: Option<InteractionPath>) {
388        let mut repeat = 0;
389
390        // update state and vars
391        match args.state {
392            KeyState::Pressed => {
393                if let Some((d_id, code, time, count)) = &mut self.last_key_down {
394                    let max_t = self.repeat_config.get().start_delay * 2;
395                    if args.key_code == *code && args.device_id == *d_id && (args.timestamp - *time) < max_t {
396                        *count = (*count).saturating_add(1);
397                        repeat = *count;
398                    } else {
399                        *d_id = args.device_id;
400                        *code = args.key_code;
401                        *count = 0;
402                    }
403                    *time = args.timestamp;
404                } else {
405                    self.last_key_down = Some((args.device_id, args.key_code, args.timestamp, 0));
406                }
407
408                let key_code = args.key_code;
409                if !self.codes.with(|c| c.contains(&key_code)) {
410                    self.codes.modify(move |cs| {
411                        cs.push(key_code);
412                    });
413                }
414
415                let key = &args.key;
416                if !matches!(&key, Key::Unidentified) {
417                    if !self.keys.with(|c| c.contains(key)) {
418                        self.keys.modify(clmv!(key, |ks| {
419                            ks.push(key);
420                        }));
421                    }
422
423                    if key.is_modifier() {
424                        self.set_modifiers(key.clone(), true);
425                    }
426                }
427            }
428            KeyState::Released => {
429                self.last_key_down = None;
430
431                let key = args.key_code;
432                if self.codes.with(|c| c.contains(&key)) {
433                    self.codes.modify(move |cs| {
434                        if let Some(i) = cs.iter().position(|c| *c == key) {
435                            cs.swap_remove(i);
436                        }
437                    });
438                }
439
440                let key = &args.key;
441                if !matches!(&key, Key::Unidentified) {
442                    if self.keys.with(|c| c.contains(key)) {
443                        self.keys.modify(clmv!(key, |ks| {
444                            if let Some(i) = ks.iter().position(|k| k == &key) {
445                                ks.swap_remove(i);
446                            }
447                        }));
448                    }
449
450                    if key.is_modifier() {
451                        self.set_modifiers(key.clone(), false);
452                    }
453                }
454            }
455        }
456
457        // notify events
458        if let Some(target) = focused
459            && (target.window_id() == args.window_id || WINDOWS.nest_parent(target.window_id()).map(|(p, _)| p) == Some(args.window_id))
460        {
461            let args = KeyInputArgs::now(
462                target.window_id(),
463                args.device_id,
464                args.key_code,
465                args.key_location,
466                args.state,
467                args.key.clone(),
468                args.key_modified.clone(),
469                args.text.clone(),
470                self.current_modifiers(),
471                repeat,
472                target,
473            );
474            KEY_INPUT_EVENT.notify(args);
475        }
476    }
477    fn set_modifiers(&mut self, key: Key, pressed: bool) {
478        let prev_modifiers = self.current_modifiers();
479
480        if pressed {
481            self.current_modifiers.insert(key);
482        } else {
483            self.current_modifiers.remove(&key);
484        }
485
486        let new_modifiers = self.current_modifiers();
487
488        if prev_modifiers != new_modifiers {
489            self.modifiers.set(new_modifiers);
490            MODIFIERS_CHANGED_EVENT.notify(ModifiersChangedArgs::now(prev_modifiers, new_modifiers));
491        }
492    }
493
494    fn clear_modifiers(&mut self) {
495        let prev_modifiers = self.current_modifiers();
496        self.current_modifiers.clear();
497        let new_modifiers = self.current_modifiers();
498
499        if prev_modifiers != new_modifiers {
500            self.modifiers.set(new_modifiers);
501            MODIFIERS_CHANGED_EVENT.notify(ModifiersChangedArgs::now(prev_modifiers, new_modifiers));
502        }
503    }
504
505    fn current_modifiers(&self) -> ModifiersState {
506        let mut state = ModifiersState::empty();
507        for key in &self.current_modifiers {
508            state |= ModifiersState::from_key(key.clone());
509        }
510        state
511    }
512
513    fn caret_animation(&self) -> Var<Factor> {
514        let var = var(1.fct());
515        let cfg = self.caret_animation_config.clone();
516
517        let zero = 0.fct();
518        let one = 1.fct();
519        let mut init = true;
520
521        var.animate(move |anim, vm| {
522            let (interval, timeout) = cfg.get();
523            if anim.start_time().elapsed() >= timeout || interval == Duration::MAX {
524                if **vm != one {
525                    vm.set(one);
526                }
527                anim.stop();
528            } else {
529                if **vm == one {
530                    if !std::mem::take(&mut init) {
531                        vm.set(zero);
532                    }
533                } else {
534                    vm.set(one);
535                }
536                anim.sleep(interval);
537            }
538        })
539        .perm();
540
541        var.read_only()
542    }
543}
544
545/// Extension trait that adds keyboard simulation methods to [`HeadlessApp`].
546///
547/// [`HeadlessApp`]: zng_app::HeadlessApp
548pub trait HeadlessAppKeyboardExt {
549    /// Notifies keyboard input event.
550    ///
551    /// Note that the app is not updated so the event is pending after this call.
552    fn on_keyboard_input(&mut self, window_id: WindowId, code: KeyCode, location: KeyLocation, key: Key, state: KeyState);
553
554    /// Does a key-down, key-up and updates.
555    fn press_key(&mut self, window_id: WindowId, code: KeyCode, location: KeyLocation, key: Key);
556
557    /// Does a modifiers changed, key-down, key-up, reset modifiers and updates.
558    fn press_modified_key(&mut self, window_id: WindowId, modifiers: ModifiersState, code: KeyCode, location: KeyLocation, key: Key);
559}
560impl HeadlessAppKeyboardExt for HeadlessApp {
561    fn on_keyboard_input(&mut self, window_id: WindowId, code: KeyCode, location: KeyLocation, key: Key, state: KeyState) {
562        use zng_app::view_process::raw_events::*;
563
564        let args = RawKeyInputArgs::now(
565            window_id,
566            InputDeviceId::virtual_keyboard(),
567            code,
568            location,
569            state,
570            key.clone(),
571            key,
572            "",
573        );
574        RAW_KEY_INPUT_EVENT.notify(args);
575    }
576
577    fn press_key(&mut self, window_id: WindowId, code: KeyCode, location: KeyLocation, key: Key) {
578        self.on_keyboard_input(window_id, code, location, key.clone(), KeyState::Pressed);
579        self.on_keyboard_input(window_id, code, location, key, KeyState::Released);
580        let _ = self.update(false);
581    }
582
583    fn press_modified_key(&mut self, window_id: WindowId, modifiers: ModifiersState, code: KeyCode, location: KeyLocation, key: Key) {
584        if modifiers.is_empty() {
585            self.press_key(window_id, code, location, key);
586        } else {
587            let modifiers = modifiers.keys();
588            for key in &modifiers {
589                self.on_keyboard_input(window_id, code, location, key.clone(), KeyState::Pressed);
590            }
591
592            // pressed the modifiers.
593            let _ = self.update(false);
594
595            self.on_keyboard_input(window_id, code, location, key.clone(), KeyState::Pressed);
596            self.on_keyboard_input(window_id, code, location, key.clone(), KeyState::Released);
597
598            // pressed the key.
599            let _ = self.update(false);
600
601            for key in modifiers {
602                self.on_keyboard_input(window_id, code, location, key, KeyState::Released);
603            }
604
605            // released the modifiers.
606            let _ = self.update(false);
607        }
608    }
609}