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
//! Decide whether a keybind/operator gate should refuse because the
//! user is typing into a UI text input.
//!
//! Why a wrapper exists: Bevy's [`bevy::input_focus::InputFocus`]
//! `set_initial_focus` system runs in `PostStartup` and assigns the
//! `PrimaryWindow` entity as the focused entity when nothing else has
//! claimed focus yet. A gate written as `input_focus.0.is_none()`
//! therefore reads "user is typing" whenever the editor is in its
//! post-load steady state, and refuses the keybind. In production the
//! viewport-click handler clears focus, masking the bug; in headless
//! tests (and on the very first key press after launch) the gate
//! refuses spuriously.
//!
//! [`KeybindFocus`] returns `is_typing()` only when the focused entity
//! has a [`TextInputNode`].
use bevy::ecs::system::SystemParam;
use bevy::input_focus::InputFocus;
use bevy::prelude::*;
use jackdaw_api::prelude::ActionSources;
use jackdaw_feathers::text_edit::TextInputNode;
/// `SystemParam` that returns whether keybinds and operator dispatches
/// should be suppressed because the user is editing a text input.
#[derive(SystemParam)]
pub struct KeybindFocus<'w, 's> {
input_focus: Res<'w, InputFocus>,
text_inputs: Query<'w, 's, (), With<TextInputNode>>,
}
impl KeybindFocus<'_, '_> {
/// True when the focused entity carries a `TextInputNode`.
/// Used by gate predicates to refuse keyboard-driven operators
/// while the user is editing a text field.
pub fn is_typing(&self) -> bool {
let Some(focused) = self.input_focus.0 else {
return false;
};
self.text_inputs.contains(focused)
}
/// True if the input focus changed since the system last ran.
pub fn is_changed(&self) -> bool {
self.input_focus.is_changed()
}
}
pub(crate) fn disable_keyboard_input_when_typing(
focus: KeybindFocus,
mut sources: ResMut<ActionSources>,
) {
if !focus.is_changed() {
return;
}
sources.keyboard = !focus.is_typing();
}