From 5335b245f454b0f1b6565c93fb1f4e581cc2655c Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Thu, 12 Mar 2026 21:40:59 -0700 Subject: [PATCH 01/46] Squashed commit of the following: commit cf4a2dbdc9c27820317da2908a55942b21ed7f9d Merge: 41c5874f4 3fea954ea Author: Luke Yoo Date: Thu Mar 12 21:40:00 2026 -0700 Merge branch 'main' into ui-widgets-to-default commit 41c5874f4c9d082cf95aae6469bbb30cf42c6c1d Author: Luke Yoo Date: Thu Mar 12 21:35:43 2026 -0700 Remove unused imports commit 24987e82b1c70933c1517e3b9526f6eb778abd9c Author: Luke Yoo Date: Thu Mar 12 21:10:19 2026 -0700 Add InputDispatchPlugin at UIWidgetPlugins commit 8bd32958572b154a36dd7237ec4f03bb3a9f8aca Author: Luke Yoo Date: Thu Mar 12 16:02:59 2026 -0700 Add `UiWidgetsPlugins` to the plugin group for `DefaultPlugins` --- crates/bevy_feathers/src/lib.rs | 5 +---- crates/bevy_internal/src/default_plugins.rs | 3 +++ crates/bevy_ui_widgets/src/lib.rs | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/bevy_feathers/src/lib.rs b/crates/bevy_feathers/src/lib.rs index ed6517d84d8d6..3dce1fffae53d 100644 --- a/crates/bevy_feathers/src/lib.rs +++ b/crates/bevy_feathers/src/lib.rs @@ -23,11 +23,10 @@ use bevy_app::{ }; use bevy_asset::embedded_asset; use bevy_ecs::{query::With, schedule::IntoScheduleConfigs}; -use bevy_input_focus::{tab_navigation::TabNavigationPlugin, InputDispatchPlugin}; +use bevy_input_focus::tab_navigation::TabNavigationPlugin; use bevy_text::{TextColor, TextFont}; use bevy_ui::UiSystems; use bevy_ui_render::UiMaterialPlugin; -use bevy_ui_widgets::UiWidgetsPlugins; use crate::{ alpha_pattern::{AlphaPatternMaterial, AlphaPatternResource}, @@ -101,8 +100,6 @@ pub struct FeathersPlugins; impl PluginGroup for FeathersPlugins { fn build(self) -> PluginGroupBuilder { PluginGroupBuilder::start::() - .add_group(UiWidgetsPlugins) - .add(InputDispatchPlugin) .add(TabNavigationPlugin) .add(FeathersPlugin) } diff --git a/crates/bevy_internal/src/default_plugins.rs b/crates/bevy_internal/src/default_plugins.rs index 0f75594b7bf91..d35d79c98a063 100644 --- a/crates/bevy_internal/src/default_plugins.rs +++ b/crates/bevy_internal/src/default_plugins.rs @@ -88,6 +88,9 @@ plugin_group! { #[cfg(feature = "hotpatching")] bevy_app::hotpatch:::HotPatchPlugin, #[plugin_group] + #[cfg(feature = "bevy_ui_widgets")] + bevy_ui_widgets:::UiWidgetsPlugins, + #[plugin_group] #[cfg(feature = "bevy_picking")] bevy_picking:::DefaultPickingPlugins, #[doc(hidden)] diff --git a/crates/bevy_ui_widgets/src/lib.rs b/crates/bevy_ui_widgets/src/lib.rs index 8f30e2842701a..1b7a55d225d2f 100644 --- a/crates/bevy_ui_widgets/src/lib.rs +++ b/crates/bevy_ui_widgets/src/lib.rs @@ -37,11 +37,13 @@ pub use slider::*; use bevy_app::{PluginGroup, PluginGroupBuilder}; use bevy_ecs::{entity::Entity, event::EntityEvent}; +use bevy_input_focus::InputDispatchPlugin; 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. +#[derive(Default)] pub struct UiWidgetsPlugins; impl PluginGroup for UiWidgetsPlugins { @@ -54,6 +56,7 @@ impl PluginGroup for UiWidgetsPlugins { .add(RadioGroupPlugin) .add(ScrollbarPlugin) .add(SliderPlugin) + .add(InputDispatchPlugin) } } From e7bab9fb8322e63fbbd0cc92f6441e5b9194b08b Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Thu, 12 Mar 2026 23:11:13 -0700 Subject: [PATCH 02/46] Apply migration guide to examples --- examples/ui/widgets/standard_widgets.rs | 6 ++---- examples/ui/widgets/standard_widgets_observers.rs | 9 ++------- examples/ui/widgets/vertical_slider.rs | 9 ++------- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/examples/ui/widgets/standard_widgets.rs b/examples/ui/widgets/standard_widgets.rs index 0f281a9dd607f..5d58fd384e0f4 100644 --- a/examples/ui/widgets/standard_widgets.rs +++ b/examples/ui/widgets/standard_widgets.rs @@ -10,7 +10,7 @@ use bevy::{ color::palettes::basic::*, input_focus::{ tab_navigation::{TabGroup, TabIndex, TabNavigationPlugin}, - InputDispatchPlugin, InputFocus, + InputFocus, }, picking::hover::Hovered, prelude::*, @@ -20,7 +20,7 @@ use bevy::{ popover::{Popover, PopoverAlign, PopoverPlacement, PopoverSide}, Activate, Button, Checkbox, CoreSliderDragState, MenuAction, MenuButton, MenuEvent, MenuItem, MenuPopup, RadioButton, RadioGroup, Slider, SliderRange, SliderThumb, - SliderValue, TrackClick, UiWidgetsPlugins, ValueChange, + SliderValue, TrackClick, ValueChange, }, }; @@ -28,8 +28,6 @@ fn main() { App::new() .add_plugins(( DefaultPlugins, - UiWidgetsPlugins, - InputDispatchPlugin, TabNavigationPlugin, )) .insert_resource(DemoWidgetStates { diff --git a/examples/ui/widgets/standard_widgets_observers.rs b/examples/ui/widgets/standard_widgets_observers.rs index c5a24f1833f71..585c9f11778a4 100644 --- a/examples/ui/widgets/standard_widgets_observers.rs +++ b/examples/ui/widgets/standard_widgets_observers.rs @@ -6,17 +6,14 @@ use bevy::{ color::palettes::basic::*, - input_focus::{ - tab_navigation::{TabGroup, TabIndex, TabNavigationPlugin}, - InputDispatchPlugin, - }, + input_focus::tab_navigation::{TabGroup, TabIndex, TabNavigationPlugin}, picking::hover::Hovered, prelude::*, reflect::Is, ui::{Checked, InteractionDisabled, Pressed}, ui_widgets::{ checkbox_self_update, observe, Activate, Button, Checkbox, Slider, SliderRange, - SliderThumb, SliderValue, UiWidgetsPlugins, ValueChange, + SliderThumb, SliderValue, ValueChange, }, }; @@ -24,8 +21,6 @@ fn main() { App::new() .add_plugins(( DefaultPlugins, - UiWidgetsPlugins, - InputDispatchPlugin, TabNavigationPlugin, )) .insert_resource(DemoWidgetStates { slider_value: 50.0 }) diff --git a/examples/ui/widgets/vertical_slider.rs b/examples/ui/widgets/vertical_slider.rs index e210e48155c62..b580c4083496e 100644 --- a/examples/ui/widgets/vertical_slider.rs +++ b/examples/ui/widgets/vertical_slider.rs @@ -1,15 +1,12 @@ //! Simple example showing vertical and horizontal slider widgets with snap behavior and value labels use bevy::{ - input_focus::{ - tab_navigation::{TabGroup, TabIndex, TabNavigationPlugin}, - InputDispatchPlugin, - }, + input_focus::tab_navigation::{TabGroup, TabIndex, TabNavigationPlugin}, picking::hover::Hovered, prelude::*, ui_widgets::{ observe, slider_self_update, CoreSliderDragState, Slider, SliderRange, SliderThumb, - SliderValue, TrackClick, UiWidgetsPlugins, + SliderValue, TrackClick, }, }; @@ -20,8 +17,6 @@ fn main() { App::new() .add_plugins(( DefaultPlugins, - UiWidgetsPlugins, - InputDispatchPlugin, TabNavigationPlugin, )) .add_systems(Startup, setup) From 6c8b886be2149e6bdb3136314deed69a76093189 Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Sat, 14 Mar 2026 15:33:55 -0700 Subject: [PATCH 03/46] Include `InputDispatchPlugin` in `DefaultPlugins` --- crates/bevy_input_focus/src/lib.rs | 1 + crates/bevy_internal/src/default_plugins.rs | 2 ++ crates/bevy_ui_widgets/src/lib.rs | 2 -- 3 files changed, 3 insertions(+), 2 deletions(-) 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 d35d79c98a063..71fa409c7cff6 100644 --- a/crates/bevy_internal/src/default_plugins.rs +++ b/crates/bevy_internal/src/default_plugins.rs @@ -12,6 +12,8 @@ plugin_group! { bevy_transform:::TransformPlugin, bevy_diagnostic:::DiagnosticsPlugin, bevy_input:::InputPlugin, + #[cfg(feature = "bevy_input_focus")] + bevy_input_focus:::InputDispatchPlugin, #[custom(cfg(not(feature = "bevy_window")))] bevy_app:::ScheduleRunnerPlugin, #[cfg(feature = "bevy_window")] diff --git a/crates/bevy_ui_widgets/src/lib.rs b/crates/bevy_ui_widgets/src/lib.rs index 1b7a55d225d2f..6ff9c69db9ee4 100644 --- a/crates/bevy_ui_widgets/src/lib.rs +++ b/crates/bevy_ui_widgets/src/lib.rs @@ -37,7 +37,6 @@ pub use slider::*; use bevy_app::{PluginGroup, PluginGroupBuilder}; use bevy_ecs::{entity::Entity, event::EntityEvent}; -use bevy_input_focus::InputDispatchPlugin; use crate::popover::PopoverPlugin; @@ -56,7 +55,6 @@ impl PluginGroup for UiWidgetsPlugins { .add(RadioGroupPlugin) .add(ScrollbarPlugin) .add(SliderPlugin) - .add(InputDispatchPlugin) } } From 714bd13e28b4c447434c2d45b30a20fe1a88ac5e Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Sun, 15 Mar 2026 13:06:16 -0700 Subject: [PATCH 04/46] Move popover and scrollbar to bevy_ui --- crates/bevy_ui/src/lib.rs | 2 ++ crates/bevy_ui/src/widget/mod.rs | 4 ++++ .../src => bevy_ui/src/widget}/popover.rs | 17 ++++++++++++----- .../src => bevy_ui/src/widget}/scrollbar.rs | 18 ++++++++++++++---- crates/bevy_ui_widgets/src/lib.rs | 7 ------- .../ui/navigation/directional_navigation.rs | 8 ++------ .../directional_navigation_overrides.rs | 8 ++------ examples/ui/scroll_and_overflow/scrollbars.rs | 16 ++++------------ examples/ui/widgets/standard_widgets.rs | 5 +---- .../ui/widgets/standard_widgets_observers.rs | 5 +---- examples/ui/widgets/tab_navigation.rs | 4 ++-- examples/ui/widgets/vertical_slider.rs | 5 +---- 12 files changed, 45 insertions(+), 54 deletions(-) rename crates/{bevy_ui_widgets/src => bevy_ui/src/widget}/popover.rs (96%) rename crates/{bevy_ui_widgets/src => bevy_ui/src/widget}/scrollbar.rs (96%) diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index d2e1e46589baf..79363ee4d7e14 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -216,6 +216,8 @@ impl Plugin for UiPlugin { ), ); + app.add_plugins((widget::PopoverPlugin, widget::ScrollbarPlugin)); + build_text_interop(app); } } diff --git a/crates/bevy_ui/src/widget/mod.rs b/crates/bevy_ui/src/widget/mod.rs index bbd319e986f09..b239a74789c72 100644 --- a/crates/bevy_ui/src/widget/mod.rs +++ b/crates/bevy_ui/src/widget/mod.rs @@ -3,11 +3,15 @@ mod button; mod image; mod label; +pub mod popover; +pub mod scrollbar; mod text; mod viewport; pub use button::*; pub use image::*; pub use label::*; +pub use popover::*; +pub use scrollbar::*; pub use text::*; pub use viewport::*; diff --git a/crates/bevy_ui_widgets/src/popover.rs b/crates/bevy_ui/src/widget/popover.rs similarity index 96% rename from crates/bevy_ui_widgets/src/popover.rs rename to crates/bevy_ui/src/widget/popover.rs index eb32d2d61f5ea..ee5391a5c8bd8 100644 --- a/crates/bevy_ui_widgets/src/popover.rs +++ b/crates/bevy_ui/src/widget/popover.rs @@ -1,5 +1,9 @@ //! Framework for positioning of popups, tooltips, and other popover UI elements. +use crate::{ + ui_stack_system, ComputedNode, ComputedUiRenderTargetInfo, Node, PositionType, + UiGlobalTransform, UiSystems, Val, +}; use bevy_app::{App, Plugin, PostUpdate}; use bevy_camera::visibility::Visibility; use bevy_ecs::{ @@ -7,9 +11,6 @@ use bevy_ecs::{ schedule::IntoScheduleConfigs, system::Query, }; use bevy_math::{Rect, Vec2}; -use bevy_ui::{ - ComputedNode, ComputedUiRenderTargetInfo, Node, PositionType, UiGlobalTransform, UiSystems, Val, -}; /// Which side of the parent element the popover element should be placed. #[derive(Debug, Default, Clone, Copy, PartialEq)] @@ -91,7 +92,8 @@ impl Clone for Popover { } } -fn position_popover( +/// Clips or positions popovers +pub fn position_popover( mut q_popover: Query<( &mut Node, &mut Visibility, @@ -242,7 +244,12 @@ pub struct PopoverPlugin; impl Plugin for PopoverPlugin { fn build(&self, app: &mut App) { - app.add_systems(PostUpdate, position_popover.in_set(UiSystems::Prepare)); + app.add_systems( + PostUpdate, + position_popover + .ambiguous_with(ui_stack_system) + .in_set(UiSystems::Prepare), + ); } } diff --git a/crates/bevy_ui_widgets/src/scrollbar.rs b/crates/bevy_ui/src/widget/scrollbar.rs similarity index 96% rename from crates/bevy_ui_widgets/src/scrollbar.rs rename to crates/bevy_ui/src/widget/scrollbar.rs index 6c3087d7576b5..4a9708d2aab7f 100644 --- a/crates/bevy_ui_widgets/src/scrollbar.rs +++ b/crates/bevy_ui/src/widget/scrollbar.rs @@ -6,13 +6,16 @@ use bevy_ecs::{ observer::On, query::{With, Without}, reflect::ReflectComponent, + schedule::IntoScheduleConfigs, system::{Query, Res}, }; use bevy_math::Vec2; use bevy_picking::events::{Cancel, Drag, DragEnd, DragStart, Pointer, Press}; use bevy_reflect::{prelude::ReflectDefault, Reflect}; -use bevy_ui::{ - ComputedNode, ComputedUiRenderTargetInfo, Node, ScrollPosition, UiGlobalTransform, UiScale, Val, + +use crate::{ + ui_layout_system, ui_stack_system, widget::popover, ComputedNode, ComputedUiRenderTargetInfo, + Node, ScrollPosition, UiGlobalTransform, UiScale, UiSystems, Val, }; /// Used to select the orientation of a scrollbar, slider, or other oriented control. @@ -243,7 +246,7 @@ fn scrollbar_on_drag_cancel( } } -fn update_scrollbar_thumb( +pub fn update_scrollbar_thumb( q_scroll_area: Query<(&ScrollPosition, &ComputedNode)>, q_scrollbar: Query<(&Scrollbar, &ComputedNode, &Children)>, mut q_thumb: Query<&mut Node, With>, @@ -363,6 +366,13 @@ impl Plugin for ScrollbarPlugin { .add_observer(scrollbar_on_drag_end) .add_observer(scrollbar_on_drag_cancel) .add_observer(scrollbar_on_drag) - .add_systems(PostUpdate, update_scrollbar_thumb); + .add_systems( + PostUpdate, + update_scrollbar_thumb + .ambiguous_with(popover::position_popover) + .ambiguous_with(ui_stack_system) + .ambiguous_with(ui_layout_system) + .in_set(UiSystems::Prepare), + ); } } diff --git a/crates/bevy_ui_widgets/src/lib.rs b/crates/bevy_ui_widgets/src/lib.rs index 6ff9c69db9ee4..9b497c55591f2 100644 --- a/crates/bevy_ui_widgets/src/lib.rs +++ b/crates/bevy_ui_widgets/src/lib.rs @@ -22,9 +22,7 @@ mod button; mod checkbox; mod menu; mod observe; -pub mod popover; mod radio; -mod scrollbar; mod slider; pub use button::*; @@ -32,14 +30,11 @@ pub use checkbox::*; pub use menu::*; pub use observe::*; pub use radio::*; -pub use scrollbar::*; pub use slider::*; use bevy_app::{PluginGroup, PluginGroupBuilder}; use bevy_ecs::{entity::Entity, event::EntityEvent}; -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. #[derive(Default)] @@ -48,12 +43,10 @@ pub struct UiWidgetsPlugins; impl PluginGroup for UiWidgetsPlugins { fn build(self) -> PluginGroupBuilder { PluginGroupBuilder::start::() - .add(PopoverPlugin) .add(ButtonPlugin) .add(CheckboxPlugin) .add(MenuPlugin) .add(RadioGroupPlugin) - .add(ScrollbarPlugin) .add(SliderPlugin) } } diff --git a/examples/ui/navigation/directional_navigation.rs b/examples/ui/navigation/directional_navigation.rs index 5b6a96fd7b528..1433829745643 100644 --- a/examples/ui/navigation/directional_navigation.rs +++ b/examples/ui/navigation/directional_navigation.rs @@ -20,7 +20,7 @@ use bevy::{ camera::NormalizedRenderTarget, input_focus::{ directional_navigation::{AutoNavigationConfig, DirectionalNavigationPlugin}, - InputDispatchPlugin, InputFocus, InputFocusVisible, + InputFocus, InputFocusVisible, }, math::{CompassOctant, Dir2, Rot2}, picking::{ @@ -35,11 +35,7 @@ use bevy::{ fn main() { App::new() // Input focus is not enabled by default, so we need to add the corresponding plugins - .add_plugins(( - DefaultPlugins, - InputDispatchPlugin, - DirectionalNavigationPlugin, - )) + .add_plugins((DefaultPlugins, DirectionalNavigationPlugin)) // This resource is canonically used to track whether or not to render a focus indicator // It starts as false, but we set it to true here as we would like to see the focus indicator .insert_resource(InputFocusVisible(true)) diff --git a/examples/ui/navigation/directional_navigation_overrides.rs b/examples/ui/navigation/directional_navigation_overrides.rs index 75a7f601ec4ca..ed5ac605dbc57 100644 --- a/examples/ui/navigation/directional_navigation_overrides.rs +++ b/examples/ui/navigation/directional_navigation_overrides.rs @@ -25,7 +25,7 @@ use bevy::{ directional_navigation::{ AutoNavigationConfig, DirectionalNavigationMap, DirectionalNavigationPlugin, }, - InputDispatchPlugin, InputFocus, InputFocusVisible, + InputFocus, InputFocusVisible, }, math::{CompassOctant, Dir2}, picking::{ @@ -41,11 +41,7 @@ fn main() { App::new() // Input focus is not enabled by default, so we need to add the corresponding plugins // The navigation system's resources are initialized by the DirectionalNavigationPlugin. - .add_plugins(( - DefaultPlugins, - InputDispatchPlugin, - DirectionalNavigationPlugin, - )) + .add_plugins((DefaultPlugins, DirectionalNavigationPlugin)) // This resource is canonically used to track whether or not to render a focus indicator // It starts as false, but we set it to true here as we would like to see the focus indicator .insert_resource(InputFocusVisible(true)) diff --git a/examples/ui/scroll_and_overflow/scrollbars.rs b/examples/ui/scroll_and_overflow/scrollbars.rs index a3b1a754c32a3..bea0d28599785 100644 --- a/examples/ui/scroll_and_overflow/scrollbars.rs +++ b/examples/ui/scroll_and_overflow/scrollbars.rs @@ -2,25 +2,17 @@ use bevy::{ ecs::{relationship::RelatedSpawner, spawn::SpawnWith}, - input_focus::{ - tab_navigation::{TabGroup, TabNavigationPlugin}, - InputDispatchPlugin, - }, + input_focus::tab_navigation::{TabGroup, TabNavigationPlugin}, picking::hover::Hovered, prelude::*, - ui_widgets::{ - ControlOrientation, CoreScrollbarDragState, CoreScrollbarThumb, Scrollbar, ScrollbarPlugin, + ui::widget::scrollbar::{ + ControlOrientation, CoreScrollbarDragState, CoreScrollbarThumb, Scrollbar, }, }; fn main() { App::new() - .add_plugins(( - DefaultPlugins, - ScrollbarPlugin, - InputDispatchPlugin, - TabNavigationPlugin, - )) + .add_plugins((DefaultPlugins, TabNavigationPlugin)) .insert_resource(UiScale(1.25)) .add_systems(Startup, setup_view_root) .add_systems(Update, update_scrollbar_thumb) diff --git a/examples/ui/widgets/standard_widgets.rs b/examples/ui/widgets/standard_widgets.rs index 5d58fd384e0f4..c9c297812faf6 100644 --- a/examples/ui/widgets/standard_widgets.rs +++ b/examples/ui/widgets/standard_widgets.rs @@ -26,10 +26,7 @@ use bevy::{ fn main() { App::new() - .add_plugins(( - DefaultPlugins, - TabNavigationPlugin, - )) + .add_plugins((DefaultPlugins, TabNavigationPlugin)) .insert_resource(DemoWidgetStates { slider_value: 50.0, slider_click: TrackClick::Snap, diff --git a/examples/ui/widgets/standard_widgets_observers.rs b/examples/ui/widgets/standard_widgets_observers.rs index 585c9f11778a4..56eb42b9336a4 100644 --- a/examples/ui/widgets/standard_widgets_observers.rs +++ b/examples/ui/widgets/standard_widgets_observers.rs @@ -19,10 +19,7 @@ use bevy::{ fn main() { App::new() - .add_plugins(( - DefaultPlugins, - TabNavigationPlugin, - )) + .add_plugins((DefaultPlugins, TabNavigationPlugin)) .insert_resource(DemoWidgetStates { slider_value: 50.0 }) .add_systems(Startup, setup) .add_observer(button_on_interaction::) diff --git a/examples/ui/widgets/tab_navigation.rs b/examples/ui/widgets/tab_navigation.rs index 2755d0fdf1a0b..29897630ac6fb 100644 --- a/examples/ui/widgets/tab_navigation.rs +++ b/examples/ui/widgets/tab_navigation.rs @@ -4,14 +4,14 @@ use bevy::{ color::palettes::basic::*, input_focus::{ tab_navigation::{TabGroup, TabIndex, TabNavigationPlugin}, - InputDispatchPlugin, InputFocus, + InputFocus, }, prelude::*, }; fn main() { App::new() - .add_plugins((DefaultPlugins, InputDispatchPlugin, TabNavigationPlugin)) + .add_plugins((DefaultPlugins, TabNavigationPlugin)) .add_systems(Startup, setup) .add_systems(Update, (button_system, focus_system)) .run(); diff --git a/examples/ui/widgets/vertical_slider.rs b/examples/ui/widgets/vertical_slider.rs index b580c4083496e..1d1dfab354e2b 100644 --- a/examples/ui/widgets/vertical_slider.rs +++ b/examples/ui/widgets/vertical_slider.rs @@ -15,10 +15,7 @@ const SLIDER_THUMB: Color = Color::srgb(0.35, 0.75, 0.35); fn main() { App::new() - .add_plugins(( - DefaultPlugins, - TabNavigationPlugin, - )) + .add_plugins((DefaultPlugins, TabNavigationPlugin)) .add_systems(Startup, setup) .add_systems(Update, (update_slider_visuals, update_value_labels)) .run(); From d310bdff700e15c1f8ceefc529dedb270d53e3bc Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Sun, 15 Mar 2026 15:51:23 -0700 Subject: [PATCH 05/46] Ambiguate the execution orders of systems in `InputDispatchPlugin` --- crates/bevy_input_focus/src/lib.rs | 38 +++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/crates/bevy_input_focus/src/lib.rs b/crates/bevy_input_focus/src/lib.rs index a82a0bc475203..5c93d2cb852a8 100644 --- a/crates/bevy_input_focus/src/lib.rs +++ b/crates/bevy_input_focus/src/lib.rs @@ -233,11 +233,43 @@ impl Plugin for InputDispatchPlugin { PreUpdate, ( #[cfg(feature = "keyboard")] - dispatch_focused_input::, + { + let s = dispatch_focused_input::; + + #[cfg(feature = "gamepad")] + let s = s + .ambiguous_with(dispatch_focused_input::) + .after(bevy_input::gamepad::gamepad_event_processing_system); + + #[cfg(feature = "mouse")] + let s = s.ambiguous_with(dispatch_focused_input::); + s + }, #[cfg(feature = "gamepad")] - dispatch_focused_input::, + { + let s = dispatch_focused_input:: + .after(bevy_input::gamepad::gamepad_event_processing_system); + + #[cfg(feature = "keyboard")] + let s = s.ambiguous_with(dispatch_focused_input::); + + #[cfg(feature = "mouse")] + let s = s.ambiguous_with(dispatch_focused_input::); + s + }, #[cfg(feature = "mouse")] - dispatch_focused_input::, + { + let s = dispatch_focused_input::; + + #[cfg(feature = "keyboard")] + let s = s.ambiguous_with(dispatch_focused_input::); + + #[cfg(feature = "gamepad")] + let s = s + .ambiguous_with(dispatch_focused_input::) + .after(bevy_input::gamepad::gamepad_event_processing_system); + s + }, ) .in_set(InputFocusSystems::Dispatch), ); From 79940d74af54be7c8c70f40b79a462c5601030d3 Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Mon, 16 Mar 2026 14:11:08 -0700 Subject: [PATCH 06/46] Wrap the execution orders in system sets --- crates/bevy_ui/src/lib.rs | 2 -- crates/bevy_ui/src/widget/mod.rs | 4 ---- crates/bevy_ui_widgets/Cargo.toml | 1 + crates/bevy_ui_widgets/src/lib.rs | 7 +++++++ .../widget => bevy_ui_widgets/src}/popover.rs | 15 +++++++++------ .../widget => bevy_ui_widgets/src}/scrollbar.rs | 17 +++++++++-------- examples/ui/scroll_and_overflow/scrollbars.rs | 4 +--- 7 files changed, 27 insertions(+), 23 deletions(-) rename crates/{bevy_ui/src/widget => bevy_ui_widgets/src}/popover.rs (96%) rename crates/{bevy_ui/src/widget => bevy_ui_widgets/src}/scrollbar.rs (97%) diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 79363ee4d7e14..d2e1e46589baf 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -216,8 +216,6 @@ impl Plugin for UiPlugin { ), ); - app.add_plugins((widget::PopoverPlugin, widget::ScrollbarPlugin)); - build_text_interop(app); } } diff --git a/crates/bevy_ui/src/widget/mod.rs b/crates/bevy_ui/src/widget/mod.rs index b239a74789c72..bbd319e986f09 100644 --- a/crates/bevy_ui/src/widget/mod.rs +++ b/crates/bevy_ui/src/widget/mod.rs @@ -3,15 +3,11 @@ mod button; mod image; mod label; -pub mod popover; -pub mod scrollbar; mod text; mod viewport; pub use button::*; pub use image::*; pub use label::*; -pub use popover::*; -pub use scrollbar::*; pub use text::*; pub use viewport::*; diff --git a/crates/bevy_ui_widgets/Cargo.toml b/crates/bevy_ui_widgets/Cargo.toml index 136019c6693cf..d369b6351232c 100644 --- a/crates/bevy_ui_widgets/Cargo.toml +++ b/crates/bevy_ui_widgets/Cargo.toml @@ -21,6 +21,7 @@ bevy_math = { path = "../bevy_math", version = "0.19.0-dev" } bevy_picking = { path = "../bevy_picking", version = "0.19.0-dev" } bevy_reflect = { path = "../bevy_reflect", version = "0.19.0-dev" } bevy_ui = { path = "../bevy_ui", version = "0.19.0-dev" } +bevy_render = { path = "../bevy_render", version = "0.19.0-dev" } # other accesskit = "0.24" diff --git a/crates/bevy_ui_widgets/src/lib.rs b/crates/bevy_ui_widgets/src/lib.rs index 9b497c55591f2..42a359f2fdbd0 100644 --- a/crates/bevy_ui_widgets/src/lib.rs +++ b/crates/bevy_ui_widgets/src/lib.rs @@ -22,7 +22,9 @@ mod button; mod checkbox; mod menu; mod observe; +pub mod popover; mod radio; +mod scrollbar; mod slider; pub use button::*; @@ -30,11 +32,14 @@ pub use checkbox::*; pub use menu::*; pub use observe::*; pub use radio::*; +pub use scrollbar::*; pub use slider::*; use bevy_app::{PluginGroup, PluginGroupBuilder}; use bevy_ecs::{entity::Entity, event::EntityEvent}; +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. #[derive(Default)] @@ -43,11 +48,13 @@ pub struct UiWidgetsPlugins; impl PluginGroup for UiWidgetsPlugins { fn build(self) -> PluginGroupBuilder { PluginGroupBuilder::start::() + .add(PopoverPlugin) .add(ButtonPlugin) .add(CheckboxPlugin) .add(MenuPlugin) .add(RadioGroupPlugin) .add(SliderPlugin) + .add(ScrollbarPlugin) } } diff --git a/crates/bevy_ui/src/widget/popover.rs b/crates/bevy_ui_widgets/src/popover.rs similarity index 96% rename from crates/bevy_ui/src/widget/popover.rs rename to crates/bevy_ui_widgets/src/popover.rs index ee5391a5c8bd8..a7c84c8fe773e 100644 --- a/crates/bevy_ui/src/widget/popover.rs +++ b/crates/bevy_ui_widgets/src/popover.rs @@ -1,9 +1,5 @@ //! Framework for positioning of popups, tooltips, and other popover UI elements. -use crate::{ - ui_stack_system, ComputedNode, ComputedUiRenderTargetInfo, Node, PositionType, - UiGlobalTransform, UiSystems, Val, -}; use bevy_app::{App, Plugin, PostUpdate}; use bevy_camera::visibility::Visibility; use bevy_ecs::{ @@ -11,6 +7,10 @@ use bevy_ecs::{ schedule::IntoScheduleConfigs, system::Query, }; use bevy_math::{Rect, Vec2}; +use bevy_render::RenderSystems; +use bevy_ui::{ + ComputedNode, ComputedUiRenderTargetInfo, Node, PositionType, UiGlobalTransform, UiSystems, Val, +}; /// Which side of the parent element the popover element should be placed. #[derive(Debug, Default, Clone, Copy, PartialEq)] @@ -247,8 +247,11 @@ impl Plugin for PopoverPlugin { app.add_systems( PostUpdate, position_popover - .ambiguous_with(ui_stack_system) - .in_set(UiSystems::Prepare), + .in_set(UiSystems::Prepare) + .ambiguous_with(UiSystems::Prepare) + .ambiguous_with(UiSystems::Stack) + .before(RenderSystems::Render) + .before(UiSystems::Layout), ); } } diff --git a/crates/bevy_ui/src/widget/scrollbar.rs b/crates/bevy_ui_widgets/src/scrollbar.rs similarity index 97% rename from crates/bevy_ui/src/widget/scrollbar.rs rename to crates/bevy_ui_widgets/src/scrollbar.rs index 4a9708d2aab7f..b3c6bd1fd96c7 100644 --- a/crates/bevy_ui/src/widget/scrollbar.rs +++ b/crates/bevy_ui_widgets/src/scrollbar.rs @@ -12,10 +12,10 @@ use bevy_ecs::{ use bevy_math::Vec2; use bevy_picking::events::{Cancel, Drag, DragEnd, DragStart, Pointer, Press}; use bevy_reflect::{prelude::ReflectDefault, Reflect}; - -use crate::{ - ui_layout_system, ui_stack_system, widget::popover, ComputedNode, ComputedUiRenderTargetInfo, - Node, ScrollPosition, UiGlobalTransform, UiScale, UiSystems, Val, +use bevy_render::RenderSystems; +use bevy_ui::{ + ComputedNode, ComputedUiRenderTargetInfo, Node, ScrollPosition, UiGlobalTransform, UiScale, + UiSystems, Val, }; /// Used to select the orientation of a scrollbar, slider, or other oriented control. @@ -369,10 +369,11 @@ impl Plugin for ScrollbarPlugin { .add_systems( PostUpdate, update_scrollbar_thumb - .ambiguous_with(popover::position_popover) - .ambiguous_with(ui_stack_system) - .ambiguous_with(ui_layout_system) - .in_set(UiSystems::Prepare), + .in_set(UiSystems::Prepare) + .ambiguous_with(UiSystems::Prepare) + .ambiguous_with(UiSystems::Stack) + .before(RenderSystems::Render) + .before(UiSystems::Layout), ); } } diff --git a/examples/ui/scroll_and_overflow/scrollbars.rs b/examples/ui/scroll_and_overflow/scrollbars.rs index bea0d28599785..b25ed998508d2 100644 --- a/examples/ui/scroll_and_overflow/scrollbars.rs +++ b/examples/ui/scroll_and_overflow/scrollbars.rs @@ -5,9 +5,7 @@ use bevy::{ input_focus::tab_navigation::{TabGroup, TabNavigationPlugin}, picking::hover::Hovered, prelude::*, - ui::widget::scrollbar::{ - ControlOrientation, CoreScrollbarDragState, CoreScrollbarThumb, Scrollbar, - }, + ui_widgets::{ControlOrientation, CoreScrollbarDragState, CoreScrollbarThumb, Scrollbar}, }; fn main() { From 8ec74b4c388f7bd9ac150e6478a11e271ef5f389 Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Mon, 16 Mar 2026 14:22:21 -0700 Subject: [PATCH 07/46] Wrap the execution orders in system sets --- crates/bevy_input_focus/src/lib.rs | 43 +++++------------------------- 1 file changed, 7 insertions(+), 36 deletions(-) diff --git a/crates/bevy_input_focus/src/lib.rs b/crates/bevy_input_focus/src/lib.rs index 5c93d2cb852a8..b5cfdf35389e0 100644 --- a/crates/bevy_input_focus/src/lib.rs +++ b/crates/bevy_input_focus/src/lib.rs @@ -43,6 +43,7 @@ use bevy_input::gamepad::GamepadButtonChangedEvent; use bevy_input::keyboard::KeyboardInput; #[cfg(feature = "mouse")] use bevy_input::mouse::MouseWheel; +use bevy_input::InputSystems; use bevy_window::{PrimaryWindow, Window}; use core::fmt::Debug; @@ -233,45 +234,15 @@ impl Plugin for InputDispatchPlugin { PreUpdate, ( #[cfg(feature = "keyboard")] - { - let s = dispatch_focused_input::; - - #[cfg(feature = "gamepad")] - let s = s - .ambiguous_with(dispatch_focused_input::) - .after(bevy_input::gamepad::gamepad_event_processing_system); - - #[cfg(feature = "mouse")] - let s = s.ambiguous_with(dispatch_focused_input::); - s - }, + dispatch_focused_input::, #[cfg(feature = "gamepad")] - { - let s = dispatch_focused_input:: - .after(bevy_input::gamepad::gamepad_event_processing_system); - - #[cfg(feature = "keyboard")] - let s = s.ambiguous_with(dispatch_focused_input::); - - #[cfg(feature = "mouse")] - let s = s.ambiguous_with(dispatch_focused_input::); - s - }, + dispatch_focused_input::, #[cfg(feature = "mouse")] - { - let s = dispatch_focused_input::; - - #[cfg(feature = "keyboard")] - let s = s.ambiguous_with(dispatch_focused_input::); - - #[cfg(feature = "gamepad")] - let s = s - .ambiguous_with(dispatch_focused_input::) - .after(bevy_input::gamepad::gamepad_event_processing_system); - s - }, + dispatch_focused_input::, ) - .in_set(InputFocusSystems::Dispatch), + .in_set(InputFocusSystems::Dispatch) + .ambiguous_with(InputFocusSystems::Dispatch) + .after(InputSystems), ); } } From fb25d065617d8c29b04807bf78f25ef254583333 Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Mon, 16 Mar 2026 14:35:49 -0700 Subject: [PATCH 08/46] Stub in migration guide --- ...ns_and_input_dispatch_plugin_are_now_default.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md diff --git a/release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md b/release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md new file mode 100644 index 0000000000000..c15a30710e1f7 --- /dev/null +++ b/release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md @@ -0,0 +1,14 @@ +--- +title: Feature that broke +pull_requests: [14791, 15458, 15269] +--- + +Copy the contents of this file into a new file in `./migration-guides`, update the metadata, and add migration guide content here. + +Remember, your aim is to communicate: + +- What has changed since the last release? +- Why did we make this breaking change? +- How can users migrate their existing code? + +For more specifics about style and content, see the [instructions](./migration_guides.md). From f7fad09ed1126d34c8c284bce85c99821fed1d51 Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Mon, 16 Mar 2026 14:38:30 -0700 Subject: [PATCH 09/46] Regress update_scrollbar_thumb and position_popover to private --- crates/bevy_ui_widgets/src/popover.rs | 3 +-- crates/bevy_ui_widgets/src/scrollbar.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ui_widgets/src/popover.rs b/crates/bevy_ui_widgets/src/popover.rs index a7c84c8fe773e..aedff98a46c73 100644 --- a/crates/bevy_ui_widgets/src/popover.rs +++ b/crates/bevy_ui_widgets/src/popover.rs @@ -92,8 +92,7 @@ impl Clone for Popover { } } -/// Clips or positions popovers -pub fn position_popover( +fn position_popover( mut q_popover: Query<( &mut Node, &mut Visibility, diff --git a/crates/bevy_ui_widgets/src/scrollbar.rs b/crates/bevy_ui_widgets/src/scrollbar.rs index b3c6bd1fd96c7..069d9f41402fd 100644 --- a/crates/bevy_ui_widgets/src/scrollbar.rs +++ b/crates/bevy_ui_widgets/src/scrollbar.rs @@ -246,7 +246,7 @@ fn scrollbar_on_drag_cancel( } } -pub fn update_scrollbar_thumb( +fn update_scrollbar_thumb( q_scroll_area: Query<(&ScrollPosition, &ComputedNode)>, q_scrollbar: Query<(&Scrollbar, &ComputedNode, &Children)>, mut q_thumb: Query<&mut Node, With>, From 12bb5f75c3bac2c0c0e28110d2fa87d0c3b6b11f Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Mon, 16 Mar 2026 16:21:24 -0700 Subject: [PATCH 10/46] Update the migration guide --- ...d_input_dispatch_plugin_are_now_default.md | 49 ++++++++++++++++--- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md b/release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md index c15a30710e1f7..e746ea8286793 100644 --- a/release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md +++ b/release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md @@ -1,14 +1,47 @@ --- -title: Feature that broke -pull_requests: [14791, 15458, 15269] +title: "Include `UiWidgetsPlugins` in `DefaultPlugins`" +pull_requests: [23346] --- -Copy the contents of this file into a new file in `./migration-guides`, update the metadata, and add migration guide content here. +TODO: What has changed since the last release? -Remember, your aim is to communicate: +TODO: Why did we make this breaking change? -- What has changed since the last release? -- Why did we make this breaking change? -- How can users migrate their existing code? +Remove `UiWidgetsPlugins` if you have `DefaultPlugins` -For more specifics about style and content, see the [instructions](./migration_guides.md). +```rs +// 0.18 +fn main() { + App::new() + .add_plugins(DefaultPlugins, UiWidgetsPlugins) + .add_plugins((my_ambitious_game::game_plugin)) + .run(); +} + +// 0.19 +fn main() { + App::new() + .add_plugins(DefaultPlugins) // Puff! + .add_plugins((my_ambitious_game::game_plugin)) + .run(); +} +``` + +Remove `InputDispatchPlugin` if you have `DefaultPlugins` +```rs +// 0.18 +fn main() { + App::new() + .add_plugins(DefaultPlugins, UiWidgetsPlugins, InputDispatchPlugin) + .add_plugins((my_sequel_game::game_plugin)) + .run(); +} + +// 0.19 +fn main() { + App::new() + .add_plugins(DefaultPlugins) // Puff! + .add_plugins((my_sequel_game::game_plugin)) + .run(); +} +``` \ No newline at end of file From 36ddbad30399b2784296e7f69c12b94f41190afc Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Mon, 16 Mar 2026 16:41:23 -0700 Subject: [PATCH 11/46] Resolve MD031/blanks-around-fences --- ..._widgets_plugins_and_input_dispatch_plugin_are_now_default.md | 1 + 1 file changed, 1 insertion(+) diff --git a/release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md b/release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md index e746ea8286793..07dde369b184b 100644 --- a/release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md +++ b/release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md @@ -28,6 +28,7 @@ fn main() { ``` Remove `InputDispatchPlugin` if you have `DefaultPlugins` + ```rs // 0.18 fn main() { From 9c21ed74394705cf79fe639870c0e1bba825c0e1 Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Mon, 16 Mar 2026 16:51:31 -0700 Subject: [PATCH 12/46] Update the migration guide --- ...d_input_dispatch_plugin_are_now_default.md | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md b/release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md index 07dde369b184b..8e5f3e83d1507 100644 --- a/release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md +++ b/release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md @@ -3,7 +3,7 @@ title: "Include `UiWidgetsPlugins` in `DefaultPlugins`" pull_requests: [23346] --- -TODO: What has changed since the last release? +`UiWidgetsPlugins` and `InputDispatchPlugin` are now part of `DefaultPlugins`. TODO: Why did we make this breaking change? @@ -45,4 +45,44 @@ fn main() { .add_plugins((my_sequel_game::game_plugin)) .run(); } -``` \ No newline at end of file +``` + +Remove `PopoverPlugin` if you have `DefaultPlugins` + +```rs +// 0.18 +fn main() { + App::new() + .add_plugins(DefaultPlugins, PopoverPlugin) + .add_plugins((my_threequel_game::game_plugin)) + .run(); +} + +// 0.19 +fn main() { + App::new() + .add_plugins(DefaultPlugins) // Puff! + .add_plugins((my_threequel_game::game_plugin)) + .run(); +} +``` + +Remove `ScrollbarPlugin` if you have `DefaultPlugins` + +```rs +// 0.18 +fn main() { + App::new() + .add_plugins(DefaultPlugins, ScrollbarPlugin) + .add_plugins((my_fourquel_game::game_plugin)) + .run(); +} + +// 0.19 +fn main() { + App::new() + .add_plugins(DefaultPlugins) // Puff! + .add_plugins((my_fourquel_game::game_plugin)) + .run(); +} +``` From d36148ea083fe9e3888c0f422219cf417720c016 Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Tue, 17 Mar 2026 20:16:07 +0000 Subject: [PATCH 13/46] Update release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md Co-authored-by: Kevin Chen --- ...widgets_plugins_and_input_dispatch_plugin_are_now_default.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md b/release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md index 8e5f3e83d1507..c7daafdf202c6 100644 --- a/release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md +++ b/release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md @@ -1,5 +1,5 @@ --- -title: "Include `UiWidgetsPlugins` in `DefaultPlugins`" +title: "`UiWidgetsPlugins` and `InputDispatchPlugin` are now in `DefaultPlugins`" pull_requests: [23346] --- From 69fa98e9200fd9e68ff5f6261251bcc4ccbb7f07 Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Sat, 28 Mar 2026 16:23:01 -0700 Subject: [PATCH 14/46] Add `UiSystems::ComputeRelative` --- crates/bevy_gizmos_render/Cargo.toml | 1 + .../src/transform_gizmo_render.rs | 5 ++++- crates/bevy_ui/src/lib.rs | 8 ++++++-- crates/bevy_ui_widgets/Cargo.toml | 1 - crates/bevy_ui_widgets/src/popover.rs | 18 ++++++++++-------- crates/bevy_ui_widgets/src/scrollbar.rs | 14 +++++++------- 6 files changed, 28 insertions(+), 19 deletions(-) diff --git a/crates/bevy_gizmos_render/Cargo.toml b/crates/bevy_gizmos_render/Cargo.toml index 41d9197e1880d..ba62810a3798c 100644 --- a/crates/bevy_gizmos_render/Cargo.toml +++ b/crates/bevy_gizmos_render/Cargo.toml @@ -33,6 +33,7 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.19.0-dev" } bevy_color = { path = "../bevy_color", version = "0.19.0-dev" } bevy_material = { path = "../bevy_material", version = "0.19.0-dev" } bevy_log = { path = "../bevy_log", version = "0.19.0-dev" } +bevy_ui = { path = "../bevy_ui", version = "0.19.0-dev" } # other bytemuck = "1.0" diff --git a/crates/bevy_gizmos_render/src/transform_gizmo_render.rs b/crates/bevy_gizmos_render/src/transform_gizmo_render.rs index ed4dca2610684..6fb5ece849494 100644 --- a/crates/bevy_gizmos_render/src/transform_gizmo_render.rs +++ b/crates/bevy_gizmos_render/src/transform_gizmo_render.rs @@ -34,6 +34,8 @@ use bevy_gizmos::transform_gizmo::{ VIEW_CIRCLE_MAJOR, VIEW_CIRCLE_MINOR, VIEW_RING_MAJOR, VIEW_RING_MINOR, }; +use bevy_ui::UiSystems; + /// The render layer used exclusively for gizmo meshes. const GIZMO_RENDER_LAYER: usize = 15; @@ -88,7 +90,8 @@ impl Plugin for TransformGizmoRenderPlugin { PostUpdate, update_gizmo_meshes .after(bevy_transform::TransformSystems::Propagate) - .after(bevy_camera::visibility::VisibilitySystems::VisibilityPropagate), + .after(bevy_camera::visibility::VisibilitySystems::VisibilityPropagate) + .ambiguous_with(bevy_ui::UiSystems::ComputeRelative), ); } } diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index d5c6f2c710c19..17342ea7b2578 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -74,7 +74,7 @@ pub mod prelude { } use bevy_app::{prelude::*, AnimationSystems, HierarchyPropagatePlugin, PropagateSet}; -use bevy_camera::CameraUpdateSystems; +use bevy_camera::{visibility::VisibilitySystems, CameraUpdateSystems}; use bevy_ecs::prelude::*; use bevy_input::InputSystems; use bevy_transform::TransformSystems; @@ -112,6 +112,8 @@ pub enum UiSystems { /// /// Runs in [`PostUpdate`]. Stack, + /// After this label, TODO + ComputeRelative, } /// The current scale of the UI. @@ -150,6 +152,7 @@ impl Plugin for UiPlugin { UiSystems::Content, UiSystems::Layout, UiSystems::PostLayout, + UiSystems::ComputeRelative.after(VisibilitySystems::VisibilityPropagate), ) .chain(), ) @@ -216,7 +219,8 @@ impl Plugin for UiPlugin { .in_set(UiSystems::PostLayout) .in_set(AmbiguousWithText) .in_set(AmbiguousWithUpdateText2dLayout), - ), + ) + .before(UiSystems::ComputeRelative), ); build_text_interop(app); diff --git a/crates/bevy_ui_widgets/Cargo.toml b/crates/bevy_ui_widgets/Cargo.toml index c194d1d090727..0566386299841 100644 --- a/crates/bevy_ui_widgets/Cargo.toml +++ b/crates/bevy_ui_widgets/Cargo.toml @@ -21,7 +21,6 @@ bevy_math = { path = "../bevy_math", version = "0.19.0-dev" } bevy_picking = { path = "../bevy_picking", version = "0.19.0-dev" } bevy_reflect = { path = "../bevy_reflect", version = "0.19.0-dev" } bevy_ui = { path = "../bevy_ui", version = "0.19.0-dev" } -bevy_render = { path = "../bevy_render", version = "0.19.0-dev" } bevy_text = { path = "../bevy_text", version = "0.19.0-dev" } # other diff --git a/crates/bevy_ui_widgets/src/popover.rs b/crates/bevy_ui_widgets/src/popover.rs index aedff98a46c73..ee9e7d3c52603 100644 --- a/crates/bevy_ui_widgets/src/popover.rs +++ b/crates/bevy_ui_widgets/src/popover.rs @@ -7,7 +7,6 @@ use bevy_ecs::{ schedule::IntoScheduleConfigs, system::Query, }; use bevy_math::{Rect, Vec2}; -use bevy_render::RenderSystems; use bevy_ui::{ ComputedNode, ComputedUiRenderTargetInfo, Node, PositionType, UiGlobalTransform, UiSystems, Val, }; @@ -243,14 +242,17 @@ pub struct PopoverPlugin; impl Plugin for PopoverPlugin { fn build(&self, app: &mut App) { - app.add_systems( + app.configure_sets( PostUpdate, - position_popover - .in_set(UiSystems::Prepare) - .ambiguous_with(UiSystems::Prepare) - .ambiguous_with(UiSystems::Stack) - .before(RenderSystems::Render) - .before(UiSystems::Layout), + ( + bevy_app::PropagateSet::::default(), + bevy_app::PropagateSet::::default(), + ) + .in_set(UiSystems::ComputeRelative), + ) + .add_systems( + PostUpdate, + position_popover.in_set(UiSystems::ComputeRelative), ); } } diff --git a/crates/bevy_ui_widgets/src/scrollbar.rs b/crates/bevy_ui_widgets/src/scrollbar.rs index 069d9f41402fd..8e3e70c2d57b5 100644 --- a/crates/bevy_ui_widgets/src/scrollbar.rs +++ b/crates/bevy_ui_widgets/src/scrollbar.rs @@ -12,7 +12,6 @@ use bevy_ecs::{ use bevy_math::Vec2; use bevy_picking::events::{Cancel, Drag, DragEnd, DragStart, Pointer, Press}; use bevy_reflect::{prelude::ReflectDefault, Reflect}; -use bevy_render::RenderSystems; use bevy_ui::{ ComputedNode, ComputedUiRenderTargetInfo, Node, ScrollPosition, UiGlobalTransform, UiScale, UiSystems, Val, @@ -51,7 +50,7 @@ pub enum ControlOrientation { /// The application is free to position the scrollbars relative to the scrolling container however /// it wants: it can overlay them on top of the scrolling content, or use a grid layout to displace /// the content to make room for the scrollbars. -#[derive(Component, Debug, Reflect)] +#[derive(Component, Debug, Reflect, Clone, PartialEq)] #[reflect(Component)] pub struct Scrollbar { /// Entity being scrolled. @@ -366,14 +365,15 @@ impl Plugin for ScrollbarPlugin { .add_observer(scrollbar_on_drag_end) .add_observer(scrollbar_on_drag_cancel) .add_observer(scrollbar_on_drag) + .configure_sets( + PostUpdate, + bevy_app::PropagateSet::::default().in_set(UiSystems::ComputeRelative), + ) .add_systems( PostUpdate, update_scrollbar_thumb - .in_set(UiSystems::Prepare) - .ambiguous_with(UiSystems::Prepare) - .ambiguous_with(UiSystems::Stack) - .before(RenderSystems::Render) - .before(UiSystems::Layout), + .in_set(UiSystems::ComputeRelative) + .ambiguous_with(UiSystems::ComputeRelative), ); } } From 25880c6867fd87be19459b77862a29100a7dae8d Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Sun, 29 Mar 2026 12:02:45 -0700 Subject: [PATCH 15/46] Remove unused import --- crates/bevy_gizmos_render/src/transform_gizmo_render.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/bevy_gizmos_render/src/transform_gizmo_render.rs b/crates/bevy_gizmos_render/src/transform_gizmo_render.rs index 6fb5ece849494..35fbdef636db2 100644 --- a/crates/bevy_gizmos_render/src/transform_gizmo_render.rs +++ b/crates/bevy_gizmos_render/src/transform_gizmo_render.rs @@ -34,8 +34,6 @@ use bevy_gizmos::transform_gizmo::{ VIEW_CIRCLE_MAJOR, VIEW_CIRCLE_MINOR, VIEW_RING_MAJOR, VIEW_RING_MINOR, }; -use bevy_ui::UiSystems; - /// The render layer used exclusively for gizmo meshes. const GIZMO_RENDER_LAYER: usize = 15; From 8e6a735d865e7616971a9838a83687b15051f512 Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Sun, 29 Mar 2026 12:36:37 -0700 Subject: [PATCH 16/46] Remove dead code --- crates/bevy_ui_widgets/src/popover.rs | 10 +--------- crates/bevy_ui_widgets/src/scrollbar.rs | 4 ---- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/crates/bevy_ui_widgets/src/popover.rs b/crates/bevy_ui_widgets/src/popover.rs index ee9e7d3c52603..42ff24b139ee0 100644 --- a/crates/bevy_ui_widgets/src/popover.rs +++ b/crates/bevy_ui_widgets/src/popover.rs @@ -242,15 +242,7 @@ pub struct PopoverPlugin; impl Plugin for PopoverPlugin { fn build(&self, app: &mut App) { - app.configure_sets( - PostUpdate, - ( - bevy_app::PropagateSet::::default(), - bevy_app::PropagateSet::::default(), - ) - .in_set(UiSystems::ComputeRelative), - ) - .add_systems( + app.add_systems( PostUpdate, position_popover.in_set(UiSystems::ComputeRelative), ); diff --git a/crates/bevy_ui_widgets/src/scrollbar.rs b/crates/bevy_ui_widgets/src/scrollbar.rs index 8e3e70c2d57b5..ff0c7d55c2b7c 100644 --- a/crates/bevy_ui_widgets/src/scrollbar.rs +++ b/crates/bevy_ui_widgets/src/scrollbar.rs @@ -365,10 +365,6 @@ impl Plugin for ScrollbarPlugin { .add_observer(scrollbar_on_drag_end) .add_observer(scrollbar_on_drag_cancel) .add_observer(scrollbar_on_drag) - .configure_sets( - PostUpdate, - bevy_app::PropagateSet::::default().in_set(UiSystems::ComputeRelative), - ) .add_systems( PostUpdate, update_scrollbar_thumb From 5d315cb03c5bbf4dcf399695e930f2977632b96f Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 31 Mar 2026 16:32:54 -0400 Subject: [PATCH 17/46] Fix TODO in migration guide --- ...widgets_plugins_and_input_dispatch_plugin_are_now_default.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md b/_release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md index c7daafdf202c6..a2b604f90026d 100644 --- a/_release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md +++ b/_release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md @@ -5,7 +5,7 @@ pull_requests: [23346] `UiWidgetsPlugins` and `InputDispatchPlugin` are now part of `DefaultPlugins`. -TODO: Why did we make this breaking change? +These plugins are now mature enough to be included as part of the default Bevy experience. Remove `UiWidgetsPlugins` if you have `DefaultPlugins` From ab08ea2c87f2165ae1c9b61fd216fe500e22f2ad Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 31 Mar 2026 16:38:15 -0400 Subject: [PATCH 18/46] Yeet unneeded migration guides for subplugins --- ...d_input_dispatch_plugin_are_now_default.md | 39 ------------------- 1 file changed, 39 deletions(-) diff --git a/_release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md b/_release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md index a2b604f90026d..e9c54e8599abc 100644 --- a/_release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md +++ b/_release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md @@ -47,42 +47,3 @@ fn main() { } ``` -Remove `PopoverPlugin` if you have `DefaultPlugins` - -```rs -// 0.18 -fn main() { - App::new() - .add_plugins(DefaultPlugins, PopoverPlugin) - .add_plugins((my_threequel_game::game_plugin)) - .run(); -} - -// 0.19 -fn main() { - App::new() - .add_plugins(DefaultPlugins) // Puff! - .add_plugins((my_threequel_game::game_plugin)) - .run(); -} -``` - -Remove `ScrollbarPlugin` if you have `DefaultPlugins` - -```rs -// 0.18 -fn main() { - App::new() - .add_plugins(DefaultPlugins, ScrollbarPlugin) - .add_plugins((my_fourquel_game::game_plugin)) - .run(); -} - -// 0.19 -fn main() { - App::new() - .add_plugins(DefaultPlugins) // Puff! - .add_plugins((my_fourquel_game::game_plugin)) - .run(); -} -``` From 568e28dfe1c975a2caad3fe7c52fc7f73e3d50d6 Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Tue, 31 Mar 2026 13:50:23 -0700 Subject: [PATCH 19/46] Focus on PostLayout --- crates/bevy_gizmos_render/Cargo.toml | 1 - .../src/transform_gizmo_render.rs | 3 +- crates/bevy_ui/src/accessibility.rs | 5 ++-- crates/bevy_ui/src/lib.rs | 29 ++++++++++--------- crates/bevy_ui_widgets/src/scrollbar.rs | 4 +-- 5 files changed, 20 insertions(+), 22 deletions(-) diff --git a/crates/bevy_gizmos_render/Cargo.toml b/crates/bevy_gizmos_render/Cargo.toml index ba62810a3798c..41d9197e1880d 100644 --- a/crates/bevy_gizmos_render/Cargo.toml +++ b/crates/bevy_gizmos_render/Cargo.toml @@ -33,7 +33,6 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.19.0-dev" } bevy_color = { path = "../bevy_color", version = "0.19.0-dev" } bevy_material = { path = "../bevy_material", version = "0.19.0-dev" } bevy_log = { path = "../bevy_log", version = "0.19.0-dev" } -bevy_ui = { path = "../bevy_ui", version = "0.19.0-dev" } # other bytemuck = "1.0" diff --git a/crates/bevy_gizmos_render/src/transform_gizmo_render.rs b/crates/bevy_gizmos_render/src/transform_gizmo_render.rs index 006b4c9a34bf2..142916938d802 100644 --- a/crates/bevy_gizmos_render/src/transform_gizmo_render.rs +++ b/crates/bevy_gizmos_render/src/transform_gizmo_render.rs @@ -102,8 +102,7 @@ impl Plugin for TransformGizmoRenderPlugin { ) .chain() .after(bevy_transform::TransformSystems::Propagate) - .after(bevy_camera::visibility::VisibilitySystems::VisibilityPropagate) - .ambiguous_with(bevy_ui::UiSystems::ComputeRelative), + .after(bevy_camera::visibility::VisibilitySystems::VisibilityPropagate), ); } } diff --git a/crates/bevy_ui/src/accessibility.rs b/crates/bevy_ui/src/accessibility.rs index 8ec9ae02b8ee3..5f24d3069f322 100644 --- a/crates/bevy_ui/src/accessibility.rs +++ b/crates/bevy_ui/src/accessibility.rs @@ -3,7 +3,7 @@ use crate::{ prelude::{Button, Label}, ui_transform::UiGlobalTransform, widget::{ImageNode, TextUiReader}, - ComputedNode, + ComputedNode, UiSystems, }; use bevy_a11y::AccessibilityNode; use bevy_app::{App, Plugin, PostUpdate}; @@ -153,7 +153,8 @@ impl Plugin for AccessibilityPlugin { .after(bevy_transform::TransformSystems::Propagate) .after(CameraUpdateSystems) // the listed systems do not affect calculated size - .ambiguous_with(crate::ui_stack_system), + .ambiguous_with(crate::ui_stack_system) + .before(UiSystems::PostLayout), button_changed, image_changed, label_changed, diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 17342ea7b2578..c4887f077b105 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -112,8 +112,6 @@ pub enum UiSystems { /// /// Runs in [`PostUpdate`]. Stack, - /// After this label, TODO - ComputeRelative, } /// The current scale of the UI. @@ -152,7 +150,6 @@ impl Plugin for UiPlugin { UiSystems::Content, UiSystems::Layout, UiSystems::PostLayout, - UiSystems::ComputeRelative.after(VisibilitySystems::VisibilityPropagate), ) .chain(), ) @@ -184,6 +181,7 @@ impl Plugin for UiPlugin { let ui_layout_system_config = ui_layout_system .in_set(UiSystems::Layout) + .before(UiSystems::PostLayout) .before(TransformSystems::Propagate); let ui_layout_system_config = ui_layout_system_config @@ -195,15 +193,19 @@ impl Plugin for UiPlugin { ( propagate_ui_target_cameras.in_set(UiSystems::Prepare), ui_layout_system_config, - ui_stack_system - .in_set(UiSystems::Stack) - // These systems don't care about stack index - .ambiguous_with(widget::measure_text_system) - .ambiguous_with(update_clipping_system) - .ambiguous_with(ui_layout_system) - .ambiguous_with(widget::update_viewport_render_target_size) - .in_set(AmbiguousWithText), - update_clipping_system.after(TransformSystems::Propagate), + ( + ui_stack_system + .in_set(UiSystems::Stack) + .after(UiSystems::Prepare) + // These systems don't care about stack index + .ambiguous_with(widget::measure_text_system) + .ambiguous_with(update_clipping_system) + .ambiguous_with(ui_layout_system) + .ambiguous_with(widget::update_viewport_render_target_size) + .in_set(AmbiguousWithText), + update_clipping_system.after(TransformSystems::Propagate), + ) + .before(UiSystems::PostLayout), // Potential conflicts: `Assets` // They run independently since `widget::image_node_system` will only ever observe // its own ImageNode, and `widget::text_system` & `bevy_text::update_text2d_layout` @@ -219,8 +221,7 @@ impl Plugin for UiPlugin { .in_set(UiSystems::PostLayout) .in_set(AmbiguousWithText) .in_set(AmbiguousWithUpdateText2dLayout), - ) - .before(UiSystems::ComputeRelative), + ), ); build_text_interop(app); diff --git a/crates/bevy_ui_widgets/src/scrollbar.rs b/crates/bevy_ui_widgets/src/scrollbar.rs index ff0c7d55c2b7c..5032ede0ef443 100644 --- a/crates/bevy_ui_widgets/src/scrollbar.rs +++ b/crates/bevy_ui_widgets/src/scrollbar.rs @@ -367,9 +367,7 @@ impl Plugin for ScrollbarPlugin { .add_observer(scrollbar_on_drag) .add_systems( PostUpdate, - update_scrollbar_thumb - .in_set(UiSystems::ComputeRelative) - .ambiguous_with(UiSystems::ComputeRelative), + update_scrollbar_thumb.in_set(UiSystems::Prepare), ); } } From de19e2df65c55f23a47809fd562de98ee1d6c9cf Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Tue, 31 Mar 2026 14:00:49 -0700 Subject: [PATCH 20/46] Remove unused import --- crates/bevy_ui/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index c4887f077b105..f388f7a226ef7 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -74,7 +74,7 @@ pub mod prelude { } use bevy_app::{prelude::*, AnimationSystems, HierarchyPropagatePlugin, PropagateSet}; -use bevy_camera::{visibility::VisibilitySystems, CameraUpdateSystems}; +use bevy_camera::CameraUpdateSystems; use bevy_ecs::prelude::*; use bevy_input::InputSystems; use bevy_transform::TransformSystems; From 302e158aa15692bd7114c7e42b8fd310cd26119d Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Tue, 31 Mar 2026 14:08:16 -0700 Subject: [PATCH 21/46] Reyeet migration guide --- ..._widgets_plugins_and_input_dispatch_plugin_are_now_default.md | 1 - 1 file changed, 1 deletion(-) diff --git a/_release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md b/_release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md index e9c54e8599abc..729e3b9febb0f 100644 --- a/_release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md +++ b/_release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md @@ -46,4 +46,3 @@ fn main() { .run(); } ``` - From 28989e73cbed9819f191e358a94c430a8a1810cc Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Tue, 31 Mar 2026 15:03:51 -0700 Subject: [PATCH 22/46] Remove redundant ordering --- crates/bevy_ui/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index f388f7a226ef7..407cfda4df849 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -181,7 +181,6 @@ impl Plugin for UiPlugin { let ui_layout_system_config = ui_layout_system .in_set(UiSystems::Layout) - .before(UiSystems::PostLayout) .before(TransformSystems::Propagate); let ui_layout_system_config = ui_layout_system_config From c62359509717c57eb366293b52edf97808fcfcc3 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 31 Mar 2026 23:16:41 +0100 Subject: [PATCH 23/46] Removed `Node` from scrollbar thumb nodes, instead `CoreScrollbarThumb` requires all the UI components and gains a `border_radius` field. `update_scrollbar_thumb` updates its `UiGlobalTransform` and `ComputedNode` in `PostLayout`. --- crates/bevy_ui_widgets/src/scrollbar.rs | 177 ++++++++++++------ examples/ui/scroll_and_overflow/scrollbars.rs | 13 +- 2 files changed, 122 insertions(+), 68 deletions(-) diff --git a/crates/bevy_ui_widgets/src/scrollbar.rs b/crates/bevy_ui_widgets/src/scrollbar.rs index 6c3087d7576b5..4c4c3cc778035 100644 --- a/crates/bevy_ui_widgets/src/scrollbar.rs +++ b/crates/bevy_ui_widgets/src/scrollbar.rs @@ -1,4 +1,5 @@ use bevy_app::{App, Plugin, PostUpdate}; +use bevy_camera::visibility::Visibility; use bevy_ecs::{ component::Component, entity::Entity, @@ -6,13 +7,16 @@ use bevy_ecs::{ observer::On, query::{With, Without}, reflect::ReflectComponent, + schedule::IntoScheduleConfigs, system::{Query, Res}, }; -use bevy_math::Vec2; +use bevy_math::{Affine2, Vec2}; use bevy_picking::events::{Cancel, Drag, DragEnd, DragStart, Pointer, Press}; use bevy_reflect::{prelude::ReflectDefault, Reflect}; use bevy_ui::{ - ComputedNode, ComputedUiRenderTargetInfo, Node, ScrollPosition, UiGlobalTransform, UiScale, Val, + BackgroundColor, BorderColor, BorderRadius, ComputedNode, ComputedUiRenderTargetInfo, + ComputedUiTargetCamera, ContentSize, FocusPolicy, ScrollPosition, UiGlobalTransform, UiScale, + UiSystems, UiTransform, ZIndex, }; /// Used to select the orientation of a scrollbar, slider, or other oriented control. @@ -64,11 +68,27 @@ pub struct Scrollbar { /// Marker component to indicate that the entity is a scrollbar thumb (the moving, draggable part of /// the scrollbar). This should be a child of the scrollbar entity. -#[derive(Component, Debug)] -#[require(CoreScrollbarDragState)] +#[derive(Component, Debug, Default)] +#[require( + CoreScrollbarDragState, + ComputedNode, + ContentSize, + ComputedUiTargetCamera, + ComputedUiRenderTargetInfo, + UiTransform, + BackgroundColor, + BorderColor, + FocusPolicy, + ScrollPosition, + Visibility, + ZIndex +)] #[derive(Reflect)] #[reflect(Component)] -pub struct CoreScrollbarThumb; +pub struct CoreScrollbarThumb { + /// Border radius of the scrollbar thumb, used to update [`ComputedNode::border_radius`] in [`update_scrollbar_thumb`]. + pub border_radius: BorderRadius, +} impl Scrollbar { /// Construct a new scrollbar. @@ -244,11 +264,23 @@ fn scrollbar_on_drag_cancel( } fn update_scrollbar_thumb( - q_scroll_area: Query<(&ScrollPosition, &ComputedNode)>, - q_scrollbar: Query<(&Scrollbar, &ComputedNode, &Children)>, - mut q_thumb: Query<&mut Node, With>, + q_scroll_area: Query<(&ScrollPosition, &ComputedNode), Without>, + q_scrollbar: Query< + (&Scrollbar, &ComputedNode, &UiGlobalTransform, &Children), + Without, + >, + mut q_thumb: Query< + ( + &CoreScrollbarThumb, + &UiTransform, + &ComputedUiRenderTargetInfo, + &mut ComputedNode, + &mut UiGlobalTransform, + ), + With, + >, ) { - for (scrollbar, scrollbar_node, children) in q_scrollbar.iter() { + for (scrollbar, scrollbar_node, scrollbar_transform, children) in q_scrollbar.iter() { let Ok(scroll_area) = q_scroll_area.get(scrollbar.target) else { continue; }; @@ -297,57 +329,77 @@ fn update_scrollbar_thumb( } for child in children { - if let Ok(mut thumb) = q_thumb.get_mut(*child) { - match scrollbar.orientation { - ControlOrientation::Horizontal => { - let (thumb_size, thumb_pos) = size_and_pos( - content_size.x, - visible_size.x, - track_length.x, - scrollbar.min_thumb_length, - scroll_area.0.x, - ); - - let top = Val::Px(0.); - let bottom = Val::Px(0.); - let left = Val::Px(thumb_pos); - let width = Val::Px(thumb_size); - if top != thumb.top - || bottom != thumb.bottom - || left != thumb.left - || width != thumb.width - { - thumb.top = top; - thumb.bottom = bottom; - thumb.left = left; - thumb.width = width; - } - } - ControlOrientation::Vertical => { - let (thumb_size, thumb_pos) = size_and_pos( - content_size.y, - visible_size.y, - track_length.y, - scrollbar.min_thumb_length, - scroll_area.0.y, - ); - - let left = Val::Px(0.); - let right = Val::Px(0.); - let top = Val::Px(thumb_pos); - let height = Val::Px(thumb_size); - if thumb.left != left - || thumb.right != right - || thumb.top != top - || thumb.height != height - { - thumb.left = left; - thumb.right = right; - thumb.top = top; - thumb.height = height; - } - } - }; + let Ok(( + thumb, + thumb_transform, + target_info, + mut thumb_node, + mut thumb_global_transform, + )) = q_thumb.get_mut(*child) + else { + continue; + }; + + let (thumb_logical_size, thumb_center) = match scrollbar.orientation { + ControlOrientation::Horizontal => { + let (thumb_size, thumb_pos) = size_and_pos( + content_size.x, + visible_size.x, + track_length.x, + scrollbar.min_thumb_length, + scroll_area.0.x, + ); + ( + Vec2::new(thumb_size, track_length.y), + Vec2::new(thumb_pos + 0.5 * (thumb_size - track_length.x), 0.), + ) + } + ControlOrientation::Vertical => { + let (thumb_size, thumb_pos) = size_and_pos( + content_size.y, + visible_size.y, + track_length.y, + scrollbar.min_thumb_length, + scroll_area.0.y, + ); + ( + Vec2::new(track_length.x, thumb_size), + Vec2::new(0., thumb_pos + 0.5 * (thumb_size - track_length.y)), + ) + } + }; + + let inverse_scale_factor = target_info.scale_factor().recip(); + let thumb_physical_size = thumb_logical_size * target_info.scale_factor(); + + if thumb_node.size != thumb_physical_size + || thumb_node.unrounded_size != thumb_physical_size + || thumb_node.inverse_scale_factor != inverse_scale_factor + { + thumb_node.size = thumb_physical_size; + thumb_node.unrounded_size = thumb_physical_size; + thumb_node.inverse_scale_factor = inverse_scale_factor; + } + + let border_radius = thumb.border_radius.resolve( + target_info.scale_factor(), + thumb_physical_size, + target_info.physical_size().as_vec2(), + ); + if thumb_node.border_radius != border_radius { + thumb_node.border_radius = border_radius; + } + + let new_transform = scrollbar_transform.affine() + * thumb_transform.compute_affine( + target_info.scale_factor(), + thumb_physical_size, + target_info.physical_size().as_vec2(), + ) + * Affine2::from_translation(thumb_center * target_info.scale_factor()); + + if thumb_global_transform.affine() != new_transform { + *thumb_global_transform = new_transform.into(); } } } @@ -363,6 +415,9 @@ impl Plugin for ScrollbarPlugin { .add_observer(scrollbar_on_drag_end) .add_observer(scrollbar_on_drag_cancel) .add_observer(scrollbar_on_drag) - .add_systems(PostUpdate, update_scrollbar_thumb); + .add_systems( + PostUpdate, + update_scrollbar_thumb.in_set(UiSystems::PostLayout), + ); } } diff --git a/examples/ui/scroll_and_overflow/scrollbars.rs b/examples/ui/scroll_and_overflow/scrollbars.rs index a3b1a754c32a3..694425c2f327b 100644 --- a/examples/ui/scroll_and_overflow/scrollbars.rs +++ b/examples/ui/scroll_and_overflow/scrollbars.rs @@ -118,14 +118,11 @@ fn scroll_area_demo() -> impl Bundle { min_thumb_length: 8.0, }, Children::spawn(Spawn(( - Node { - position_type: PositionType::Absolute, - border_radius: BorderRadius::all(px(4)), - ..default() - }, Hovered::default(), BackgroundColor(colors::GRAY2.into()), - CoreScrollbarThumb, + CoreScrollbarThumb { + border_radius: BorderRadius::all(px(4)), + }, ))), )); @@ -150,7 +147,9 @@ fn scroll_area_demo() -> impl Bundle { }, Hovered::default(), BackgroundColor(colors::GRAY2.into()), - CoreScrollbarThumb, + CoreScrollbarThumb { + border_radius: BorderRadius::all(px(4)), + }, ))), )); }),)), From a6ad25a00e43d2e51fed882bb986bc26c56ad676 Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Tue, 31 Mar 2026 15:19:06 -0700 Subject: [PATCH 24/46] Migrate example multiple_text_inputs --- examples/ui/text/multiple_text_inputs.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/examples/ui/text/multiple_text_inputs.rs b/examples/ui/text/multiple_text_inputs.rs index 5372ff67feec3..0f9832cd5b764 100644 --- a/examples/ui/text/multiple_text_inputs.rs +++ b/examples/ui/text/multiple_text_inputs.rs @@ -13,18 +13,13 @@ use bevy::input_focus::{ }; use bevy::prelude::*; use bevy::text::{EditableText, FontCx, LayoutCx, TextCursorStyle}; -use bevy::ui_widgets::EditableTextInputPlugin; fn main() { App::new() - .add_plugins(DefaultPlugins) - .add_plugins(( - // `EditableTextInputPlugin` is also part of `UiWidgetsPlugins`, but we only need `EditableText` for this example - EditableTextInputPlugin, - // Input focus is required to direct keyboard input to the correct `EditableText` - InputDispatchPlugin, - TabNavigationPlugin, - )) + // `EditableTextInputPlugin` is part of `DefaultPlugins` + // Input focus is required to direct keyboard input to the correct `EditableText` + .add_plugins((DefaultPlugins, TabNavigationPlugin)) + .add_plugins() .add_systems(Startup, setup) .add_systems( Update, From f05c5d30ac2190fd254b8be080fead4623077450 Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Tue, 31 Mar 2026 15:21:47 -0700 Subject: [PATCH 25/46] Remove empty add_plugins --- examples/ui/text/multiple_text_inputs.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/ui/text/multiple_text_inputs.rs b/examples/ui/text/multiple_text_inputs.rs index 0f9832cd5b764..937fee77a0b8b 100644 --- a/examples/ui/text/multiple_text_inputs.rs +++ b/examples/ui/text/multiple_text_inputs.rs @@ -19,7 +19,6 @@ fn main() { // `EditableTextInputPlugin` is part of `DefaultPlugins` // Input focus is required to direct keyboard input to the correct `EditableText` .add_plugins((DefaultPlugins, TabNavigationPlugin)) - .add_plugins() .add_systems(Startup, setup) .add_systems( Update, From dc46d3e6293e6ab4ec97ced2271d83d41013db72 Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Tue, 31 Mar 2026 15:29:59 -0700 Subject: [PATCH 26/46] Remove unused import --- examples/ui/text/multiple_text_inputs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ui/text/multiple_text_inputs.rs b/examples/ui/text/multiple_text_inputs.rs index 937fee77a0b8b..c242759a789ec 100644 --- a/examples/ui/text/multiple_text_inputs.rs +++ b/examples/ui/text/multiple_text_inputs.rs @@ -9,7 +9,7 @@ use bevy::input::keyboard::Key; use bevy::input_focus::AutoFocus; use bevy::input_focus::{ tab_navigation::{TabGroup, TabIndex, TabNavigationPlugin}, - InputDispatchPlugin, InputFocus, + InputFocus, }; use bevy::prelude::*; use bevy::text::{EditableText, FontCx, LayoutCx, TextCursorStyle}; From 7f31047e5ade6a7911ae99d9b29fbd9390a167c6 Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Tue, 31 Mar 2026 15:39:38 -0700 Subject: [PATCH 27/46] Move scrollbar to UiSystems::PostLayout --- crates/bevy_ui_widgets/src/popover.rs | 2 +- crates/bevy_ui_widgets/src/scrollbar.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ui_widgets/src/popover.rs b/crates/bevy_ui_widgets/src/popover.rs index 337d4f0fc88a3..4cf5afa70a8c8 100644 --- a/crates/bevy_ui_widgets/src/popover.rs +++ b/crates/bevy_ui_widgets/src/popover.rs @@ -95,7 +95,7 @@ impl Clone for Popover { } } -fn position_popover( +pub(crate) fn position_popover( mut q_popover: Query<( Entity, &mut Node, diff --git a/crates/bevy_ui_widgets/src/scrollbar.rs b/crates/bevy_ui_widgets/src/scrollbar.rs index 5032ede0ef443..9a6a6bf14840d 100644 --- a/crates/bevy_ui_widgets/src/scrollbar.rs +++ b/crates/bevy_ui_widgets/src/scrollbar.rs @@ -367,7 +367,9 @@ impl Plugin for ScrollbarPlugin { .add_observer(scrollbar_on_drag) .add_systems( PostUpdate, - update_scrollbar_thumb.in_set(UiSystems::Prepare), + update_scrollbar_thumb + .in_set(UiSystems::PostLayout) + .after(crate::popover::position_popover), ); } } From 02858f33dafd13265c36c0fa329dff4526dc6edf Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 31 Mar 2026 23:39:46 +0100 Subject: [PATCH 28/46] fixed doc link --- crates/bevy_ui_widgets/src/scrollbar.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ui_widgets/src/scrollbar.rs b/crates/bevy_ui_widgets/src/scrollbar.rs index 4c4c3cc778035..72a244e689ebb 100644 --- a/crates/bevy_ui_widgets/src/scrollbar.rs +++ b/crates/bevy_ui_widgets/src/scrollbar.rs @@ -86,7 +86,7 @@ pub struct Scrollbar { #[derive(Reflect)] #[reflect(Component)] pub struct CoreScrollbarThumb { - /// Border radius of the scrollbar thumb, used to update [`ComputedNode::border_radius`] in [`update_scrollbar_thumb`]. + /// Border radius of the scrollbar thumb, used to update [`ComputedNode::border_radius`] in [`UiSystems::PostLayout`]. pub border_radius: BorderRadius, } From 8ad3da4f2a5b04b4493451d68c601de67692d689 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 31 Mar 2026 15:59:12 -0700 Subject: [PATCH 29/46] Correctly order UiSystems::Stack --- crates/bevy_ui/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index d5c6f2c710c19..c4719882a36bc 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -150,6 +150,7 @@ impl Plugin for UiPlugin { UiSystems::Content, UiSystems::Layout, UiSystems::PostLayout, + UiSystems::Stack, ) .chain(), ) From c6c3ae31b83c08f8d0c48af4ea7ddcbec6ece40a Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Tue, 31 Mar 2026 16:00:20 -0700 Subject: [PATCH 30/46] Remove unused before(UiSystems::Prepare) --- crates/bevy_ui/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 407cfda4df849..9da29a213b144 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -195,7 +195,6 @@ impl Plugin for UiPlugin { ( ui_stack_system .in_set(UiSystems::Stack) - .after(UiSystems::Prepare) // These systems don't care about stack index .ambiguous_with(widget::measure_text_system) .ambiguous_with(update_clipping_system) From 26bde1f95f124cf29d0a4223dc1a9e42b56a51ca Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Tue, 31 Mar 2026 16:41:36 -0700 Subject: [PATCH 31/46] Show some direction --- crates/bevy_ui/src/lib.rs | 21 ++++++++++----------- crates/bevy_ui_widgets/src/scrollbar.rs | 6 +++++- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 11adf5f98a6de..0d21406af8427 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -193,17 +193,16 @@ impl Plugin for UiPlugin { ( propagate_ui_target_cameras.in_set(UiSystems::Prepare), ui_layout_system_config, - ( - ui_stack_system - .in_set(UiSystems::Stack) - // These systems don't care about stack index - .ambiguous_with(widget::measure_text_system) - .ambiguous_with(update_clipping_system) - .ambiguous_with(ui_layout_system) - .ambiguous_with(widget::update_viewport_render_target_size) - .in_set(AmbiguousWithText), - update_clipping_system.after(TransformSystems::Propagate), - ) + ui_stack_system + .in_set(UiSystems::Stack) + // These systems don't care about stack index + .ambiguous_with(widget::measure_text_system) + .ambiguous_with(update_clipping_system) + .ambiguous_with(ui_layout_system) + .ambiguous_with(widget::update_viewport_render_target_size) + .in_set(AmbiguousWithText), + update_clipping_system + .after(TransformSystems::Propagate) .before(UiSystems::PostLayout), // Potential conflicts: `Assets` // They run independently since `widget::image_node_system` will only ever observe diff --git a/crates/bevy_ui_widgets/src/scrollbar.rs b/crates/bevy_ui_widgets/src/scrollbar.rs index 14b062c0bc750..43aab41fe877d 100644 --- a/crates/bevy_ui_widgets/src/scrollbar.rs +++ b/crates/bevy_ui_widgets/src/scrollbar.rs @@ -417,7 +417,11 @@ impl Plugin for ScrollbarPlugin { .add_observer(scrollbar_on_drag) .add_systems( PostUpdate, - update_scrollbar_thumb.in_set(UiSystems::PostLayout), + update_scrollbar_thumb + .in_set(UiSystems::PostLayout) + .after(crate::popover::position_popover) + .after(bevy_ui::widget::editable_text_system) + .after(bevy_ui::widget::text_system), ); } } From f6c60a39596532ece7e37227b51c255fa9de1210 Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Tue, 31 Mar 2026 21:40:07 -0700 Subject: [PATCH 32/46] Revert "Show some direction" This reverts commit 26bde1f95f124cf29d0a4223dc1a9e42b56a51ca. --- crates/bevy_ui/src/lib.rs | 21 +++++++++++---------- crates/bevy_ui_widgets/src/scrollbar.rs | 6 +----- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 0d21406af8427..11adf5f98a6de 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -193,16 +193,17 @@ impl Plugin for UiPlugin { ( propagate_ui_target_cameras.in_set(UiSystems::Prepare), ui_layout_system_config, - ui_stack_system - .in_set(UiSystems::Stack) - // These systems don't care about stack index - .ambiguous_with(widget::measure_text_system) - .ambiguous_with(update_clipping_system) - .ambiguous_with(ui_layout_system) - .ambiguous_with(widget::update_viewport_render_target_size) - .in_set(AmbiguousWithText), - update_clipping_system - .after(TransformSystems::Propagate) + ( + ui_stack_system + .in_set(UiSystems::Stack) + // These systems don't care about stack index + .ambiguous_with(widget::measure_text_system) + .ambiguous_with(update_clipping_system) + .ambiguous_with(ui_layout_system) + .ambiguous_with(widget::update_viewport_render_target_size) + .in_set(AmbiguousWithText), + update_clipping_system.after(TransformSystems::Propagate), + ) .before(UiSystems::PostLayout), // Potential conflicts: `Assets` // They run independently since `widget::image_node_system` will only ever observe diff --git a/crates/bevy_ui_widgets/src/scrollbar.rs b/crates/bevy_ui_widgets/src/scrollbar.rs index 43aab41fe877d..14b062c0bc750 100644 --- a/crates/bevy_ui_widgets/src/scrollbar.rs +++ b/crates/bevy_ui_widgets/src/scrollbar.rs @@ -417,11 +417,7 @@ impl Plugin for ScrollbarPlugin { .add_observer(scrollbar_on_drag) .add_systems( PostUpdate, - update_scrollbar_thumb - .in_set(UiSystems::PostLayout) - .after(crate::popover::position_popover) - .after(bevy_ui::widget::editable_text_system) - .after(bevy_ui::widget::text_system), + update_scrollbar_thumb.in_set(UiSystems::PostLayout), ); } } From a5e13f8b7c37c5bebeb94b2b86697436280b75a3 Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Tue, 31 Mar 2026 21:42:07 -0700 Subject: [PATCH 33/46] Revert "Merge remote-tracking branch 'alice/order-ui-systems-better' into ui-widgets-to-default-2" This reverts commit 1beedf8f5b391fa3de9aa041232950b6537278d5, reversing changes made to 61b0ae6a8048cd049aa0546246c4b2c722c43914. --- crates/bevy_ecs/src/error/bevy_error.rs | 43 +++++-------------------- crates/bevy_ecs/src/error/handler.rs | 4 +-- crates/bevy_ui/src/lib.rs | 1 - 3 files changed, 9 insertions(+), 39 deletions(-) diff --git a/crates/bevy_ecs/src/error/bevy_error.rs b/crates/bevy_ecs/src/error/bevy_error.rs index b305d700134eb..d4a2983812db6 100644 --- a/crates/bevy_ecs/src/error/bevy_error.rs +++ b/crates/bevy_ecs/src/error/bevy_error.rs @@ -9,19 +9,10 @@ use core::{ /// /// # Severity /// -/// Each [`BevyError`] carries a [`Severity`] value that indicates how serious the error is. -/// While the levels within [`Severity`] correspond to traditional logging levels, -/// these levels are fundamentally advisory metadata. -/// The global error handler ultimately has discretion to respond to each of these errors -/// according to its configuration. -/// The error handler ultimately has discretion to respond to each of these errors according to its configuration. -/// You can change the behavior of the default handler by modifying the [`DefaultErrorHandler`] resource. +/// Each [`BevyError`] carries a [`Severity`] value that indicates how serious the error is. Severity is advisory +/// metadata used by error handlers to decide how to react (for example: ignore, log, or panic). /// -/// By default, errors without an assigned severity use [`Severity::Panic`], and will cause your application to panic. -/// You can change the severity of an error by using [`with_severity`] on any [`Result`] type. -/// -/// [`DefaultErrorHandler`]: crate::error::handler::DefaultErrorHandler -/// [`with_severity`]: ResultSeverityExt::with_severity +/// By default, errors have [`Severity::Critical`], which preserves Bevy’s known panic-on-error behavior unless explicitly overridden. /// /// # Backtraces /// @@ -119,36 +110,18 @@ struct InnerBevyError { } /// Indicates how severe a [`BevyError`] is. -/// -/// These levels correspond to traditional logging levels, -/// but the severity is advisory metadata used by error handlers to decide how to react (for example: ignore, log, or panic). -/// -/// To change the behavior of unhandled errors returned from systems, -/// you can modify the [default error handler], and read the [`Severity`] stored inside of each [`BevyError`]. -/// -/// You can change the severity of an error (including assigning an error severity) to an ordinary result -/// by calling [`with_severity`]. -/// -/// [`with_severity`]: ResultSeverityExt::with_severity -/// [default error handler]: crate::error::handler::DefaultErrorHandler #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)] pub enum Severity { - /// The error can be safely ignored, and can be completely discarded. + /// The error can be safely ignored. Ignore, - /// The error can be ignored, unless verbose debugging is required. - Trace, /// The error can be safely ignored, but may need to be surfaced during debugging. Debug, - /// Nothing has gone wrong, but the error is useful to the user and should be reported. - Info, /// Something unexpected but recoverable happened. - /// - /// Something has probably gone wrong. Warning, /// A real error occurred, but the program may continue. Error, - /// A fatal error; the program cannot continue. - Panic, + /// A fatal error; the default handler may panic. + Critical, } impl BevyError { @@ -169,7 +142,7 @@ impl BevyError { /// Extension methods for annotating errors with a [`Severity`]. pub trait ResultSeverityExt { - /// Overrides the [`Severity`] of the error if this result is `Err`. + /// Overrides the severity of the error if this result is `Err`. /// This does not change control flow; it only annotates the error. /// /// # Example @@ -205,7 +178,7 @@ where BevyError { inner: Box::new(InnerBevyError { error: error.into(), - severity: Severity::Panic, + severity: Severity::Critical, #[cfg(feature = "backtrace")] backtrace: std::backtrace::Backtrace::capture(), }), diff --git a/crates/bevy_ecs/src/error/handler.rs b/crates/bevy_ecs/src/error/handler.rs index 1e1b3ecc7bd36..010d2df13795b 100644 --- a/crates/bevy_ecs/src/error/handler.rs +++ b/crates/bevy_ecs/src/error/handler.rs @@ -126,12 +126,10 @@ impl Default for DefaultErrorHandler { pub fn match_severity(err: BevyError, ctx: ErrorContext) { match err.severity() { Severity::Ignore => ignore(err, ctx), - Severity::Trace => trace(err, ctx), Severity::Debug => debug(err, ctx), - Severity::Info => info(err, ctx), Severity::Warning => warn(err, ctx), Severity::Error => error(err, ctx), - Severity::Panic => panic(err, ctx), + Severity::Critical => panic(err, ctx), } } diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 11adf5f98a6de..9da29a213b144 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -150,7 +150,6 @@ impl Plugin for UiPlugin { UiSystems::Content, UiSystems::Layout, UiSystems::PostLayout, - UiSystems::Stack, ) .chain(), ) From 536aea6a896569f0bc31fc2af07061721e47472e Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Tue, 31 Mar 2026 22:46:54 -0700 Subject: [PATCH 34/46] Go edgy on PostLayout --- crates/bevy_ui_widgets/src/popover.rs | 2 +- crates/bevy_ui_widgets/src/scrollbar.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ui_widgets/src/popover.rs b/crates/bevy_ui_widgets/src/popover.rs index 4cf5afa70a8c8..74be148ea1f92 100644 --- a/crates/bevy_ui_widgets/src/popover.rs +++ b/crates/bevy_ui_widgets/src/popover.rs @@ -305,7 +305,7 @@ pub struct PopoverPlugin; impl Plugin for PopoverPlugin { fn build(&self, app: &mut App) { - app.add_systems(PostUpdate, position_popover.in_set(UiSystems::PostLayout)); + app.add_systems(PostUpdate, position_popover.after(UiSystems::PostLayout)); } } diff --git a/crates/bevy_ui_widgets/src/scrollbar.rs b/crates/bevy_ui_widgets/src/scrollbar.rs index 14b062c0bc750..05d7ff77c4035 100644 --- a/crates/bevy_ui_widgets/src/scrollbar.rs +++ b/crates/bevy_ui_widgets/src/scrollbar.rs @@ -417,7 +417,9 @@ impl Plugin for ScrollbarPlugin { .add_observer(scrollbar_on_drag) .add_systems( PostUpdate, - update_scrollbar_thumb.in_set(UiSystems::PostLayout), + update_scrollbar_thumb + .after(UiSystems::PostLayout) + .before(crate::popover::position_popover), ); } } From 0c60af7b8a74ae2b4b1afe64ac12fbde40298b5e Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Tue, 31 Mar 2026 23:42:18 -0700 Subject: [PATCH 35/46] Chain instead of ambiguity --- crates/bevy_input_focus/src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/bevy_input_focus/src/lib.rs b/crates/bevy_input_focus/src/lib.rs index b5cfdf35389e0..79e951686d85a 100644 --- a/crates/bevy_input_focus/src/lib.rs +++ b/crates/bevy_input_focus/src/lib.rs @@ -236,13 +236,12 @@ impl Plugin for InputDispatchPlugin { #[cfg(feature = "keyboard")] dispatch_focused_input::, #[cfg(feature = "gamepad")] - dispatch_focused_input::, + dispatch_focused_input::.after(InputSystems), #[cfg(feature = "mouse")] dispatch_focused_input::, ) - .in_set(InputFocusSystems::Dispatch) - .ambiguous_with(InputFocusSystems::Dispatch) - .after(InputSystems), + .chain() + .in_set(InputFocusSystems::Dispatch), ); } } From 37be382f4434d5618834d98f92726e6ca903c47e Mon Sep 17 00:00:00 2001 From: Luke Yoo Date: Wed, 1 Apr 2026 00:00:12 -0700 Subject: [PATCH 36/46] Re-revert merged bevy_error.rs and handler.rs from "Merge remote-tracking branch 'alice/order-ui-systems-better' into ui-widgets-to-default-2" --- crates/bevy_ecs/src/error/bevy_error.rs | 43 ++++++++++++++++++++----- crates/bevy_ecs/src/error/handler.rs | 4 ++- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/crates/bevy_ecs/src/error/bevy_error.rs b/crates/bevy_ecs/src/error/bevy_error.rs index d4a2983812db6..b305d700134eb 100644 --- a/crates/bevy_ecs/src/error/bevy_error.rs +++ b/crates/bevy_ecs/src/error/bevy_error.rs @@ -9,10 +9,19 @@ use core::{ /// /// # Severity /// -/// Each [`BevyError`] carries a [`Severity`] value that indicates how serious the error is. Severity is advisory -/// metadata used by error handlers to decide how to react (for example: ignore, log, or panic). +/// Each [`BevyError`] carries a [`Severity`] value that indicates how serious the error is. +/// While the levels within [`Severity`] correspond to traditional logging levels, +/// these levels are fundamentally advisory metadata. +/// The global error handler ultimately has discretion to respond to each of these errors +/// according to its configuration. +/// The error handler ultimately has discretion to respond to each of these errors according to its configuration. +/// You can change the behavior of the default handler by modifying the [`DefaultErrorHandler`] resource. /// -/// By default, errors have [`Severity::Critical`], which preserves Bevy’s known panic-on-error behavior unless explicitly overridden. +/// By default, errors without an assigned severity use [`Severity::Panic`], and will cause your application to panic. +/// You can change the severity of an error by using [`with_severity`] on any [`Result`] type. +/// +/// [`DefaultErrorHandler`]: crate::error::handler::DefaultErrorHandler +/// [`with_severity`]: ResultSeverityExt::with_severity /// /// # Backtraces /// @@ -110,18 +119,36 @@ struct InnerBevyError { } /// Indicates how severe a [`BevyError`] is. +/// +/// These levels correspond to traditional logging levels, +/// but the severity is advisory metadata used by error handlers to decide how to react (for example: ignore, log, or panic). +/// +/// To change the behavior of unhandled errors returned from systems, +/// you can modify the [default error handler], and read the [`Severity`] stored inside of each [`BevyError`]. +/// +/// You can change the severity of an error (including assigning an error severity) to an ordinary result +/// by calling [`with_severity`]. +/// +/// [`with_severity`]: ResultSeverityExt::with_severity +/// [default error handler]: crate::error::handler::DefaultErrorHandler #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)] pub enum Severity { - /// The error can be safely ignored. + /// The error can be safely ignored, and can be completely discarded. Ignore, + /// The error can be ignored, unless verbose debugging is required. + Trace, /// The error can be safely ignored, but may need to be surfaced during debugging. Debug, + /// Nothing has gone wrong, but the error is useful to the user and should be reported. + Info, /// Something unexpected but recoverable happened. + /// + /// Something has probably gone wrong. Warning, /// A real error occurred, but the program may continue. Error, - /// A fatal error; the default handler may panic. - Critical, + /// A fatal error; the program cannot continue. + Panic, } impl BevyError { @@ -142,7 +169,7 @@ impl BevyError { /// Extension methods for annotating errors with a [`Severity`]. pub trait ResultSeverityExt { - /// Overrides the severity of the error if this result is `Err`. + /// Overrides the [`Severity`] of the error if this result is `Err`. /// This does not change control flow; it only annotates the error. /// /// # Example @@ -178,7 +205,7 @@ where BevyError { inner: Box::new(InnerBevyError { error: error.into(), - severity: Severity::Critical, + severity: Severity::Panic, #[cfg(feature = "backtrace")] backtrace: std::backtrace::Backtrace::capture(), }), diff --git a/crates/bevy_ecs/src/error/handler.rs b/crates/bevy_ecs/src/error/handler.rs index 010d2df13795b..1e1b3ecc7bd36 100644 --- a/crates/bevy_ecs/src/error/handler.rs +++ b/crates/bevy_ecs/src/error/handler.rs @@ -126,10 +126,12 @@ impl Default for DefaultErrorHandler { pub fn match_severity(err: BevyError, ctx: ErrorContext) { match err.severity() { Severity::Ignore => ignore(err, ctx), + Severity::Trace => trace(err, ctx), Severity::Debug => debug(err, ctx), + Severity::Info => info(err, ctx), Severity::Warning => warn(err, ctx), Severity::Error => error(err, ctx), - Severity::Critical => panic(err, ctx), + Severity::Panic => panic(err, ctx), } } From 9ef4f8e15c9860e87f11eaa6e78a556aace5210b Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 2 Apr 2026 11:34:34 -0400 Subject: [PATCH 37/46] Apply system ordering fixes from ickshonpe Co-authored-by: ickshonpe --- crates/bevy_ui/src/lib.rs | 4 +++- crates/bevy_ui_widgets/src/popover.rs | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 9da29a213b144..b135348874379 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -191,7 +191,9 @@ impl Plugin for UiPlugin { PostUpdate, ( propagate_ui_target_cameras.in_set(UiSystems::Prepare), - ui_layout_system_config, + ui_layout_system + .in_set(UiSystems::Layout) + .ambiguous_with(bevy_sprite::update_text2d_layout), ( ui_stack_system .in_set(UiSystems::Stack) diff --git a/crates/bevy_ui_widgets/src/popover.rs b/crates/bevy_ui_widgets/src/popover.rs index 74be148ea1f92..75496a68f2a18 100644 --- a/crates/bevy_ui_widgets/src/popover.rs +++ b/crates/bevy_ui_widgets/src/popover.rs @@ -305,7 +305,12 @@ pub struct PopoverPlugin; impl Plugin for PopoverPlugin { fn build(&self, app: &mut App) { - app.add_systems(PostUpdate, position_popover.after(UiSystems::PostLayout)); + app.add_systems( + PostUpdate, + position_popover + .in_set(UiSystems::Layout) + .after(bevy_ui::ui_layout_system), + ); } } From a05747c9121e3a6a519ccba07a9380bef0f1a061 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 2 Apr 2026 11:35:14 -0400 Subject: [PATCH 38/46] Add clipping ordering fix Co-authored-by: ickshonpe --- crates/bevy_ui/src/lib.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index b135348874379..a24c88e9afa0b 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -194,18 +194,15 @@ impl Plugin for UiPlugin { ui_layout_system .in_set(UiSystems::Layout) .ambiguous_with(bevy_sprite::update_text2d_layout), - ( - ui_stack_system - .in_set(UiSystems::Stack) - // These systems don't care about stack index - .ambiguous_with(widget::measure_text_system) - .ambiguous_with(update_clipping_system) - .ambiguous_with(ui_layout_system) - .ambiguous_with(widget::update_viewport_render_target_size) - .in_set(AmbiguousWithText), - update_clipping_system.after(TransformSystems::Propagate), - ) + ui_stack_system + .in_set(UiSystems::Stack) + // These systems don't care about stack index + .ambiguous_with(widget::measure_text_system) + .ambiguous_with(ui_layout_system) + .ambiguous_with(widget::update_viewport_render_target_size) + .in_set(AmbiguousWithText) .before(UiSystems::PostLayout), + update_clipping_system.in_set(UiSystems::PostLayout), // Potential conflicts: `Assets` // They run independently since `widget::image_node_system` will only ever observe // its own ImageNode, and `widget::text_system` & `bevy_text::update_text2d_layout` From 217a2ef873592b0be46a885b902829ae6b2144e0 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 2 Apr 2026 08:41:59 -0700 Subject: [PATCH 39/46] Clean up unused variable --- crates/bevy_ui/src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index a24c88e9afa0b..f22d248c7d3a4 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -179,11 +179,9 @@ impl Plugin for UiPlugin { widget::viewport_picking.in_set(PickingSystems::PostInput), ); - let ui_layout_system_config = ui_layout_system + ui_layout_system .in_set(UiSystems::Layout) - .before(TransformSystems::Propagate); - - let ui_layout_system_config = ui_layout_system_config + .before(TransformSystems::Propagate) // Text and Text2D operate on disjoint sets of entities .ambiguous_with(bevy_sprite::update_text2d_layout); From aa7774e5e65d0ee464f61dbc72697d760c86b2e8 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 2 Apr 2026 08:50:10 -0700 Subject: [PATCH 40/46] All input dispatching should run after InputSystems --- crates/bevy_input_focus/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/bevy_input_focus/src/lib.rs b/crates/bevy_input_focus/src/lib.rs index 79e951686d85a..772d36eda4e5c 100644 --- a/crates/bevy_input_focus/src/lib.rs +++ b/crates/bevy_input_focus/src/lib.rs @@ -236,12 +236,13 @@ impl Plugin for InputDispatchPlugin { #[cfg(feature = "keyboard")] dispatch_focused_input::, #[cfg(feature = "gamepad")] - dispatch_focused_input::.after(InputSystems), + dispatch_focused_input::, #[cfg(feature = "mouse")] dispatch_focused_input::, ) .chain() - .in_set(InputFocusSystems::Dispatch), + .in_set(InputFocusSystems::Dispatch) + .after(InputSystems), ); } } From a1b148078fee8bd7d2af1d3bf98193a3f8d68f0f Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 2 Apr 2026 08:52:22 -0700 Subject: [PATCH 41/46] Fix cycle of position_popover --- crates/bevy_ui_widgets/src/popover.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/crates/bevy_ui_widgets/src/popover.rs b/crates/bevy_ui_widgets/src/popover.rs index 75496a68f2a18..4cf5afa70a8c8 100644 --- a/crates/bevy_ui_widgets/src/popover.rs +++ b/crates/bevy_ui_widgets/src/popover.rs @@ -305,12 +305,7 @@ pub struct PopoverPlugin; impl Plugin for PopoverPlugin { fn build(&self, app: &mut App) { - app.add_systems( - PostUpdate, - position_popover - .in_set(UiSystems::Layout) - .after(bevy_ui::ui_layout_system), - ); + app.add_systems(PostUpdate, position_popover.in_set(UiSystems::PostLayout)); } } From 4d63f825cb7408f56ff809aab9d7e6bbc4adb39c Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 2 Apr 2026 10:07:49 -0700 Subject: [PATCH 42/46] Remove unused import --- examples/ui/scroll_and_overflow/scrollbars.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/ui/scroll_and_overflow/scrollbars.rs b/examples/ui/scroll_and_overflow/scrollbars.rs index 891a9ac223212..82c1ea9899f5c 100644 --- a/examples/ui/scroll_and_overflow/scrollbars.rs +++ b/examples/ui/scroll_and_overflow/scrollbars.rs @@ -5,9 +5,7 @@ use bevy::{ input_focus::tab_navigation::{TabGroup, TabNavigationPlugin}, picking::hover::Hovered, prelude::*, - ui_widgets::{ - ControlOrientation, CoreScrollbarDragState, Scrollbar, ScrollbarPlugin, ScrollbarThumb, - }, + ui_widgets::{ControlOrientation, CoreScrollbarDragState, Scrollbar, ScrollbarThumb}, }; fn main() { From 74c3b891d6bbbc837deab6324987e0a83680b0ac Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 2 Apr 2026 10:12:43 -0700 Subject: [PATCH 43/46] Fix spurious merge conflict change for ScrollbarPlugin order --- crates/bevy_ui_widgets/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ui_widgets/src/lib.rs b/crates/bevy_ui_widgets/src/lib.rs index ec472c58cafa1..027ebbf85951c 100644 --- a/crates/bevy_ui_widgets/src/lib.rs +++ b/crates/bevy_ui_widgets/src/lib.rs @@ -55,8 +55,8 @@ impl PluginGroup for UiWidgetsPlugins { .add(CheckboxPlugin) .add(MenuPlugin) .add(RadioGroupPlugin) - .add(SliderPlugin) .add(ScrollbarPlugin) + .add(SliderPlugin) .add(EditableTextInputPlugin) } } From de3cf6d8f383cfb66b9d148e1cb8aae10b46b470 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 2 Apr 2026 14:09:18 -0400 Subject: [PATCH 44/46] Remove old comments Co-authored-by: ickshonpe --- examples/ui/navigation/directional_navigation.rs | 1 - examples/ui/text/multiple_text_inputs.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/examples/ui/navigation/directional_navigation.rs b/examples/ui/navigation/directional_navigation.rs index 1433829745643..1412e6e160737 100644 --- a/examples/ui/navigation/directional_navigation.rs +++ b/examples/ui/navigation/directional_navigation.rs @@ -34,7 +34,6 @@ use bevy::{ fn main() { App::new() - // Input focus is not enabled by default, so we need to add the corresponding plugins .add_plugins((DefaultPlugins, DirectionalNavigationPlugin)) // This resource is canonically used to track whether or not to render a focus indicator // It starts as false, but we set it to true here as we would like to see the focus indicator diff --git a/examples/ui/text/multiple_text_inputs.rs b/examples/ui/text/multiple_text_inputs.rs index c242759a789ec..80a0e39101e89 100644 --- a/examples/ui/text/multiple_text_inputs.rs +++ b/examples/ui/text/multiple_text_inputs.rs @@ -17,7 +17,6 @@ use bevy::text::{EditableText, FontCx, LayoutCx, TextCursorStyle}; fn main() { App::new() // `EditableTextInputPlugin` is part of `DefaultPlugins` - // Input focus is required to direct keyboard input to the correct `EditableText` .add_plugins((DefaultPlugins, TabNavigationPlugin)) .add_systems(Startup, setup) .add_systems( From d9fad7bdc77399df4d6c37d0c8c0fdff1aef23f4 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 2 Apr 2026 14:09:45 -0400 Subject: [PATCH 45/46] Fix timing of position_popover Co-authored-by: ickshonpe --- crates/bevy_ui_widgets/src/popover.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/bevy_ui_widgets/src/popover.rs b/crates/bevy_ui_widgets/src/popover.rs index 4cf5afa70a8c8..85c6925b5c46f 100644 --- a/crates/bevy_ui_widgets/src/popover.rs +++ b/crates/bevy_ui_widgets/src/popover.rs @@ -305,7 +305,13 @@ pub struct PopoverPlugin; impl Plugin for PopoverPlugin { fn build(&self, app: &mut App) { - app.add_systems(PostUpdate, position_popover.in_set(UiSystems::PostLayout)); + app.add_systems( + PostUpdate, + position_popover + .in_set(UiSystems::Layout) + .after(ui_layout_system) + .before(update_scrollbar_thumb), + ); } } From 3920238e124653e1b2d1bde8a771358d766fbcd7 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 2 Apr 2026 11:12:54 -0700 Subject: [PATCH 46/46] Fix missing imports --- crates/bevy_ui_widgets/src/popover.rs | 20 +++++++++++--------- crates/bevy_ui_widgets/src/scrollbar.rs | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/crates/bevy_ui_widgets/src/popover.rs b/crates/bevy_ui_widgets/src/popover.rs index 85c6925b5c46f..88acd2e28eb31 100644 --- a/crates/bevy_ui_widgets/src/popover.rs +++ b/crates/bevy_ui_widgets/src/popover.rs @@ -11,10 +11,12 @@ use bevy_ecs::{ }; use bevy_math::{Affine2, Rect, Vec2}; use bevy_ui::{ - ComputedNode, ComputedUiRenderTargetInfo, Node, PositionType, UiGlobalTransform, UiSystems, - UiTransform, Val2, + ui_layout_system, ComputedNode, ComputedUiRenderTargetInfo, Node, PositionType, + UiGlobalTransform, UiSystems, UiTransform, Val2, }; +use crate::update_scrollbar_thumb; + /// Which side of the parent element the popover element should be placed. #[derive(Debug, Default, Clone, Copy, PartialEq)] pub enum PopoverSide { @@ -305,13 +307,13 @@ pub struct PopoverPlugin; impl Plugin for PopoverPlugin { fn build(&self, app: &mut App) { - app.add_systems( - PostUpdate, - position_popover - .in_set(UiSystems::Layout) - .after(ui_layout_system) - .before(update_scrollbar_thumb), - ); + app.add_systems( + PostUpdate, + position_popover + .in_set(UiSystems::Layout) + .after(ui_layout_system) + .before(update_scrollbar_thumb), + ); } } diff --git a/crates/bevy_ui_widgets/src/scrollbar.rs b/crates/bevy_ui_widgets/src/scrollbar.rs index 8f32dbb4ef8bf..d73296212f8e4 100644 --- a/crates/bevy_ui_widgets/src/scrollbar.rs +++ b/crates/bevy_ui_widgets/src/scrollbar.rs @@ -270,7 +270,7 @@ fn scrollbar_on_drag_cancel( } } -fn update_scrollbar_thumb( +pub(crate) fn update_scrollbar_thumb( q_scroll_area: Query<(&ScrollPosition, &ComputedNode), Without>, q_scrollbar: Query< (&Scrollbar, &ComputedNode, &UiGlobalTransform, &Children),