Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/ui/scrollbars.rs
6595 views
1
//! Demonstrations of scrolling and scrollbars.
2
3
use bevy::{
4
core_widgets::{
5
ControlOrientation, CoreScrollbar, CoreScrollbarDragState, CoreScrollbarPlugin,
6
CoreScrollbarThumb,
7
},
8
ecs::{relationship::RelatedSpawner, spawn::SpawnWith},
9
input_focus::{
10
tab_navigation::{TabGroup, TabNavigationPlugin},
11
InputDispatchPlugin,
12
},
13
picking::hover::Hovered,
14
prelude::*,
15
};
16
17
fn main() {
18
App::new()
19
.add_plugins((
20
DefaultPlugins,
21
CoreScrollbarPlugin,
22
InputDispatchPlugin,
23
TabNavigationPlugin,
24
))
25
.insert_resource(UiScale(1.25))
26
.add_systems(Startup, setup_view_root)
27
.add_systems(Update, update_scrollbar_thumb)
28
.run();
29
}
30
31
fn setup_view_root(mut commands: Commands) {
32
let camera = commands.spawn((Camera::default(), Camera2d)).id();
33
34
commands.spawn((
35
Node {
36
display: Display::Flex,
37
flex_direction: FlexDirection::Column,
38
position_type: PositionType::Absolute,
39
left: px(0),
40
top: px(0),
41
right: px(0),
42
bottom: px(0),
43
padding: UiRect::all(px(3)),
44
row_gap: px(6),
45
..Default::default()
46
},
47
BackgroundColor(Color::srgb(0.1, 0.1, 0.1)),
48
UiTargetCamera(camera),
49
TabGroup::default(),
50
Children::spawn((Spawn(Text::new("Scrolling")), Spawn(scroll_area_demo()))),
51
));
52
}
53
54
/// Create a scrolling area.
55
///
56
/// The "scroll area" is a container that can be scrolled. It has a nested structure which is
57
/// three levels deep:
58
/// - The outermost node is a grid that contains the scroll area and the scrollbars.
59
/// - The scroll area is a flex container that contains the scrollable content. This
60
/// is the element that has the `overflow: scroll` property.
61
/// - The scrollable content consists of the elements actually displayed in the scrolling area.
62
fn scroll_area_demo() -> impl Bundle {
63
(
64
// Frame element which contains the scroll area and scrollbars.
65
Node {
66
display: Display::Grid,
67
width: px(200),
68
height: px(150),
69
grid_template_columns: vec![RepeatedGridTrack::flex(1, 1.), RepeatedGridTrack::auto(1)],
70
grid_template_rows: vec![RepeatedGridTrack::flex(1, 1.), RepeatedGridTrack::auto(1)],
71
row_gap: px(2),
72
column_gap: px(2),
73
..default()
74
},
75
Children::spawn((SpawnWith(|parent: &mut RelatedSpawner<ChildOf>| {
76
// The actual scrolling area.
77
// Note that we're using `SpawnWith` here because we need to get the entity id of the
78
// scroll area in order to set the target of the scrollbars.
79
let scroll_area_id = parent
80
.spawn((
81
Node {
82
display: Display::Flex,
83
flex_direction: FlexDirection::Column,
84
padding: UiRect::all(px(4)),
85
overflow: Overflow::scroll(),
86
..default()
87
},
88
BackgroundColor(colors::GRAY1.into()),
89
ScrollPosition(Vec2::new(0.0, 10.0)),
90
Children::spawn((
91
// The actual content of the scrolling area
92
Spawn(text_row("Alpha Wolf")),
93
Spawn(text_row("Beta Blocker")),
94
Spawn(text_row("Delta Sleep")),
95
Spawn(text_row("Gamma Ray")),
96
Spawn(text_row("Epsilon Eridani")),
97
Spawn(text_row("Zeta Function")),
98
Spawn(text_row("Lambda Calculus")),
99
Spawn(text_row("Nu Metal")),
100
Spawn(text_row("Pi Day")),
101
Spawn(text_row("Chi Pants")),
102
Spawn(text_row("Psi Powers")),
103
Spawn(text_row("Omega Fatty Acid")),
104
)),
105
))
106
.id();
107
108
// Vertical scrollbar
109
parent.spawn((
110
Node {
111
min_width: px(8),
112
grid_row: GridPlacement::start(1),
113
grid_column: GridPlacement::start(2),
114
..default()
115
},
116
CoreScrollbar {
117
orientation: ControlOrientation::Vertical,
118
target: scroll_area_id,
119
min_thumb_length: 8.0,
120
},
121
Children::spawn(Spawn((
122
Node {
123
position_type: PositionType::Absolute,
124
..default()
125
},
126
Hovered::default(),
127
BackgroundColor(colors::GRAY2.into()),
128
BorderRadius::all(px(4)),
129
CoreScrollbarThumb,
130
))),
131
));
132
133
// Horizontal scrollbar
134
parent.spawn((
135
Node {
136
min_height: px(8),
137
grid_row: GridPlacement::start(2),
138
grid_column: GridPlacement::start(1),
139
..default()
140
},
141
CoreScrollbar {
142
orientation: ControlOrientation::Horizontal,
143
target: scroll_area_id,
144
min_thumb_length: 8.0,
145
},
146
Children::spawn(Spawn((
147
Node {
148
position_type: PositionType::Absolute,
149
..default()
150
},
151
Hovered::default(),
152
BackgroundColor(colors::GRAY2.into()),
153
BorderRadius::all(px(4)),
154
CoreScrollbarThumb,
155
))),
156
));
157
}),)),
158
)
159
}
160
161
/// Create a list row
162
fn text_row(caption: &str) -> impl Bundle {
163
(
164
Text::new(caption),
165
TextFont {
166
font_size: 14.0,
167
..default()
168
},
169
)
170
}
171
172
// Update the color of the scrollbar thumb.
173
fn update_scrollbar_thumb(
174
mut q_thumb: Query<
175
(&mut BackgroundColor, &Hovered, &CoreScrollbarDragState),
176
(
177
With<CoreScrollbarThumb>,
178
Or<(Changed<Hovered>, Changed<CoreScrollbarDragState>)>,
179
),
180
>,
181
) {
182
for (mut thumb_bg, Hovered(is_hovering), drag) in q_thumb.iter_mut() {
183
let color: Color = if *is_hovering || drag.dragging {
184
// If hovering, use a lighter color
185
colors::GRAY3
186
} else {
187
// Default color for the slider
188
colors::GRAY2
189
}
190
.into();
191
192
if thumb_bg.0 != color {
193
// Update the color of the thumb
194
thumb_bg.0 = color;
195
}
196
}
197
}
198
199
mod colors {
200
use bevy::color::Srgba;
201
202
pub const GRAY1: Srgba = Srgba::new(0.224, 0.224, 0.243, 1.0);
203
pub const GRAY2: Srgba = Srgba::new(0.486, 0.486, 0.529, 1.0);
204
pub const GRAY3: Srgba = Srgba::new(1.0, 1.0, 1.0, 1.0);
205
}
206
207