1use 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
16fn 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
30fn 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
53fn scroll_area_demo() -> impl Bundle {
62 (
63 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 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 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 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 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
160fn text_row(caption: &str) -> impl Bundle {
162 (
163 Text::new(caption),
164 TextFont {
165 font_size: 14.0,
166 ..default()
167 },
168 )
169}
170
171fn 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 colors::GRAY3
185 } else {
186 colors::GRAY2
188 }
189 .into();
190
191 if thumb_bg.0 != color {
192 thumb_bg.0 = color;
194 }
195 }
196}
197
198mod 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}