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