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}