diff --git a/crates/bevy_input/src/button_input.rs b/crates/bevy_input/src/button_input.rs index 31c27f7330..d329470d8b 100644 --- a/crates/bevy_input/src/button_input.rs +++ b/crates/bevy_input/src/button_input.rs @@ -62,8 +62,7 @@ use { /// /// `ButtonInput` is tied to window focus. For example, if the user holds a button /// while the window loses focus, [`ButtonInput::just_released`] will be triggered. Similarly if the window -/// regains focus, [`ButtonInput::just_pressed`] will be triggered. Currently this happens even if the -/// focus switches from one Bevy window to another (for example because a new window was just spawned). +/// regains focus, [`ButtonInput::just_pressed`] will be triggered. /// /// `ButtonInput` is independent of window focus. /// diff --git a/crates/bevy_input/src/keyboard.rs b/crates/bevy_input/src/keyboard.rs index 223003c85c..018c2feb1f 100644 --- a/crates/bevy_input/src/keyboard.rs +++ b/crates/bevy_input/src/keyboard.rs @@ -134,7 +134,7 @@ pub struct KeyboardFocusLost; /// ## Differences /// /// The main difference between the [`KeyboardInput`] event and the [`ButtonInput`] resources is that -/// the latter have convenient functions such as [`ButtonInput::pressed`], [`ButtonInput::just_pressed`] and [`ButtonInput::just_released`]. +/// the latter has convenient functions such as [`ButtonInput::pressed`], [`ButtonInput::just_pressed`] and [`ButtonInput::just_released`] and is window id agnostic. pub fn keyboard_input_system( mut key_input: ResMut>, mut keyboard_input_events: EventReader, diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index bd1cee59c3..9313abf607 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -27,7 +27,7 @@ use bevy_ecs::prelude::*; use bevy_window::{exit_on_all_closed, Window, WindowCreated}; pub use converters::convert_system_cursor_icon; pub use state::{CursorSource, CustomCursorCache, CustomCursorCacheKey, PendingCursor}; -use system::{changed_windows, despawn_windows}; +use system::{changed_windows, check_keyboard_focus_lost, despawn_windows}; pub use system::{create_monitors, create_windows}; #[cfg(all(target_family = "wasm", target_os = "unknown"))] pub use winit::platform::web::CustomCursorExtWebSys; @@ -133,6 +133,7 @@ impl Plugin for WinitPlugin { // so we don't need to care about its ordering relative to `changed_windows` changed_windows.ambiguous_with(exit_on_all_closed), despawn_windows, + check_keyboard_focus_lost, ) .chain(), ); diff --git a/crates/bevy_winit/src/state.rs b/crates/bevy_winit/src/state.rs index 53c21e71ec..56006e56a3 100644 --- a/crates/bevy_winit/src/state.rs +++ b/crates/bevy_winit/src/state.rs @@ -10,7 +10,6 @@ use bevy_ecs::{ }; use bevy_input::{ gestures::*, - keyboard::KeyboardFocusLost, mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel}, }; use bevy_log::{error, trace, warn}; @@ -267,18 +266,14 @@ impl ApplicationHandler for WinitAppRunnerState { .send(WindowCloseRequested { window }), WindowEvent::KeyboardInput { ref event, - is_synthetic, + // On some platforms, winit sends "synthetic" key press events when the window + // gains or loses focus. These should not be handled, so we only process key + // events if they are not synthetic key presses. + is_synthetic: false, .. } => { - // Winit sends "synthetic" key press events when the window gains focus. These - // should not be handled, so we only process key events if they are not synthetic - // key presses. "synthetic" key release events should still be handled though, for - // properly releasing keys when the window loses focus. - if !(is_synthetic && event.state.is_pressed()) { - // Process the keyboard input event, as long as it's not a synthetic key press. - self.bevy_window_events - .send(converters::convert_keyboard_input(event, window)); - } + self.bevy_window_events + .send(converters::convert_keyboard_input(event, window)); } WindowEvent::CursorMoved { position, .. } => { let physical_position = DVec2::new(position.x, position.y); @@ -354,9 +349,6 @@ impl ApplicationHandler for WinitAppRunnerState { win.focused = focused; self.bevy_window_events .send(WindowFocused { window, focused }); - if !focused { - self.bevy_window_events.send(KeyboardFocusLost); - } } WindowEvent::Occluded(occluded) => { self.bevy_window_events diff --git a/crates/bevy_winit/src/system.rs b/crates/bevy_winit/src/system.rs index 2fa86d1e51..34a4dddc51 100644 --- a/crates/bevy_winit/src/system.rs +++ b/crates/bevy_winit/src/system.rs @@ -6,10 +6,11 @@ use bevy_ecs::{ removal_detection::RemovedComponents, system::{Local, NonSendMut, Query, SystemParamItem}, }; +use bevy_input::keyboard::KeyboardFocusLost; use bevy_utils::tracing::{error, info, warn}; use bevy_window::{ ClosingWindow, Monitor, PrimaryMonitor, RawHandleWrapper, VideoMode, Window, WindowClosed, - WindowClosing, WindowCreated, WindowMode, WindowResized, WindowWrapper, + WindowClosing, WindowCreated, WindowFocused, WindowMode, WindowResized, WindowWrapper, }; use winit::{ @@ -122,6 +123,26 @@ pub fn create_windows( } } +/// Check whether keyboard focus was lost. This is different from window +/// focus in that swapping between Bevy windows keeps window focus. +pub(crate) fn check_keyboard_focus_lost( + mut focus_events: EventReader, + mut keyboard_focus: EventWriter, +) { + let mut focus_lost = false; + let mut focus_gained = false; + for e in focus_events.read() { + if e.focused { + focus_gained = true; + } else { + focus_lost = true; + } + } + if focus_lost & !focus_gained { + keyboard_focus.send(KeyboardFocusLost); + } +} + /// Synchronize available monitors as reported by [`winit`] with [`Monitor`] entities in the world. pub fn create_monitors( event_loop: &ActiveEventLoop,