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