Skip to main content

cotis_interactivity/
keyboard.rs

1//! Keyboard edge detection and text input buffering.
2//!
3//! [`KeyboardManager`] turns per-frame key state from
4//! [`cotis_utils::interactivity::keyboard::SimpleKeyboardProvider`] into edge
5//! signals (press, release, hold). [`TextManager`] accumulates typed characters
6//! from [`cotis_utils::interactivity::keyboard::SimpleTextProvider`].
7
8use crate::utils::UpdatableSignal;
9use cotis_utils::interactivity::keyboard::{
10    KeyboardKey, SimpleKeyboardProvider, SimpleTextProvider,
11};
12use std::collections::{HashMap, HashSet, VecDeque};
13
14/// Tracks keyboard key state and edge transitions across frames.
15///
16/// Call [`update`](Self::update) each frame with a keyboard provider, then query
17/// edge methods such as [`is_key_pressed`](Self::is_key_pressed).
18///
19/// # Examples
20///
21/// ```rust,ignore
22/// use cotis_interactivity::keyboard::KeyboardManager;
23/// use cotis_utils::interactivity::keyboard::KeyboardKey;
24///
25/// // let mut keyboard = KeyboardManager::new();
26/// // keyboard.update(app.render_borrow());
27/// // if keyboard.is_key_pressed(KeyboardKey::KeyEscape) { ... }
28/// ```
29pub struct KeyboardManager {
30    keys: HashMap<KeyboardKey, UpdatableSignal>,
31    latest_key: Option<KeyboardKey>,
32}
33
34impl Default for KeyboardManager {
35    fn default() -> Self {
36        Self::new()
37    }
38}
39
40impl KeyboardManager {
41    /// Creates an empty keyboard manager.
42    pub fn new() -> KeyboardManager {
43        Self {
44            keys: Default::default(),
45            latest_key: None,
46        }
47    }
48
49    /// Syncs tracked keys from `provider`'s current pressed-key set.
50    ///
51    /// Keys that appear in the provider for the first time produce a rising edge
52    /// on the frame they are first seen.
53    pub fn update(&mut self, provider: &dyn SimpleKeyboardProvider) {
54        let keys = provider.get_pressed_keys();
55        self.latest_key = keys.last().cloned();
56        let keys: HashSet<_> = keys.into_iter().collect();
57        for (key, signal) in self.keys.iter_mut() {
58            if !keys.contains(key) {
59                signal.update(false);
60            }
61        }
62        for key in keys {
63            if let Some(signal) = self.keys.get_mut(&key) {
64                signal.update(true);
65            } else {
66                let mut signal = UpdatableSignal::new(false);
67                signal.update(true);
68                self.keys.insert(key, signal);
69            }
70        }
71    }
72
73    /// Returns `true` on the frame a key is first pressed (rising edge).
74    pub fn is_key_pressed(&self, key: KeyboardKey) -> bool {
75        if let Some(signal) = self.keys.get(&key) {
76            signal.rising_edge()
77        } else {
78            false
79        }
80    }
81
82    /// Returns `true` when a key has been held for two or more consecutive frames.
83    ///
84    /// # Note
85    ///
86    /// This is **not** operating-system key-repeat timing. It detects sustained
87    /// holds via an internal two-frame threshold (`is_on_long`).
88    pub fn is_key_pressed_repeat(&self, key: KeyboardKey) -> bool {
89        if let Some(signal) = self.keys.get(&key) {
90            signal.is_on_long()
91        } else {
92            false
93        }
94    }
95
96    /// Returns `true` while a key is currently held down.
97    pub fn is_key_down(&self, key: KeyboardKey) -> bool {
98        if let Some(signal) = self.keys.get(&key) {
99            signal.is_on()
100        } else {
101            false
102        }
103    }
104
105    /// Returns `true` while a key is not held down.
106    pub fn is_key_up(&self, key: KeyboardKey) -> bool {
107        !self.is_key_down(key)
108    }
109
110    /// Returns `true` on the frame a key is released (falling edge).
111    pub fn is_key_released(&self, key: KeyboardKey) -> bool {
112        if let Some(signal) = self.keys.get(&key) {
113            signal.falling_edge()
114        } else {
115            false
116        }
117    }
118
119    /// Returns the most recently pressed key according to provider iteration order.
120    ///
121    /// The return value reflects the last key in `get_pressed_keys()` from the
122    /// most recent [`update`](Self::update) call.
123    pub fn get_key_pressed(&mut self) -> Option<KeyboardKey> {
124        self.latest_key
125    }
126}
127
128/// FIFO buffer for typed characters from a text provider.
129///
130/// Call [`update`](Self::update) each frame to append new characters, then
131/// consume them with [`pop`](Self::pop) or inspect with [`peek`](Self::peek).
132pub struct TextManager {
133    buffer: VecDeque<char>,
134}
135
136impl Default for TextManager {
137    fn default() -> Self {
138        Self::new()
139    }
140}
141
142impl TextManager {
143    /// Creates an empty text buffer.
144    pub fn new() -> TextManager {
145        Self {
146            buffer: Default::default(),
147        }
148    }
149
150    /// Appends characters from `provider` to the end of the buffer.
151    pub fn update(&mut self, provider: &dyn SimpleTextProvider) {
152        for c in provider.get_pressed_chars() {
153            self.buffer.push_back(c);
154        }
155    }
156
157    /// Discards all buffered characters.
158    pub fn clear(&mut self) {
159        self.buffer.clear();
160    }
161
162    /// Removes and returns the oldest buffered character.
163    pub fn pop(&mut self) -> Option<char> {
164        self.buffer.pop_front()
165    }
166
167    /// Returns a reference to the oldest buffered character without removing it.
168    pub fn peek(&mut self) -> Option<&char> {
169        self.buffer.front()
170    }
171}