use bevy::{
core_widgets::{
ControlOrientation, CoreScrollbar, CoreScrollbarDragState, CoreScrollbarPlugin,
CoreScrollbarThumb,
},
ecs::{relationship::RelatedSpawner, spawn::SpawnWith},
input_focus::{
tab_navigation::{TabGroup, TabNavigationPlugin},
InputDispatchPlugin,
},
picking::hover::Hovered,
prelude::*,
};
fn main() {
App::new()
.add_plugins((
DefaultPlugins,
CoreScrollbarPlugin,
InputDispatchPlugin,
TabNavigationPlugin,
))
.insert_resource(UiScale(1.25))
.add_systems(Startup, setup_view_root)
.add_systems(Update, update_scrollbar_thumb)
.run();
}
fn setup_view_root(mut commands: Commands) {
let camera = commands.spawn((Camera::default(), Camera2d)).id();
commands.spawn((
Node {
display: Display::Flex,
flex_direction: FlexDirection::Column,
position_type: PositionType::Absolute,
left: px(0),
top: px(0),
right: px(0),
bottom: px(0),
padding: UiRect::all(px(3)),
row_gap: px(6),
..Default::default()
},
BackgroundColor(Color::srgb(0.1, 0.1, 0.1)),
UiTargetCamera(camera),
TabGroup::default(),
Children::spawn((Spawn(Text::new("Scrolling")), Spawn(scroll_area_demo()))),
));
}
fn scroll_area_demo() -> impl Bundle {
(
Node {
display: Display::Grid,
width: px(200),
height: px(150),
grid_template_columns: vec![RepeatedGridTrack::flex(1, 1.), RepeatedGridTrack::auto(1)],
grid_template_rows: vec![RepeatedGridTrack::flex(1, 1.), RepeatedGridTrack::auto(1)],
row_gap: px(2),
column_gap: px(2),
..default()
},
Children::spawn((SpawnWith(|parent: &mut RelatedSpawner<ChildOf>| {
let scroll_area_id = parent
.spawn((
Node {
display: Display::Flex,
flex_direction: FlexDirection::Column,
padding: UiRect::all(px(4)),
overflow: Overflow::scroll(),
..default()
},
BackgroundColor(colors::GRAY1.into()),
ScrollPosition(Vec2::new(0.0, 10.0)),
Children::spawn((
Spawn(text_row("Alpha Wolf")),
Spawn(text_row("Beta Blocker")),
Spawn(text_row("Delta Sleep")),
Spawn(text_row("Gamma Ray")),
Spawn(text_row("Epsilon Eridani")),
Spawn(text_row("Zeta Function")),
Spawn(text_row("Lambda Calculus")),
Spawn(text_row("Nu Metal")),
Spawn(text_row("Pi Day")),
Spawn(text_row("Chi Pants")),
Spawn(text_row("Psi Powers")),
Spawn(text_row("Omega Fatty Acid")),
)),
))
.id();
parent.spawn((
Node {
min_width: px(8),
grid_row: GridPlacement::start(1),
grid_column: GridPlacement::start(2),
..default()
},
CoreScrollbar {
orientation: ControlOrientation::Vertical,
target: scroll_area_id,
min_thumb_length: 8.0,
},
Children::spawn(Spawn((
Node {
position_type: PositionType::Absolute,
..default()
},
Hovered::default(),
BackgroundColor(colors::GRAY2.into()),
BorderRadius::all(px(4)),
CoreScrollbarThumb,
))),
));
parent.spawn((
Node {
min_height: px(8),
grid_row: GridPlacement::start(2),
grid_column: GridPlacement::start(1),
..default()
},
CoreScrollbar {
orientation: ControlOrientation::Horizontal,
target: scroll_area_id,
min_thumb_length: 8.0,
},
Children::spawn(Spawn((
Node {
position_type: PositionType::Absolute,
..default()
},
Hovered::default(),
BackgroundColor(colors::GRAY2.into()),
BorderRadius::all(px(4)),
CoreScrollbarThumb,
))),
));
}),)),
)
}
fn text_row(caption: &str) -> impl Bundle {
(
Text::new(caption),
TextFont {
font_size: 14.0,
..default()
},
)
}
fn update_scrollbar_thumb(
mut q_thumb: Query<
(&mut BackgroundColor, &Hovered, &CoreScrollbarDragState),
(
With<CoreScrollbarThumb>,
Or<(Changed<Hovered>, Changed<CoreScrollbarDragState>)>,
),
>,
) {
for (mut thumb_bg, Hovered(is_hovering), drag) in q_thumb.iter_mut() {
let color: Color = if *is_hovering || drag.dragging {
colors::GRAY3
} else {
colors::GRAY2
}
.into();
if thumb_bg.0 != color {
thumb_bg.0 = color;
}
}
}
mod colors {
use bevy::color::Srgba;
pub const GRAY1: Srgba = Srgba::new(0.224, 0.224, 0.243, 1.0);
pub const GRAY2: Srgba = Srgba::new(0.486, 0.486, 0.529, 1.0);
pub const GRAY3: Srgba = Srgba::new(1.0, 1.0, 1.0, 1.0);
}