diff --git a/crates/bevy_input_focus/src/lib.rs b/crates/bevy_input_focus/src/lib.rs index 400f36986f..3233fa3e78 100644 --- a/crates/bevy_input_focus/src/lib.rs +++ b/crates/bevy_input_focus/src/lib.rs @@ -20,16 +20,10 @@ pub mod tab_navigation; use bevy_app::{App, Plugin, PreUpdate, Startup}; use bevy_ecs::{ - component::Component, - entity::Entity, - event::{Event, EventReader}, - query::{QueryData, With}, - system::{Commands, Query, Res, ResMut, Resource, Single, SystemParam}, - traversal::Traversal, - world::{Command, DeferredWorld, World}, + prelude::*, query::QueryData, system::SystemParam, traversal::Traversal, world::DeferredWorld, }; use bevy_hierarchy::{HierarchyQueryExt, Parent}; -use bevy_input::keyboard::KeyboardInput; +use bevy_input::{gamepad::GamepadButtonChangedEvent, keyboard::KeyboardInput, mouse::MouseWheel}; use bevy_window::{PrimaryWindow, Window}; use core::fmt::Debug; @@ -111,17 +105,22 @@ impl SetInputFocus for Commands<'_, '_> { } } -/// A bubble-able event for keyboard input. This event is normally dispatched to the current -/// input focus entity, if any. If no entity has input focus, then the event is dispatched to -/// the main window. +/// A bubble-able user input event that starts at the currently focused entity. +/// +/// This event is normally dispatched to the current input focus entity, if any. +/// If no entity has input focus, then the event is dispatched to the main window. +/// +/// To set up your own bubbling input event, add the [`dispatch_focused_input::`](dispatch_focused_input) system to your app, +/// in the [`InputFocusSet::Dispatch`] system set during [`PreUpdate`]. #[derive(Clone, Debug, Component)] -pub struct FocusKeyboardInput { - /// The keyboard input event. - pub input: KeyboardInput, +pub struct FocusedInput { + /// The underlying input event. + pub input: E, + /// The primary window entity. window: Entity, } -impl Event for FocusKeyboardInput { +impl Event for FocusedInput { type Traversal = WindowTraversal; const AUTO_PROPAGATE: bool = true; @@ -134,8 +133,8 @@ pub struct WindowTraversal { window: Option<&'static Window>, } -impl Traversal for WindowTraversal { - fn traverse(item: Self::Item<'_>, event: &FocusKeyboardInput) -> Option { +impl Traversal> for WindowTraversal { + fn traverse(item: Self::Item<'_>, event: &FocusedInput) -> Option { let WindowTraversalItem { parent, window } = item; // Send event to parent, if it has one. @@ -161,10 +160,27 @@ impl Plugin for InputDispatchPlugin { app.add_systems(Startup, set_initial_focus) .insert_resource(InputFocus(None)) .insert_resource(InputFocusVisible(false)) - .add_systems(PreUpdate, dispatch_keyboard_input); + .add_systems( + PreUpdate, + ( + dispatch_focused_input::, + dispatch_focused_input::, + dispatch_focused_input::, + ) + .in_set(InputFocusSet::Dispatch), + ); } } +/// System sets for [`bevy_input_focus`](crate). +/// +/// These systems run in the [`PreUpdate`] schedule. +#[derive(SystemSet, Debug, PartialEq, Eq, Hash, Clone)] +pub enum InputFocusSet { + /// System which dispatches bubbled input events to the focused entity, or to the primary window. + Dispatch, +} + /// Sets the initial focus to the primary window, if any. pub fn set_initial_focus( mut input_focus: ResMut, @@ -173,10 +189,10 @@ pub fn set_initial_focus( input_focus.0 = Some(*window); } -/// System which dispatches keyboard input events to the focused entity, or to the primary window +/// System which dispatches bubbled input events to the focused entity, or to the primary window /// if no entity has focus. -fn dispatch_keyboard_input( - mut key_events: EventReader, +pub fn dispatch_focused_input( + mut key_events: EventReader, focus: Res, windows: Query>, mut commands: Commands, @@ -186,7 +202,7 @@ fn dispatch_keyboard_input( if let Some(focus_elt) = focus.0 { for ev in key_events.read() { commands.trigger_targets( - FocusKeyboardInput { + FocusedInput { input: ev.clone(), window, }, @@ -198,7 +214,7 @@ fn dispatch_keyboard_input( // There should be only one primary window. for ev in key_events.read() { commands.trigger_targets( - FocusKeyboardInput { + FocusedInput { input: ev.clone(), window, }, @@ -326,7 +342,7 @@ mod tests { struct GatherKeyboardEvents(String); fn gather_keyboard_events( - trigger: Trigger, + trigger: Trigger>, mut query: Query<&mut GatherKeyboardEvents>, ) { if let Ok(mut gather) = query.get_mut(trigger.target()) { diff --git a/crates/bevy_input_focus/src/tab_navigation.rs b/crates/bevy_input_focus/src/tab_navigation.rs index 1bc7d300e7..00706fdba7 100644 --- a/crates/bevy_input_focus/src/tab_navigation.rs +++ b/crates/bevy_input_focus/src/tab_navigation.rs @@ -36,11 +36,14 @@ use bevy_ecs::{ world::DeferredWorld, }; use bevy_hierarchy::{Children, HierarchyQueryExt, Parent}; -use bevy_input::{keyboard::KeyCode, ButtonInput, ButtonState}; +use bevy_input::{ + keyboard::{KeyCode, KeyboardInput}, + ButtonInput, ButtonState, +}; use bevy_utils::tracing::warn; use bevy_window::PrimaryWindow; -use crate::{FocusKeyboardInput, InputFocus, InputFocusVisible, SetInputFocus}; +use crate::{FocusedInput, InputFocus, InputFocusVisible, SetInputFocus}; /// A component which indicates that an entity wants to participate in tab navigation. /// @@ -263,8 +266,11 @@ fn setup_tab_navigation(mut commands: Commands, window: Query, + mut trigger: Trigger>, nav: TabNavigation, mut focus: ResMut, mut visible: ResMut,