1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
|
//! Input with binding abstraction
//!
//! Binding keys or combinations to specific actions with just raylib alone is difficult to handle in
//! input-heavy applications, as opposed to games. This is an optimisation effort. To understand how
//! it works, the first thing to know is that there are two main modes for bindings, which are local
//! and global. A local binding is specific to a certain area of the window and is used to block the
//! mouse from being sent to many different targets (think of a pop-up window over the editor, which
//! must capture the mouse). Global bindings will be processed as long as no local binding has
//! prevalence, but they do not have an area that needs to be managed by a handler.
//!
//! In summary, the local <-> global distinction is used to capture the mouse.
//!
//! Some elements want to capture the keyboard, for instance, when activating a text box, the text
//! input should only go to this box, but should a tool be bound to a character, it should not
//! activate when typing. For this purpose, any element may seize control as long as no other element
//! still has the focus. A channel is opened and no bindings will be processed. Instead the text
//! together with a few control characters is relayed directly to the channel, until the receiver
//! hangs up.
//!
//! In summary, a channel is used to seize control of the keyboard when typing into an element.
pub mod binding;
pub mod button;
pub use binding::*;
pub use button::*;
use crate::math::{ExactSurface, Rect, Vec2};
use crate::stable_vec::StableVec;
use raylib::ffi::KeyboardKey;
use raylib::RaylibHandle;
use std::collections::HashMap;
use std::sync::mpsc::{self, Receiver, Sender};
/// Input and binding handler this should only be created once per instance.
pub struct Input {
global_bindings: HashMap<Binding, bool>,
local_bindings: StableVec<(Rect<u16>, HashMap<Binding, bool>)>,
last_text: String,
text_pipe: Option<Sender<char>>,
mouse_pos: Vec2<u16>,
}
impl Input {
/// Create a new Input and binding handler.
pub fn new(rl: &RaylibHandle) -> Self {
Self {
global_bindings: HashMap::new(),
local_bindings: StableVec::new(),
last_text: String::new(),
text_pipe: None,
mouse_pos: Vec2::new(rl.get_mouse_x() as u16, rl.get_mouse_y() as u16),
}
}
/// Must be called on every frame of the program, since keypresses will be processed here. This
/// will not activate the binding function directly, since raylib is heavily polling focused.
pub fn update(&mut self, rl: &mut RaylibHandle) {
self.mouse_pos = Vec2::new(rl.get_mouse_x() as u16, rl.get_mouse_y() as u16);
/* Read the next character to be sent with some extra characters
* raylib doesn't recognize to be valid.
*/
let c = if rl.is_key_pressed(KeyboardKey::KEY_ENTER) {
Some('\n')
} else if rl.is_key_pressed(KeyboardKey::KEY_ESCAPE) {
Some('\x1B')
} else if rl.is_key_pressed(KeyboardKey::KEY_BACKSPACE) {
Some('\x7f')
} else {
rl.get_key_pressed_number().map(|c| c as u8 as char)
};
/* Send the character to the listening entity or push it to the text that
* is currently being read for the keybindings.
*/
if let Some(text_pipe) = self.text_pipe.as_mut() {
if let Some(c) = c {
if text_pipe.send(c).is_err() {
self.last_text.push(c);
self.text_pipe = None;
}
}
} else if let Some(c) = c {
self.last_text.push(c);
}
/* Update the local parts. The local stack has priority over the global
* bindings, so it is processed first, with the priority going from the
* top of the stack to the bottom in that order (reversed vec order)
*/
let mut mouse_blocked = false;
for (_, (rect, bindings)) in self.local_bindings.id_iter_mut().rev() {
if rect.contains_point(&self.mouse_pos) {
for (binding, state) in &mut bindings.iter_mut() {
*state = binding.is_pressed(
!mouse_blocked,
self.text_pipe.is_none(),
&self.last_text,
rl,
);
if *state {
self.last_text.clear();
}
}
mouse_blocked = true;
break;
}
}
/* Process the global bindings, as long as nothing prevents the bindings
* from being processed like a local binding or the text being captured.
*/
for (binding, state) in self.global_bindings.iter_mut() {
*state = binding.is_pressed(
!mouse_blocked,
self.text_pipe.is_none(),
&self.last_text,
rl,
);
if *state {
self.last_text.clear();
}
}
}
/// Add a global binding. This is necessary so the input knows which key presses to monitor.
pub fn add_global(&mut self, binding: Binding) -> bool {
self.global_bindings.insert(binding, false).is_none()
}
/// Add a local binding handler for the given area. Returns a unique and unchanging handler id.
/// Handlers with higher ids (that have been added later) are preferred over old handlers.
pub fn add_local_handler(&mut self, area: Rect<u16>) -> usize {
self.local_bindings.push((area, HashMap::new()))
}
/// Add a local binding for the given handler.
pub fn add_local(&mut self, handler_id: usize, binding: Binding) -> bool {
self.local_bindings
.get_mut(handler_id)
.expect("Handler does not exist")
.1
.insert(binding, false)
.is_none()
}
/// Update the binding rectangle of a handler.
pub fn set_binding_rect(&mut self, handler_id: usize, rect: Rect<u16>) {
self.local_bindings
.get_mut(handler_id)
.expect("Handler does not exist")
.0 = rect;
}
/// Check if a global binding has been activated this frame. If so, it returns true.
/// This will only activate once, so there is no need to worry about multiple function calls
/// when the user keeps the button down.
pub fn poll_global(&mut self, binding: &Binding) -> bool {
let state = self.global_bindings.get_mut(&binding);
if state.is_none() {
error!("Tried to poll binding that isn't registered.");
return false;
}
*state.unwrap()
}
/// Like `poll_global` bun instead checks the bindings of the local handler with the given id.
pub fn poll_local(&mut self, handler_id: usize, binding: &Binding) -> bool {
let (_, bindings) = self
.local_bindings
.get_mut(handler_id)
.expect("Invalid binding handler id");
let state = bindings.get_mut(&binding);
if state.is_none() {
error!("Tried to poll binding that isn't registered.");
return false;
}
*state.unwrap()
}
/// Attempts to capture all keyboard input from here on. If no other component is currently
/// capturing, it returns a receiver that can be used. When the entity no longer wants to
/// capture the keyboard, control must be returned by dropping the receiver.
pub fn try_capture_keyboard(&mut self) -> Option<Receiver<char>> {
if self.text_pipe.is_some() {
return None;
}
let (tx, rx) = mpsc::channel();
self.text_pipe = Some(tx);
Some(rx)
}
}
|