From e09497bd7554c2a6b9eec98377e303139487f3e1 Mon Sep 17 00:00:00 2001 From: Jordan Halase Date: Sat, 18 Apr 2026 17:23:45 -0500 Subject: [PATCH 1/6] Begin adding queries --- crates/bevy_feathers/src/controls/slider.rs | 63 +++++++++++++++------ 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/crates/bevy_feathers/src/controls/slider.rs b/crates/bevy_feathers/src/controls/slider.rs index 3c43f476de956..436c5a041778a 100644 --- a/crates/bevy_feathers/src/controls/slider.rs +++ b/crates/bevy_feathers/src/controls/slider.rs @@ -15,14 +15,14 @@ use bevy_ecs::{ system::{Commands, Query, Res}, }; use bevy_input_focus::tab_navigation::TabIndex; -use bevy_picking::PickingSystems; +use bevy_picking::{hover::Hovered, PickingSystems}; use bevy_reflect::{prelude::ReflectDefault, Reflect}; use bevy_scene::prelude::*; use bevy_text::FontWeight; use bevy_ui::{ widget::Text, AlignItems, BackgroundGradient, ColorStop, Display, FlexDirection, Gradient, InteractionDisabled, InterpolationColorSpace, JustifyContent, LinearGradient, Node, - PositionType, UiRect, Val, + PositionType, Pressed, UiRect, Val, }; use bevy_ui_widgets::{ Slider, SliderOrientation, SliderPrecision, SliderRange, SliderValue, TrackClick, @@ -90,6 +90,7 @@ pub fn slider(props: SliderProps) -> impl Scene { flex_grow: 1.0, border_radius: {RoundedCorners::All.to_border_radius(6.0)}, } + Hovered Slider { track_click: TrackClick::Drag, orientation: SliderOrientation::Horizontal, @@ -155,6 +156,7 @@ pub fn slider_bundle(props: SliderProps, overrides: B) -> impl Bundle border_radius: RoundedCorners::All.to_border_radius(6.0), ..Default::default() }, + Hovered::default(), Slider { track_click: TrackClick::Drag, orientation: SliderOrientation::Horizontal, @@ -200,17 +202,28 @@ pub fn slider_bundle(props: SliderProps, overrides: B) -> impl Bundle fn update_slider_styles( mut q_sliders: Query< - (Entity, Has, &mut BackgroundGradient), - (With, Or<(Spawned, Added)>), + ( + Entity, + Has, + Has, + &Hovered, + &mut BackgroundGradient, + ), + ( + With, + Or<(Spawned, Added, Added)>, + ), >, theme: Res, mut commands: Commands, ) { - for (slider_ent, disabled, mut gradient) in q_sliders.iter_mut() { + for (slider_ent, disabled, pressed, hovered, mut gradient) in q_sliders.iter_mut() { set_slider_styles( slider_ent, &theme, disabled, + pressed, + hovered.0, gradient.as_mut(), &mut commands, ); @@ -218,28 +231,44 @@ fn update_slider_styles( } fn update_slider_styles_remove( - mut q_sliders: Query<(Entity, Has, &mut BackgroundGradient)>, + mut q_sliders: Query<( + Entity, + Has, + Has, + &Hovered, + &mut BackgroundGradient, + )>, mut removed_disabled: RemovedComponents, + mut remove_pressed: RemovedComponents, theme: Res, mut commands: Commands, ) { - removed_disabled.read().for_each(|ent| { - if let Ok((slider_ent, disabled, mut gradient)) = q_sliders.get_mut(ent) { - set_slider_styles( - slider_ent, - &theme, - disabled, - gradient.as_mut(), - &mut commands, - ); - } - }); + removed_disabled + .read() + .chain(remove_pressed.read()) + .for_each(|ent| { + if let Ok((slider_ent, disabled, pressed, hovered, mut gradient)) = + q_sliders.get_mut(ent) + { + set_slider_styles( + slider_ent, + &theme, + disabled, + pressed, + hovered.0, + gradient.as_mut(), + &mut commands, + ); + } + }); } fn set_slider_styles( slider_ent: Entity, theme: &Res<'_, UiTheme>, disabled: bool, + pressed: bool, + hovered: bool, gradient: &mut BackgroundGradient, commands: &mut Commands, ) { From 29cd7f36de076fe0516b703e9ee84e7b24e4e913 Mon Sep 17 00:00:00 2001 From: Jordan Halase Date: Sat, 18 Apr 2026 19:10:22 -0500 Subject: [PATCH 2/6] Add theme tokens --- crates/bevy_feathers/src/controls/slider.rs | 28 +++++++++++++++++---- crates/bevy_feathers/src/dark_theme.rs | 5 ++++ crates/bevy_feathers/src/tokens.rs | 14 +++++++++-- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/crates/bevy_feathers/src/controls/slider.rs b/crates/bevy_feathers/src/controls/slider.rs index 436c5a041778a..3ceec02dd33ff 100644 --- a/crates/bevy_feathers/src/controls/slider.rs +++ b/crates/bevy_feathers/src/controls/slider.rs @@ -211,7 +211,12 @@ fn update_slider_styles( ), ( With, - Or<(Spawned, Added, Added)>, + Or<( + Spawned, + Added, + Changed, + Added, + )>, ), >, theme: Res, @@ -272,12 +277,25 @@ fn set_slider_styles( gradient: &mut BackgroundGradient, commands: &mut Commands, ) { - let bar_color = theme.color(&match disabled { - true => tokens::SLIDER_BAR_DISABLED, - false => tokens::SLIDER_BAR, + let bar_color = theme.color(&if disabled { + tokens::SLIDER_BAR_DISABLED + } else if pressed { + tokens::SLIDER_BAR_PRESSED + } else if hovered { + tokens::SLIDER_BAR_HOVER + } else { + tokens::SLIDER_BAR }); - let bg_color = theme.color(&tokens::SLIDER_BG); + let bg_color = theme.color(&if disabled { + tokens::SLIDER_BG_DISABLED + } else if pressed { + tokens::SLIDER_BG_PRESSED + } else if hovered { + tokens::SLIDER_BG_HOVER + } else { + tokens::SLIDER_BG + }); let cursor_shape = match disabled { true => bevy_window::SystemCursorIcon::NotAllowed, diff --git a/crates/bevy_feathers/src/dark_theme.rs b/crates/bevy_feathers/src/dark_theme.rs index 8be4fde80c78d..8e06b8d60476a 100644 --- a/crates/bevy_feathers/src/dark_theme.rs +++ b/crates/bevy_feathers/src/dark_theme.rs @@ -44,7 +44,12 @@ pub fn create_dark_theme() -> ThemeProps { ), // Slider (tokens::SLIDER_BG, palette::GRAY_1), + (tokens::SLIDER_BG_HOVER, palette::GRAY_1.lighter(0.05)), + (tokens::SLIDER_BG_PRESSED, palette::GRAY_1.lighter(0.1)), + (tokens::SLIDER_BG_DISABLED, palette::GRAY_2), (tokens::SLIDER_BAR, palette::ACCENT), + (tokens::SLIDER_BAR_HOVER, palette::ACCENT.lighter(0.05)), + (tokens::SLIDER_BAR_PRESSED, palette::ACCENT.lighter(0.1)), (tokens::SLIDER_BAR_DISABLED, palette::GRAY_2), (tokens::SLIDER_TEXT, palette::WHITE), (tokens::SLIDER_TEXT_DISABLED, palette::WHITE.with_alpha(0.5)), diff --git a/crates/bevy_feathers/src/tokens.rs b/crates/bevy_feathers/src/tokens.rs index 16e7306b10872..8c3b7491fde65 100644 --- a/crates/bevy_feathers/src/tokens.rs +++ b/crates/bevy_feathers/src/tokens.rs @@ -69,9 +69,19 @@ pub const BUTTON_PLAIN_BG_PRESSED: ThemeToken = /// Background for slider pub const SLIDER_BG: ThemeToken = ThemeToken::new_static("feathers.slider.bg"); -/// Background for slider moving bar +/// Background for slider (hovered) +pub const SLIDER_BG_HOVER: ThemeToken = ThemeToken::new_static("feathers.slider.bg.hover"); +/// Background for slider (pressed) +pub const SLIDER_BG_PRESSED: ThemeToken = ThemeToken::new_static("feathers.slider.bg.pressed"); +/// Background for slider (disabled) +pub const SLIDER_BG_DISABLED: ThemeToken = ThemeToken::new_static("feathers.slider.bg.disabled"); +/// Fill color for slider pub const SLIDER_BAR: ThemeToken = ThemeToken::new_static("feathers.slider.bar"); -/// Background for slider moving bar (disabled) +/// Fill color for slider (hovered) +pub const SLIDER_BAR_HOVER: ThemeToken = ThemeToken::new_static("feathers.slider.bar.hover"); +/// Fill color for slider (pressed) +pub const SLIDER_BAR_PRESSED: ThemeToken = ThemeToken::new_static("feathers.slider.bar.pressed"); +/// Fill color for slider (disabled) pub const SLIDER_BAR_DISABLED: ThemeToken = ThemeToken::new_static("feathers.slider.bar.disabled"); /// Background for slider text pub const SLIDER_TEXT: ThemeToken = ThemeToken::new_static("feathers.slider.text"); From a1299962b2052f685f67bc716a4c55ca1603ab59 Mon Sep 17 00:00:00 2001 From: Jordan Halase Date: Sat, 18 Apr 2026 19:40:21 -0500 Subject: [PATCH 3/6] Add pressed events to slider --- crates/bevy_ui_widgets/src/slider.rs | 43 +++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ui_widgets/src/slider.rs b/crates/bevy_ui_widgets/src/slider.rs index 12d9149eb36f5..6b619425a563a 100644 --- a/crates/bevy_ui_widgets/src/slider.rs +++ b/crates/bevy_ui_widgets/src/slider.rs @@ -21,10 +21,11 @@ use bevy_input::ButtonState; use bevy_input_focus::FocusedInput; use bevy_log::warn_once; use bevy_math::ops; -use bevy_picking::events::{Drag, DragEnd, DragStart, Pointer, Press}; +use bevy_picking::events::{Cancel, Drag, DragEnd, DragStart, Pointer, Press, Release}; use bevy_reflect::{prelude::ReflectDefault, Reflect}; use bevy_ui::{ - ComputedNode, ComputedUiRenderTargetInfo, InteractionDisabled, UiGlobalTransform, UiScale, + ComputedNode, ComputedUiRenderTargetInfo, InteractionDisabled, Pressed, UiGlobalTransform, + UiScale, }; use crate::ValueChange; @@ -247,6 +248,7 @@ pub struct CoreSliderDragState { pub(crate) fn slider_on_pointer_down( mut press: On>, q_slider: Query<( + Entity, &Slider, &SliderValue, &SliderRange, @@ -266,6 +268,7 @@ pub(crate) fn slider_on_pointer_down( // Thumb click, stop propagation to prevent track click. press.propagate(false); } else if let Ok(( + slider_ent, slider, value, range, @@ -284,6 +287,8 @@ pub(crate) fn slider_on_pointer_down( return; } + commands.entity(slider_ent).insert(Pressed); + let is_vertical = slider.orientation.is_vertical(node); // Find thumb size by searching descendants for the first entity with SliderThumb @@ -446,16 +451,44 @@ pub(crate) fn slider_on_drag( pub(crate) fn slider_on_drag_end( mut drag_end: On>, - mut q_slider: Query<(&Slider, &mut CoreSliderDragState)>, + mut q_slider: Query<(Entity, &Slider, &mut CoreSliderDragState)>, + mut commands: Commands, ) { - if let Ok((_slider, mut drag)) = q_slider.get_mut(drag_end.entity) { + if let Ok((slider_ent, _slider, mut drag)) = q_slider.get_mut(drag_end.entity) { drag_end.propagate(false); + commands.entity(slider_ent).remove::(); if drag.dragging { drag.dragging = false; } } } +fn slider_on_pointer_up( + mut release: On>, + mut q_slider: Query<(Entity, Has, Has), With>, + mut commands: Commands, +) { + if let Ok((slider, disabled, pressed)) = q_slider.get_mut(release.entity) { + release.propagate(false); + if !disabled && pressed { + commands.entity(slider).remove::(); + } + } +} + +fn slider_on_pointer_cancel( + mut release: On>, + mut q_slider: Query<(Entity, Has, Has), With>, + mut commands: Commands, +) { + if let Ok((slider, disabled, pressed)) = q_slider.get_mut(release.entity) { + release.propagate(false); + if !disabled && pressed { + commands.entity(slider).remove::(); + } + } +} + fn slider_on_key_input( mut focused_input: On>, q_slider: Query< @@ -616,6 +649,8 @@ pub struct SliderPlugin; impl Plugin for SliderPlugin { fn build(&self, app: &mut App) { app.add_observer(slider_on_pointer_down) + .add_observer(slider_on_pointer_up) + .add_observer(slider_on_pointer_cancel) .add_observer(slider_on_drag_start) .add_observer(slider_on_drag_end) .add_observer(slider_on_drag) From 6ae2898c0dfb03879b3fc0162981d330e2ecb0f7 Mon Sep 17 00:00:00 2001 From: Jordan Halase Date: Sat, 18 Apr 2026 19:46:31 -0500 Subject: [PATCH 4/6] Change disabled colors --- crates/bevy_feathers/src/dark_theme.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_feathers/src/dark_theme.rs b/crates/bevy_feathers/src/dark_theme.rs index 8e06b8d60476a..a7ef0c6d7e629 100644 --- a/crates/bevy_feathers/src/dark_theme.rs +++ b/crates/bevy_feathers/src/dark_theme.rs @@ -46,11 +46,11 @@ pub fn create_dark_theme() -> ThemeProps { (tokens::SLIDER_BG, palette::GRAY_1), (tokens::SLIDER_BG_HOVER, palette::GRAY_1.lighter(0.05)), (tokens::SLIDER_BG_PRESSED, palette::GRAY_1.lighter(0.1)), - (tokens::SLIDER_BG_DISABLED, palette::GRAY_2), + (tokens::SLIDER_BG_DISABLED, palette::GRAY_1.with_alpha(0.5)), (tokens::SLIDER_BAR, palette::ACCENT), (tokens::SLIDER_BAR_HOVER, palette::ACCENT.lighter(0.05)), (tokens::SLIDER_BAR_PRESSED, palette::ACCENT.lighter(0.1)), - (tokens::SLIDER_BAR_DISABLED, palette::GRAY_2), + (tokens::SLIDER_BAR_DISABLED, palette::GRAY_2.with_alpha(0.5)), (tokens::SLIDER_TEXT, palette::WHITE), (tokens::SLIDER_TEXT_DISABLED, palette::WHITE.with_alpha(0.5)), // Scrollbar From 419d24ac68b9335ab23d49b0fc2a42d39cbe2c4d Mon Sep 17 00:00:00 2001 From: Jordan Halase Date: Sat, 18 Apr 2026 19:58:16 -0500 Subject: [PATCH 5/6] Adjust colors --- crates/bevy_feathers/src/dark_theme.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_feathers/src/dark_theme.rs b/crates/bevy_feathers/src/dark_theme.rs index a7ef0c6d7e629..3df7d52aeca62 100644 --- a/crates/bevy_feathers/src/dark_theme.rs +++ b/crates/bevy_feathers/src/dark_theme.rs @@ -46,11 +46,11 @@ pub fn create_dark_theme() -> ThemeProps { (tokens::SLIDER_BG, palette::GRAY_1), (tokens::SLIDER_BG_HOVER, palette::GRAY_1.lighter(0.05)), (tokens::SLIDER_BG_PRESSED, palette::GRAY_1.lighter(0.1)), - (tokens::SLIDER_BG_DISABLED, palette::GRAY_1.with_alpha(0.5)), + (tokens::SLIDER_BG_DISABLED, palette::GRAY_1), (tokens::SLIDER_BAR, palette::ACCENT), (tokens::SLIDER_BAR_HOVER, palette::ACCENT.lighter(0.05)), (tokens::SLIDER_BAR_PRESSED, palette::ACCENT.lighter(0.1)), - (tokens::SLIDER_BAR_DISABLED, palette::GRAY_2.with_alpha(0.5)), + (tokens::SLIDER_BAR_DISABLED, palette::GRAY_2), (tokens::SLIDER_TEXT, palette::WHITE), (tokens::SLIDER_TEXT_DISABLED, palette::WHITE.with_alpha(0.5)), // Scrollbar From 71c91aa4ca8176cd8d0b0d24fa846b8543d93c3e Mon Sep 17 00:00:00 2001 From: Jordan Halase Date: Sun, 19 Apr 2026 11:14:06 -0500 Subject: [PATCH 6/6] Add With to update_slider_styles_remove() query --- crates/bevy_feathers/src/controls/slider.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/crates/bevy_feathers/src/controls/slider.rs b/crates/bevy_feathers/src/controls/slider.rs index 3ceec02dd33ff..c09e8eaae7e98 100644 --- a/crates/bevy_feathers/src/controls/slider.rs +++ b/crates/bevy_feathers/src/controls/slider.rs @@ -236,13 +236,16 @@ fn update_slider_styles( } fn update_slider_styles_remove( - mut q_sliders: Query<( - Entity, - Has, - Has, - &Hovered, - &mut BackgroundGradient, - )>, + mut q_sliders: Query< + ( + Entity, + Has, + Has, + &Hovered, + &mut BackgroundGradient, + ), + With, + >, mut removed_disabled: RemovedComponents, mut remove_pressed: RemovedComponents, theme: Res,