diff --git a/examples/ui/ui.rs b/examples/ui/ui.rs index e35d4afb7aef1..112e7ce4730bb 100644 --- a/examples/ui/ui.rs +++ b/examples/ui/ui.rs @@ -1,10 +1,14 @@ -use bevy::prelude::*; +use bevy::{ + input::mouse::{MouseScrollUnit, MouseWheel}, + prelude::*, +}; /// This example illustrates the various features of Bevy UI. fn main() { App::new() .add_plugins(DefaultPlugins) .add_startup_system(setup) + .add_system(mouse_scroll) .run(); } @@ -68,14 +72,97 @@ fn setup(mut commands: Commands, asset_server: Res) { }); }); // right vertical fill - parent.spawn_bundle(NodeBundle { - style: Style { - size: Size::new(Val::Px(200.0), Val::Percent(100.0)), + parent + .spawn_bundle(NodeBundle { + style: Style { + flex_direction: FlexDirection::ColumnReverse, + justify_content: JustifyContent::Center, + size: Size::new(Val::Px(200.0), Val::Percent(100.0)), + ..Default::default() + }, + color: Color::rgb(0.15, 0.15, 0.15).into(), ..Default::default() - }, - color: Color::rgb(0.15, 0.15, 0.15).into(), - ..Default::default() - }); + }) + .with_children(|parent| { + // Title + parent.spawn_bundle(TextBundle { + style: Style { + size: Size::new(Val::Undefined, Val::Px(25.)), + margin: Rect { + left: Val::Auto, + right: Val::Auto, + ..Default::default() + }, + ..Default::default() + }, + text: Text::with_section( + "Scrolling list", + TextStyle { + font: asset_server.load("fonts/FiraSans-Bold.ttf"), + font_size: 25., + color: Color::WHITE, + }, + Default::default(), + ), + ..Default::default() + }); + // List with hidden overflow + parent + .spawn_bundle(NodeBundle { + style: Style { + flex_direction: FlexDirection::ColumnReverse, + align_self: AlignSelf::Center, + size: Size::new(Val::Percent(100.0), Val::Percent(50.0)), + overflow: Overflow::Hidden, + ..Default::default() + }, + color: Color::rgb(0.10, 0.10, 0.10).into(), + ..Default::default() + }) + .with_children(|parent| { + // Moving panel + parent + .spawn_bundle(NodeBundle { + style: Style { + flex_direction: FlexDirection::ColumnReverse, + flex_grow: 1.0, + max_size: Size::new(Val::Undefined, Val::Undefined), + ..Default::default() + }, + color: Color::NONE.into(), + ..Default::default() + }) + .insert(ScrollingList::default()) + .with_children(|parent| { + // List items + for i in 0..30 { + parent.spawn_bundle(TextBundle { + style: Style { + flex_shrink: 0., + size: Size::new(Val::Undefined, Val::Px(20.)), + margin: Rect { + left: Val::Auto, + right: Val::Auto, + ..Default::default() + }, + ..Default::default() + }, + text: Text::with_section( + format!("Item {}", i), + TextStyle { + font: asset_server + .load("fonts/FiraSans-Bold.ttf"), + font_size: 20., + color: Color::WHITE, + }, + Default::default(), + ), + ..Default::default() + }); + } + }); + }); + }); // absolute positioning parent .spawn_bundle(NodeBundle { @@ -212,3 +299,32 @@ fn setup(mut commands: Commands, asset_server: Res) { }); }); } + +#[derive(Component, Default)] +struct ScrollingList { + position: f32, +} + +fn mouse_scroll( + mut mouse_wheel_events: EventReader, + mut query_list: Query<(&mut ScrollingList, &mut Style, &Children, &Node)>, + query_item: Query<&Node>, +) { + for mouse_wheel_event in mouse_wheel_events.iter() { + for (mut scrolling_list, mut style, children, uinode) in query_list.iter_mut() { + let items_height: f32 = children + .iter() + .map(|entity| query_item.get(*entity).unwrap().size.y) + .sum(); + let panel_height = uinode.size.y; + let max_scroll = (items_height - panel_height).max(0.); + let dy = match mouse_wheel_event.unit { + MouseScrollUnit::Line => mouse_wheel_event.y * 20., + MouseScrollUnit::Pixel => mouse_wheel_event.y, + }; + scrolling_list.position += dy; + scrolling_list.position = scrolling_list.position.clamp(-max_scroll, 0.); + style.position.top = Val::Px(scrolling_list.position); + } + } +}