You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

172 lines
5.5 KiB
Rust

#![feature(iter_intersperse)]
//! This example illustrates scrolling in Bevy UI.
use bevy::picking::{
hover::HoverMap,
pointer::{PointerId, PointerMap},
};
use games::*;
use lipsum::*;
use rand::*;
fn main() {
let mut app = App::new();
app.add_plugins(BaseGamePlugin::default())
.add_systems(Startup, (setup_list, setup_nav_tree))
.add_systems(
Update,
(
hide_menu,
control_menu.run_if(on_event::<Pointer<Over>>.or(on_event::<Pointer<Out>>)),
),
);
app.run();
}
fn setup_list(mut commands: Commands) {
// Scrolling list
commands
.spawn((
Node {
flex_direction: FlexDirection::Column,
// If height is not set, we need both align_self: Stetch and overflow: scroll()
height: Val::Percent(50.0),
// align_self: AlignSelf::Stretch,
overflow: Overflow::scroll(),
..default()
},
BackgroundColor(RED.into()),
))
.with_children(|parent| {
// List items
(0..250).for_each(|i| {
parent.spawn(Text(format!("Item {i}")));
});
})
.observe(scroll);
}
fn setup_nav_tree(mut commands: Commands) {
// Nav Tree
commands
.spawn((
Text::new("+"),
Node {
align_self: AlignSelf::Start,
justify_self: JustifySelf::End,
min_width: Val::Px(25.0),
min_height: Val::Px(25.0),
align_content: AlignContent::Center,
justify_content: JustifyContent::Center,
..default()
},
BackgroundColor(RED.into()),
))
.with_children(|parent| {
parent
.spawn((
Name::new("Buttons"),
NavState::default(),
Node {
position_type: PositionType::Absolute,
flex_direction: FlexDirection::Column,
right: Val::Percent(100.0),
width: Val::Auto,
..default()
},
Visibility::Hidden,
BackgroundColor(ORANGE.into()),
))
.with_children(|parent| {
(0..10).for_each(|_| {
let title: String = lipsum_title_with_rng(thread_rng())
.split_whitespace()
.take(2)
.intersperse(" ")
.collect();
parent.spawn((
Text::new(title),
Node {
width: Val::Auto,
margin: UiRect::all(Val::Px(5.0)),
..default()
},
BackgroundColor(ORANGE_RED.into()),
Button,
));
});
})
.with_children(|parent| {
parent.spawn((
Name::new("Preview"),
NavState::default(),
Text::new(lipsum_with_rng(thread_rng(), 50)),
Node {
position_type: PositionType::Absolute,
right: Val::Percent(100.0),
..default()
},
BackgroundColor(YELLOW.into()),
Visibility::Hidden,
));
});
});
}
// When you pointer over the '+' make the entire menu visible
fn hide_menu(mut nodes: Query<(Entity, &mut Visibility, &NavState), Changed<NavState>>) {
nodes.iter_mut().for_each(|(e, mut v, n)| {
*v = match n {
NavState::Open => Visibility::Inherited,
NavState::Closed => Visibility::Hidden,
};
});
}
// When you pointer goes off of the '+' or any of it's children make the entire menu invisible
fn control_menu(
mut over_events: EventReader<Pointer<Over>>,
mut out_events: EventReader<Pointer<Out>>,
children: Query<&ChildOf>,
parents: Query<&Children>,
mut nav: Query<&mut NavState>,
hover_map: Res<HoverMap>,
) {
over_events.read().for_each(|over| {
let root = children.root_ancestor(over.target);
parents.iter_descendants(root).for_each(|child| {
if let Ok(mut n) = nav.get_mut(child) {
*n = NavState::Open;
}
});
});
// Gives us the entiies covered by the HoverMap
let is_hovered: Vec<&Entity> = hover_map
.iter()
.flat_map(|(_, submap)| {
let x: Vec<&Entity> = submap.iter().map(|(node, _)| node).collect();
x
})
.collect();
// For all pointer out events
out_events.read().for_each(|out| {
// If a relative of out.target is hovered, do nothing
// Otherwise set to closed
let root = children.root_ancestor(out.target);
let tree_still_hovered = parents
.iter_descendants(root)
.any(|child| is_hovered.contains(&&child));
if !tree_still_hovered {
parents.iter_descendants(root).for_each(|child| {
if let Ok(mut n) = nav.get_mut(child) {
*n = NavState::Closed;
}
})
}
})
}