Path: blob/main/crates/bevy_feathers/src/controls/slider.rs
6596 views
use core::f32::consts::PI;12use bevy_app::{Plugin, PreUpdate};3use bevy_color::Color;4use bevy_core_widgets::{Callback, CoreSlider, SliderRange, SliderValue, TrackClick, ValueChange};5use bevy_ecs::{6bundle::Bundle,7children,8component::Component,9entity::Entity,10hierarchy::Children,11lifecycle::RemovedComponents,12query::{Added, Changed, Has, Or, Spawned, With},13reflect::ReflectComponent,14schedule::IntoScheduleConfigs,15spawn::SpawnRelated,16system::{Commands, In, Query, Res},17};18use bevy_input_focus::tab_navigation::TabIndex;19use bevy_picking::PickingSystems;20use bevy_reflect::{prelude::ReflectDefault, Reflect};21use bevy_ui::{22widget::Text, AlignItems, BackgroundGradient, ColorStop, Display, FlexDirection, Gradient,23InteractionDisabled, InterpolationColorSpace, JustifyContent, LinearGradient, Node,24PositionType, UiRect, Val,25};2627use crate::{28constants::{fonts, size},29cursor::EntityCursor,30font_styles::InheritableFont,31handle_or_path::HandleOrPath,32rounded_corners::RoundedCorners,33theme::{ThemeFontColor, ThemedText, UiTheme},34tokens,35};3637/// Slider template properties, passed to [`slider`] function.38pub struct SliderProps {39/// Slider current value40pub value: f32,41/// Slider minimum value42pub min: f32,43/// Slider maximum value44pub max: f32,45/// On-change handler46pub on_change: Callback<In<ValueChange<f32>>>,47}4849impl Default for SliderProps {50fn default() -> Self {51Self {52value: 0.0,53min: 0.0,54max: 1.0,55on_change: Callback::Ignore,56}57}58}5960#[derive(Component, Default, Clone)]61#[require(CoreSlider)]62#[derive(Reflect)]63#[reflect(Component, Clone, Default)]64struct SliderStyle;6566/// Marker for the text67#[derive(Component, Default, Clone, Reflect)]68#[reflect(Component, Clone, Default)]69struct SliderValueText;7071/// Spawn a new slider widget.72///73/// # Arguments74///75/// * `props` - construction properties for the slider.76/// * `overrides` - a bundle of components that are merged in with the normal slider components.77pub fn slider<B: Bundle>(props: SliderProps, overrides: B) -> impl Bundle {78(79Node {80height: size::ROW_HEIGHT,81justify_content: JustifyContent::Center,82align_items: AlignItems::Center,83padding: UiRect::axes(Val::Px(8.0), Val::Px(0.)),84flex_grow: 1.0,85..Default::default()86},87CoreSlider {88on_change: props.on_change,89track_click: TrackClick::Drag,90},91SliderStyle,92SliderValue(props.value),93SliderRange::new(props.min, props.max),94EntityCursor::System(bevy_window::SystemCursorIcon::EwResize),95TabIndex(0),96RoundedCorners::All.to_border_radius(6.0),97// Use a gradient to draw the moving bar98BackgroundGradient(vec![Gradient::Linear(LinearGradient {99angle: PI * 0.5,100stops: vec![101ColorStop::new(Color::NONE, Val::Percent(0.)),102ColorStop::new(Color::NONE, Val::Percent(50.)),103ColorStop::new(Color::NONE, Val::Percent(50.)),104ColorStop::new(Color::NONE, Val::Percent(100.)),105],106color_space: InterpolationColorSpace::Srgba,107})]),108overrides,109children![(110// Text container111Node {112display: Display::Flex,113position_type: PositionType::Absolute,114flex_direction: FlexDirection::Row,115align_items: AlignItems::Center,116justify_content: JustifyContent::Center,117..Default::default()118},119ThemeFontColor(tokens::SLIDER_TEXT),120InheritableFont {121font: HandleOrPath::Path(fonts::MONO.to_owned()),122font_size: 12.0,123},124children![(Text::new("10.0"), ThemedText, SliderValueText,)],125)],126)127}128129fn update_slider_styles(130mut q_sliders: Query<131(Entity, Has<InteractionDisabled>, &mut BackgroundGradient),132(With<SliderStyle>, Or<(Spawned, Added<InteractionDisabled>)>),133>,134theme: Res<UiTheme>,135mut commands: Commands,136) {137for (slider_ent, disabled, mut gradient) in q_sliders.iter_mut() {138set_slider_styles(139slider_ent,140&theme,141disabled,142gradient.as_mut(),143&mut commands,144);145}146}147148fn update_slider_styles_remove(149mut q_sliders: Query<(Entity, Has<InteractionDisabled>, &mut BackgroundGradient)>,150mut removed_disabled: RemovedComponents<InteractionDisabled>,151theme: Res<UiTheme>,152mut commands: Commands,153) {154removed_disabled.read().for_each(|ent| {155if let Ok((slider_ent, disabled, mut gradient)) = q_sliders.get_mut(ent) {156set_slider_styles(157slider_ent,158&theme,159disabled,160gradient.as_mut(),161&mut commands,162);163}164});165}166167fn set_slider_styles(168slider_ent: Entity,169theme: &Res<'_, UiTheme>,170disabled: bool,171gradient: &mut BackgroundGradient,172commands: &mut Commands,173) {174let bar_color = theme.color(match disabled {175true => tokens::SLIDER_BAR_DISABLED,176false => tokens::SLIDER_BAR,177});178179let bg_color = theme.color(tokens::SLIDER_BG);180181let cursor_shape = match disabled {182true => bevy_window::SystemCursorIcon::NotAllowed,183false => bevy_window::SystemCursorIcon::EwResize,184};185186if let [Gradient::Linear(linear_gradient)] = &mut gradient.0[..] {187linear_gradient.stops[0].color = bar_color;188linear_gradient.stops[1].color = bar_color;189linear_gradient.stops[2].color = bg_color;190linear_gradient.stops[3].color = bg_color;191}192193// Change cursor shape194commands195.entity(slider_ent)196.insert(EntityCursor::System(cursor_shape));197}198199fn update_slider_pos(200mut q_sliders: Query<201(Entity, &SliderValue, &SliderRange, &mut BackgroundGradient),202(203With<SliderStyle>,204Or<(205Changed<SliderValue>,206Changed<SliderRange>,207Changed<Children>,208)>,209),210>,211q_children: Query<&Children>,212mut q_slider_text: Query<&mut Text, With<SliderValueText>>,213) {214for (slider_ent, value, range, mut gradient) in q_sliders.iter_mut() {215if let [Gradient::Linear(linear_gradient)] = &mut gradient.0[..] {216let percent_value = range.thumb_position(value.0) * 100.0;217linear_gradient.stops[1].point = Val::Percent(percent_value);218linear_gradient.stops[2].point = Val::Percent(percent_value);219}220221// Find slider text child entity and update its text with the formatted value222q_children.iter_descendants(slider_ent).for_each(|child| {223if let Ok(mut text) = q_slider_text.get_mut(child) {224text.0 = format!("{}", value.0);225}226});227}228}229230/// Plugin which registers the systems for updating the slider styles.231pub struct SliderPlugin;232233impl Plugin for SliderPlugin {234fn build(&self, app: &mut bevy_app::App) {235app.add_systems(236PreUpdate,237(238update_slider_styles,239update_slider_styles_remove,240update_slider_pos,241)242.in_set(PickingSystems::Last),243);244}245}246247248