Skip to main content

tessera_ui/
keyboard_state.rs

1//! Keyboard events with their original modifier state.
2//!
3//! ## Usage
4//!
5//! Preserve keyboard shortcuts during slow frames.
6
7use std::collections::VecDeque;
8
9use winit::keyboard::ModifiersState;
10
11/// An owned keyboard sample accepted from a window or synthetic input.
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct KeyboardEvent {
14    /// Physical keyboard position when known.
15    pub physical_key: winit::keyboard::PhysicalKey,
16    /// Key meaning after the platform keyboard layout is applied.
17    pub logical_key: winit::keyboard::Key,
18    /// Text produced by this sample, if any.
19    pub text: Option<String>,
20    /// Standard, left, right, or numeric keypad location.
21    pub location: winit::keyboard::KeyLocation,
22    /// Whether the key was pressed or released.
23    pub state: winit::event::ElementState,
24    /// Whether this is a repeated press.
25    pub repeat: bool,
26}
27
28impl KeyboardEvent {
29    /// Creates a sample for a named navigation or command key.
30    pub fn named(key: winit::keyboard::NamedKey, state: winit::event::ElementState) -> Self {
31        use winit::keyboard::{KeyCode, NamedKey, NativeKeyCode, PhysicalKey};
32        let code = match key {
33            NamedKey::ArrowLeft => Some(KeyCode::ArrowLeft),
34            NamedKey::ArrowRight => Some(KeyCode::ArrowRight),
35            NamedKey::ArrowUp => Some(KeyCode::ArrowUp),
36            NamedKey::ArrowDown => Some(KeyCode::ArrowDown),
37            NamedKey::Tab => Some(KeyCode::Tab),
38            NamedKey::Enter => Some(KeyCode::Enter),
39            NamedKey::Escape => Some(KeyCode::Escape),
40            NamedKey::Space => Some(KeyCode::Space),
41            NamedKey::Backspace => Some(KeyCode::Backspace),
42            NamedKey::Delete => Some(KeyCode::Delete),
43            NamedKey::Home => Some(KeyCode::Home),
44            NamedKey::End => Some(KeyCode::End),
45            NamedKey::PageUp => Some(KeyCode::PageUp),
46            NamedKey::PageDown => Some(KeyCode::PageDown),
47            _ => None,
48        };
49        Self {
50            physical_key: code
51                .map(PhysicalKey::Code)
52                .unwrap_or(PhysicalKey::Unidentified(NativeKeyCode::Unidentified)),
53            logical_key: winit::keyboard::Key::Named(key),
54            text: None,
55            location: winit::keyboard::KeyLocation::Standard,
56            state,
57            repeat: false,
58        }
59    }
60}
61
62impl From<winit::event::KeyEvent> for KeyboardEvent {
63    fn from(event: winit::event::KeyEvent) -> Self {
64        Self {
65            physical_key: event.physical_key,
66            logical_key: event.logical_key,
67            text: event.text.map(|text| text.to_string()),
68            location: event.location,
69            state: event.state,
70            repeat: event.repeat,
71        }
72    }
73}
74
75#[derive(Debug)]
76pub(crate) struct QueuedInput<E = KeyboardEvent> {
77    pub event: E,
78    pub modifiers: ModifiersState,
79    pub sequence: u64,
80}
81
82pub(crate) fn next_input_sequence() -> u64 {
83    #[derive(Default)]
84    struct Sequence(std::cell::Cell<u64>);
85    let sequence = crate::local_runtime_resource::<Sequence>();
86    let next = sequence
87        .0
88        .get()
89        .checked_add(1)
90        .expect("input sequence exhausted");
91    sequence.0.set(next);
92    next
93}
94
95#[derive(Debug)]
96pub(crate) struct KeyboardQueue<E> {
97    events: VecDeque<QueuedInput<E>>,
98    modifiers: ModifiersState,
99}
100
101pub(crate) type KeyboardState = KeyboardQueue<KeyboardEvent>;
102
103impl<E> Default for KeyboardQueue<E> {
104    fn default() -> Self {
105        Self {
106            events: VecDeque::new(),
107            modifiers: ModifiersState::empty(),
108        }
109    }
110}
111
112impl<E> KeyboardQueue<E> {
113    pub fn push_event(&mut self, event: E) {
114        self.events.push_back(QueuedInput {
115            event,
116            modifiers: self.modifiers,
117            sequence: next_input_sequence(),
118        });
119    }
120
121    pub fn take_events(&mut self) -> Vec<QueuedInput<E>> {
122        self.events.drain(..).collect()
123    }
124
125    pub fn update_modifiers(&mut self, new_state: ModifiersState) {
126        self.modifiers = new_state;
127    }
128
129    pub fn modifiers(&self) -> ModifiersState {
130        self.modifiers
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    #[test]
139    fn modifier_release_does_not_rewrite_queued_shortcuts() {
140        let _runtime_session = crate::RuntimeSession::new();
141        let _runtime_scope = _runtime_session.enter();
142        let mut queue = KeyboardQueue::default();
143        queue.update_modifiers(ModifiersState::SHIFT);
144        queue.push_event("Tab pressed");
145        queue.push_event("Tab released");
146        queue.update_modifiers(ModifiersState::empty());
147        queue.push_event("Tab pressed again");
148        let events = queue.take_events();
149        assert_eq!(
150            events.iter().map(|event| event.event).collect::<Vec<_>>(),
151            ["Tab pressed", "Tab released", "Tab pressed again"]
152        );
153        assert_eq!(
154            events
155                .iter()
156                .map(|event| event.modifiers)
157                .collect::<Vec<_>>(),
158            [
159                ModifiersState::SHIFT,
160                ModifiersState::SHIFT,
161                ModifiersState::empty()
162            ]
163        );
164        assert!(queue.take_events().is_empty());
165    }
166
167    #[test]
168    fn long_frames_preserve_order_and_each_modifier_combination() {
169        let _runtime_session = crate::RuntimeSession::new();
170        let _runtime_scope = _runtime_session.enter();
171        let mut queue = KeyboardQueue::default();
172        for index in 0..512 {
173            queue.update_modifiers(if index % 2 == 0 {
174                ModifiersState::CONTROL | ModifiersState::SHIFT
175            } else {
176                ModifiersState::ALT
177            });
178            queue.push_event(index);
179        }
180        queue.update_modifiers(ModifiersState::empty());
181        let events = queue.take_events();
182        assert_eq!(events.len(), 512);
183        for (index, event) in events.iter().enumerate() {
184            assert_eq!(event.event, index);
185            assert_eq!(
186                event.modifiers,
187                if index % 2 == 0 {
188                    ModifiersState::CONTROL | ModifiersState::SHIFT
189                } else {
190                    ModifiersState::ALT
191                }
192            );
193        }
194    }
195}