
# Objective Change `ScrollPosition` to newtype `Vec2`. It's easier to work with a `Vec2` wrapper than individual fields. I'm not sure why this wasn't newtyped to start with. Maybe the intent was to support responsive coordinates eventually but that probably isn't very useful or straightforward to implement. And even if we do want to support responsive coords in the future, it can newtype `Val2`. ## Solution Change `ScrollPosition` to newtype `Vec2`. Also added some extra details to the doc comments. ## Testing Try the `scroll` example. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
206 lines
7.0 KiB
Rust
206 lines
7.0 KiB
Rust
//! Demonstrations of scrolling and scrollbars.
|
|
|
|
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: Val::Px(0.),
|
|
top: Val::Px(0.),
|
|
right: Val::Px(0.),
|
|
bottom: Val::Px(0.),
|
|
padding: UiRect::all(Val::Px(3.)),
|
|
row_gap: Val::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()))),
|
|
));
|
|
}
|
|
|
|
/// Create a scrolling area.
|
|
///
|
|
/// The "scroll area" is a container that can be scrolled. It has a nested structure which is
|
|
/// three levels deep:
|
|
/// - The outermost node is a grid that contains the scroll area and the scrollbars.
|
|
/// - The scroll area is a flex container that contains the scrollable content. This
|
|
/// is the element that has the `overflow: scroll` property.
|
|
/// - The scrollable content consists of the elements actually displayed in the scrolling area.
|
|
fn scroll_area_demo() -> impl Bundle {
|
|
(
|
|
// Frame element which contains the scroll area and scrollbars.
|
|
Node {
|
|
display: Display::Grid,
|
|
width: Val::Px(200.0),
|
|
height: Val::Px(150.0),
|
|
grid_template_columns: vec![RepeatedGridTrack::flex(1, 1.), RepeatedGridTrack::auto(1)],
|
|
grid_template_rows: vec![RepeatedGridTrack::flex(1, 1.), RepeatedGridTrack::auto(1)],
|
|
row_gap: Val::Px(2.0),
|
|
column_gap: Val::Px(2.0),
|
|
..default()
|
|
},
|
|
Children::spawn((SpawnWith(|parent: &mut RelatedSpawner<ChildOf>| {
|
|
// The actual scrolling area.
|
|
// Note that we're using `SpawnWith` here because we need to get the entity id of the
|
|
// scroll area in order to set the target of the scrollbars.
|
|
let scroll_area_id = parent
|
|
.spawn((
|
|
Node {
|
|
display: Display::Flex,
|
|
flex_direction: FlexDirection::Column,
|
|
padding: UiRect::all(Val::Px(4.0)),
|
|
overflow: Overflow::scroll(),
|
|
..default()
|
|
},
|
|
BackgroundColor(colors::GRAY1.into()),
|
|
ScrollPosition(Vec2::new(0.0, 10.0)),
|
|
Children::spawn((
|
|
// The actual content of the scrolling area
|
|
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();
|
|
|
|
// Vertical scrollbar
|
|
parent.spawn((
|
|
Node {
|
|
min_width: Val::Px(8.0),
|
|
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(Val::Px(4.0)),
|
|
CoreScrollbarThumb,
|
|
))),
|
|
));
|
|
|
|
// Horizontal scrollbar
|
|
parent.spawn((
|
|
Node {
|
|
min_height: Val::Px(8.0),
|
|
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(Val::Px(4.0)),
|
|
CoreScrollbarThumb,
|
|
))),
|
|
));
|
|
}),)),
|
|
)
|
|
}
|
|
|
|
/// Create a list row
|
|
fn text_row(caption: &str) -> impl Bundle {
|
|
(
|
|
Text::new(caption),
|
|
TextFont {
|
|
font_size: 14.0,
|
|
..default()
|
|
},
|
|
)
|
|
}
|
|
|
|
// Update the color of the scrollbar thumb.
|
|
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 {
|
|
// If hovering, use a lighter color
|
|
colors::GRAY3
|
|
} else {
|
|
// Default color for the slider
|
|
colors::GRAY2
|
|
}
|
|
.into();
|
|
|
|
if thumb_bg.0 != color {
|
|
// Update the color of the thumb
|
|
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);
|
|
}
|