diff --git a/crates/bevy_feathers/src/controls/slider.rs b/crates/bevy_feathers/src/controls/slider.rs index 3c43f476de956..c09e8eaae7e98 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,33 @@ 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, + Changed, + 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,37 +236,69 @@ 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, + ), + With, + >, 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, ) { - 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..3df7d52aeca62 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_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), (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"); 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)