Skip to content

Commit e7c6228

Browse files
Fix window spawning triggering ButtonInput<KeyCode>::just_pressed/just_released (#12372)
# Objective Fix #12273 ## Solution – Only emit `KeyboardFocusLost` when the keyboard focus is lost – ignore synthetic key releases too, not just key presses (as they're already covered by `KeyboardFocusLost`) --- ## Changelog ### Fixed - Don't trigger `ButtonInput<KeyCode>::just_pressed`/`just_released` when spawning a window/focus moving between Bevy windows
1 parent c2d193a commit e7c6228

File tree

5 files changed

+32
-19
lines changed

5 files changed

+32
-19
lines changed

crates/bevy_input/src/button_input.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,7 @@ use {
6262
///
6363
/// `ButtonInput<KeyCode>` is tied to window focus. For example, if the user holds a button
6464
/// while the window loses focus, [`ButtonInput::just_released`] will be triggered. Similarly if the window
65-
/// regains focus, [`ButtonInput::just_pressed`] will be triggered. Currently this happens even if the
66-
/// focus switches from one Bevy window to another (for example because a new window was just spawned).
65+
/// regains focus, [`ButtonInput::just_pressed`] will be triggered.
6766
///
6867
/// `ButtonInput<GamepadButton>` is independent of window focus.
6968
///

crates/bevy_input/src/keyboard.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ pub struct KeyboardFocusLost;
134134
/// ## Differences
135135
///
136136
/// The main difference between the [`KeyboardInput`] event and the [`ButtonInput<KeyCode>`] resources is that
137-
/// the latter have convenient functions such as [`ButtonInput::pressed`], [`ButtonInput::just_pressed`] and [`ButtonInput::just_released`].
137+
/// the latter has convenient functions such as [`ButtonInput::pressed`], [`ButtonInput::just_pressed`] and [`ButtonInput::just_released`] and is window id agnostic.
138138
pub fn keyboard_input_system(
139139
mut key_input: ResMut<ButtonInput<KeyCode>>,
140140
mut keyboard_input_events: EventReader<KeyboardInput>,

crates/bevy_winit/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use bevy_ecs::prelude::*;
2727
use bevy_window::{exit_on_all_closed, Window, WindowCreated};
2828
pub use converters::convert_system_cursor_icon;
2929
pub use state::{CursorSource, CustomCursorCache, CustomCursorCacheKey, PendingCursor};
30-
use system::{changed_windows, despawn_windows};
30+
use system::{changed_windows, check_keyboard_focus_lost, despawn_windows};
3131
pub use system::{create_monitors, create_windows};
3232
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
3333
pub use winit::platform::web::CustomCursorExtWebSys;
@@ -133,6 +133,7 @@ impl<T: Event> Plugin for WinitPlugin<T> {
133133
// so we don't need to care about its ordering relative to `changed_windows`
134134
changed_windows.ambiguous_with(exit_on_all_closed),
135135
despawn_windows,
136+
check_keyboard_focus_lost,
136137
)
137138
.chain(),
138139
);

crates/bevy_winit/src/state.rs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use bevy_ecs::{
1010
};
1111
use bevy_input::{
1212
gestures::*,
13-
keyboard::KeyboardFocusLost,
1413
mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel},
1514
};
1615
use bevy_log::{error, trace, warn};
@@ -267,18 +266,14 @@ impl<T: Event> ApplicationHandler<T> for WinitAppRunnerState<T> {
267266
.send(WindowCloseRequested { window }),
268267
WindowEvent::KeyboardInput {
269268
ref event,
270-
is_synthetic,
269+
// On some platforms, winit sends "synthetic" key press events when the window
270+
// gains or loses focus. These should not be handled, so we only process key
271+
// events if they are not synthetic key presses.
272+
is_synthetic: false,
271273
..
272274
} => {
273-
// Winit sends "synthetic" key press events when the window gains focus. These
274-
// should not be handled, so we only process key events if they are not synthetic
275-
// key presses. "synthetic" key release events should still be handled though, for
276-
// properly releasing keys when the window loses focus.
277-
if !(is_synthetic && event.state.is_pressed()) {
278-
// Process the keyboard input event, as long as it's not a synthetic key press.
279-
self.bevy_window_events
280-
.send(converters::convert_keyboard_input(event, window));
281-
}
275+
self.bevy_window_events
276+
.send(converters::convert_keyboard_input(event, window));
282277
}
283278
WindowEvent::CursorMoved { position, .. } => {
284279
let physical_position = DVec2::new(position.x, position.y);
@@ -354,9 +349,6 @@ impl<T: Event> ApplicationHandler<T> for WinitAppRunnerState<T> {
354349
win.focused = focused;
355350
self.bevy_window_events
356351
.send(WindowFocused { window, focused });
357-
if !focused {
358-
self.bevy_window_events.send(KeyboardFocusLost);
359-
}
360352
}
361353
WindowEvent::Occluded(occluded) => {
362354
self.bevy_window_events

crates/bevy_winit/src/system.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ use bevy_ecs::{
66
removal_detection::RemovedComponents,
77
system::{Local, NonSendMut, Query, SystemParamItem},
88
};
9+
use bevy_input::keyboard::KeyboardFocusLost;
910
use bevy_utils::tracing::{error, info, warn};
1011
use bevy_window::{
1112
ClosingWindow, Monitor, PrimaryMonitor, RawHandleWrapper, VideoMode, Window, WindowClosed,
12-
WindowClosing, WindowCreated, WindowMode, WindowResized, WindowWrapper,
13+
WindowClosing, WindowCreated, WindowFocused, WindowMode, WindowResized, WindowWrapper,
1314
};
1415

1516
use winit::{
@@ -122,6 +123,26 @@ pub fn create_windows<F: QueryFilter + 'static>(
122123
}
123124
}
124125

126+
/// Check whether keyboard focus was lost. This is different from window
127+
/// focus in that swapping between Bevy windows keeps window focus.
128+
pub(crate) fn check_keyboard_focus_lost(
129+
mut focus_events: EventReader<WindowFocused>,
130+
mut keyboard_focus: EventWriter<KeyboardFocusLost>,
131+
) {
132+
let mut focus_lost = false;
133+
let mut focus_gained = false;
134+
for e in focus_events.read() {
135+
if e.focused {
136+
focus_gained = true;
137+
} else {
138+
focus_lost = true;
139+
}
140+
}
141+
if focus_lost & !focus_gained {
142+
keyboard_focus.send(KeyboardFocusLost);
143+
}
144+
}
145+
125146
/// Synchronize available monitors as reported by [`winit`] with [`Monitor`] entities in the world.
126147
pub fn create_monitors(
127148
event_loop: &ActiveEventLoop,

0 commit comments

Comments
 (0)