Ui example with nav open/close

main
Elijah Voigt 3 months ago
parent c349ce08d0
commit 9f307281f4

@ -5,13 +5,12 @@ use games::*;
fn main() { fn main() {
let mut app = App::new(); let mut app = App::new();
app.add_plugins(BaseGamePlugin::default()) app.add_plugins(BaseGamePlugin::default())
.add_systems(Startup, setup); .add_systems(Startup, (setup_list, setup_nav_tree))
.add_systems(Update, run_tree.run_if(any_component_changed::<NavTree>));
app.run(); app.run();
} }
const LINE_HEIGHT: f32 = 21.; fn setup_list(mut commands: Commands) {
fn setup(mut commands: Commands) {
// Scrolling list // Scrolling list
commands commands
.spawn(( .spawn((
@ -23,7 +22,7 @@ fn setup(mut commands: Commands) {
overflow: Overflow::scroll(), overflow: Overflow::scroll(),
..default() ..default()
}, },
BackgroundColor(Color::srgb(0.10, 0.10, 0.10)), BackgroundColor(RED.into()),
)) ))
.with_children(|parent| { .with_children(|parent| {
// List items // List items
@ -34,20 +33,69 @@ fn setup(mut commands: Commands) {
.observe(scroll); .observe(scroll);
} }
/// Updates the scroll position of scrollable nodes in response to mouse input fn setup_nav_tree(mut commands: Commands) {
pub fn scroll( // Nav Tree
trigger: Trigger<Pointer<Scroll>>, commands
mut scrollers: Query<&mut ScrollPosition>, .spawn((
) { Node {
let Pointer { event: Scroll { unit, x, y, .. }, .. } = trigger.event(); position_type: PositionType::Absolute,
right: Val::Px(0.0),
let (dx, dy) = match unit { width: Val::Px(100.0),
MouseScrollUnit::Line => (x * LINE_HEIGHT, y * LINE_HEIGHT), height: Val::Px(100.0),
MouseScrollUnit::Pixel => (x * 1., y * 1.), ..default()
}; },
BackgroundColor(RED.into()),
NavTree::Closed,
))
.with_children(|parent| {
parent
.spawn((
Node {
position_type: PositionType::Absolute,
right: Val::Px(100.0),
width: Val::Px(100.0),
height: Val::Px(100.0),
..default()
},
BackgroundColor(ORANGE.into()),
NavTree::Closed,
))
.with_children(|parent| {
parent.spawn((
Node {
position_type: PositionType::Absolute,
right: Val::Px(100.0),
width: Val::Px(100.0),
height: Val::Px(100.0),
..default()
},
BackgroundColor(YELLOW.into()),
NavTree::Closed,
));
});
});
}
if let Ok(mut pos) = scrollers.get_mut(trigger.target()) { fn run_tree(
pos.offset_x -= dx; q: Query<(Entity, &NavTree), Changed<NavTree>>,
pos.offset_y -= dy; children: Query<&Children>,
} mut query: Query<(&ChildOf, &mut Visibility)>,
) {
q.iter().for_each(|(e, nav)| {
// todo!("Clean this code up please!");
match nav {
NavTree::Open => children.iter_descendants(e).for_each(|child| {
let (co, mut v) = query.get_mut(child).unwrap();
if ChildOf(e) == *co {
*v = Visibility::Inherited;
}
}),
NavTree::Closed => children.iter_descendants(e).for_each(|child| {
let (co, mut v) = query.get_mut(child).unwrap();
if ChildOf(e) == *co {
*v = Visibility::Hidden;
}
}),
}
});
} }

@ -4,7 +4,10 @@ pub(crate) struct BaseUiPlugin;
impl Plugin for BaseUiPlugin { impl Plugin for BaseUiPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
// Nope! app.add_systems(
Update,
nav_tree.run_if(on_event::<Pointer<Over>>.or(on_event::<Pointer<Out>>)),
);
} }
} }
@ -23,3 +26,88 @@ pub fn sync_resource_to_ui<R: Resource + Default + Display>(
t.0 = format!("{}", *r); t.0 = format!("{}", *r);
}); });
} }
/// Updates the scroll position of scrollable nodes in response to mouse input
pub fn scroll(trigger: Trigger<Pointer<Scroll>>, mut scrollers: Query<&mut ScrollPosition>) {
let Pointer {
event: Scroll { unit, x, y, .. },
..
} = trigger.event();
let (dx, dy) = match unit {
MouseScrollUnit::Line => (x * 16.0, y * 16.0),
MouseScrollUnit::Pixel => (x * 1., y * 1.),
};
if let Ok(mut pos) = scrollers.get_mut(trigger.target()) {
pos.offset_x -= dx;
pos.offset_y -= dy;
}
}
#[derive(Component, Debug)]
pub enum NavTree {
Open,
Closed,
}
impl NavTree {
fn open(&mut self) {
*self = NavTree::Open;
}
fn close(&mut self) {
*self = NavTree::Closed;
}
}
pub fn nav_tree(
mut over: EventReader<Pointer<Over>>,
mut out: EventReader<Pointer<Out>>,
mut query: Query<&mut NavTree>,
parents: Query<&Children>,
children: Query<&ChildOf>,
) {
// Get the entity that was just moused over (if any)
// Open this nav tree
let opens: Vec<Entity> = over
.read()
.filter_map(|over| {
query
.get_mut(over.target)
.map(|mut n| {
n.open();
over.target
})
.ok()
})
.collect();
// Close this nav tree if it is moused off
// and none of it's descendants were moused over
out.read().for_each(|out| {
if let Ok(mut nav) = query.get_mut(out.target) {
// Check if any of the opened entities is a child of this UI element
let mouse_over_child = parents
.iter_descendants(out.target)
.any(|child| opens.contains(&child));
if !mouse_over_child {
// Mouse still on nav tree
nav.close();
// Check if the mouse is over any ancestors
let mouse_over_parent = children
.iter_ancestors(out.target)
.any(|parent| opens.contains(&parent));
if !mouse_over_parent {
info!("Mouse is not over this tree");
children.iter_ancestors(out.target).for_each(|e| {
query.get_mut(e).iter_mut().for_each(|nav| {
nav.close();
});
});
}
}
}
});
}

Loading…
Cancel
Save