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