diff --git a/Cargo.toml b/Cargo.toml index 4dc83dcc41e12..30d5f04fa12ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -405,7 +405,7 @@ bevy_log = ["bevy_internal/bevy_log"] bevy_input_focus = ["bevy_internal/bevy_input_focus"] # Headless widget collection for Bevy UI. -bevy_ui_widgets = ["bevy_internal/bevy_ui_widgets"] +bevy_ui_widgets = ["bevy_internal/bevy_ui_widgets", "bevy_input_focus"] # Feathers widget collection. experimental_bevy_feathers = ["bevy_internal/bevy_feathers", "bevy_ui_widgets"] diff --git a/crates/bevy_input_focus/src/lib.rs b/crates/bevy_input_focus/src/lib.rs index 47efd05797875..a82a0bc475203 100644 --- a/crates/bevy_input_focus/src/lib.rs +++ b/crates/bevy_input_focus/src/lib.rs @@ -219,6 +219,7 @@ impl Traversal for WindowTraversal { /// /// To add bubbling to your own input events, add the [`dispatch_focused_input::`](dispatch_focused_input) system to your app, /// as described in the docs for [`FocusedInput`]. +#[derive(Default)] pub struct InputDispatchPlugin; impl Plugin for InputDispatchPlugin { diff --git a/crates/bevy_internal/src/default_plugins.rs b/crates/bevy_internal/src/default_plugins.rs index 0f75594b7bf91..d4e1b6789823b 100644 --- a/crates/bevy_internal/src/default_plugins.rs +++ b/crates/bevy_internal/src/default_plugins.rs @@ -12,6 +12,10 @@ plugin_group! { bevy_transform:::TransformPlugin, bevy_diagnostic:::DiagnosticsPlugin, bevy_input:::InputPlugin, + // NOTE: Assuming FeathersPlugin is added when "bevy_feathers" is enabled, + // This is not included due to the redundancy. + #[custom(cfg(all(feature = "bevy_input_focus", not(feature = "bevy_feathers"))))] + bevy_input_focus:::InputDispatchPlugin, #[custom(cfg(not(feature = "bevy_window")))] bevy_app:::ScheduleRunnerPlugin, #[cfg(feature = "bevy_window")] @@ -65,6 +69,8 @@ plugin_group! { bevy_ui:::UiPlugin, #[cfg(feature = "bevy_ui_render")] bevy_ui_render:::UiRenderPlugin, + #[custom(cfg(any(feature = "bevy_ui_widgets", feature = "bevy_feathers")))] + bevy_ui_widgets:::UiWidgetsButtonPlugin, #[cfg(feature = "bevy_gltf")] bevy_gltf:::GltfPlugin, #[cfg(feature = "bevy_pbr")] diff --git a/crates/bevy_ui_widgets/src/button.rs b/crates/bevy_ui_widgets/src/button.rs index 4c98f6063d8d6..2484cfa170946 100644 --- a/crates/bevy_ui_widgets/src/button.rs +++ b/crates/bevy_ui_widgets/src/button.rs @@ -113,9 +113,10 @@ fn button_on_pointer_cancel( } /// Plugin that adds the observers for the [`Button`] widget. -pub struct ButtonPlugin; +#[derive(Default)] +pub struct UiWidgetsButtonPlugin; -impl Plugin for ButtonPlugin { +impl Plugin for UiWidgetsButtonPlugin { fn build(&self, app: &mut App) { app.add_observer(button_on_key_event) .add_observer(button_on_pointer_down) diff --git a/crates/bevy_ui_widgets/src/lib.rs b/crates/bevy_ui_widgets/src/lib.rs index 8f30e2842701a..8a27547bfe660 100644 --- a/crates/bevy_ui_widgets/src/lib.rs +++ b/crates/bevy_ui_widgets/src/lib.rs @@ -42,13 +42,14 @@ use crate::popover::PopoverPlugin; /// A plugin group that registers the observers for all of the widgets in this crate. If you don't want to /// use all of the widgets, you can import the individual widget plugins instead. +/// [`button::UiWidgetsButtonPlugin`] is part of [`DefaultPlugins`]. +#[derive(Default)] pub struct UiWidgetsPlugins; impl PluginGroup for UiWidgetsPlugins { fn build(self) -> PluginGroupBuilder { PluginGroupBuilder::start::() .add(PopoverPlugin) - .add(ButtonPlugin) .add(CheckboxPlugin) .add(MenuPlugin) .add(RadioGroupPlugin) diff --git a/examples/2d/dynamic_mip_generation.rs b/examples/2d/dynamic_mip_generation.rs index 8e776ee749f34..69c6bd0581384 100644 --- a/examples/2d/dynamic_mip_generation.rs +++ b/examples/2d/dynamic_mip_generation.rs @@ -225,11 +225,7 @@ fn main() { .add_systems(Update, animate_image_scale) .add_systems( Update, - ( - widgets::handle_ui_interactions::, - update_radio_buttons, - ) - .chain(), + update_radio_buttons.run_if(resource_changed::), ) .add_systems( Update, @@ -237,10 +233,9 @@ fn main() { ) .add_systems( Update, - handle_app_setting_change - .after(widgets::handle_ui_interactions::) - .before(regenerate_image_when_requested), - ); + handle_app_setting_change.before(regenerate_image_when_requested), + ) + .add_observer(widgets::handle_ui_button_interaction_on_click::); // Because `MipGenerationJobs` is part of the render app, we need to add the // associated systems to that app, not the main one. diff --git a/examples/3d/clustered_decal_maps.rs b/examples/3d/clustered_decal_maps.rs index 0354b359b1f07..1e2b01d55802b 100644 --- a/examples/3d/clustered_decal_maps.rs +++ b/examples/3d/clustered_decal_maps.rs @@ -152,14 +152,12 @@ fn main() { .add_systems( Update, ( - widgets::handle_ui_interactions::, - update_radio_buttons, - ), - ) - .add_systems( - Update, - handle_emission_type_change.after(widgets::handle_ui_interactions::), + handle_emission_type_change, + update_radio_buttons.run_if(resource_changed::), + ) + .chain(), ) + .add_observer(widgets::handle_ui_button_interaction_on_click::) .insert_resource(SeededRng(ChaCha8Rng::seed_from_u64(19878367467712))) .run(); } diff --git a/examples/3d/clustered_decals.rs b/examples/3d/clustered_decals.rs index d0b8b5bc11174..c159f078e8502 100644 --- a/examples/3d/clustered_decals.rs +++ b/examples/3d/clustered_decals.rs @@ -8,6 +8,7 @@ use bevy::{ input::mouse::AccumulatedMouseMotion, light::ClusteredDecal, pbr::{decal, ExtendedMaterial, MaterialExtension}, + picking::hover::Hovered, prelude::*, render::{ render_resource::AsBindGroup, @@ -135,11 +136,13 @@ fn main() { .add_systems(Startup, setup) .add_systems(Update, draw_gizmos) .add_systems(Update, rotate_cube) - .add_systems(Update, widgets::handle_ui_interactions::) .add_systems( Update, - (handle_selection_change, update_radio_buttons) - .after(widgets::handle_ui_interactions::), + ( + handle_selection_change, + update_radio_buttons.run_if(resource_changed::), + ) + .chain(), ) .add_systems(Update, process_move_input) .add_systems(Update, process_scale_input) @@ -147,6 +150,7 @@ fn main() { .add_systems(Update, switch_drag_mode) .add_systems(Update, update_help_text) .add_systems(Update, update_button_visibility) + .add_observer(widgets::handle_ui_button_interaction_on_click::) .run(); } @@ -288,6 +292,8 @@ fn drag_button(label: &str) -> impl Bundle { }, Button, BackgroundColor(Color::BLACK), + // Detect the hover. + Hovered::default(), BUTTON_BORDER_COLOR, children![widgets::ui_text(label, Color::WHITE)], ) @@ -490,7 +496,7 @@ fn create_help_string(app_status: &AppStatus) -> String { /// mode back to its default value of [`DragMode::Move`]. fn switch_drag_mode( mut commands: Commands, - mut interactions: Query<(&Interaction, &DragMode)>, + mut button_interactions: Query<(&Hovered, &DragMode), With