diff --git a/examples/ui.rs b/examples/ui.rs index 7412578..a62faa6 100644 --- a/examples/ui.rs +++ b/examples/ui.rs @@ -5,13 +5,12 @@ use games::*; fn main() { let mut app = App::new(); 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::)); app.run(); } -const LINE_HEIGHT: f32 = 21.; - -fn setup(mut commands: Commands) { +fn setup_list(mut commands: Commands) { // Scrolling list commands .spawn(( @@ -23,7 +22,7 @@ fn setup(mut commands: Commands) { overflow: Overflow::scroll(), ..default() }, - BackgroundColor(Color::srgb(0.10, 0.10, 0.10)), + BackgroundColor(RED.into()), )) .with_children(|parent| { // List items @@ -34,20 +33,69 @@ fn setup(mut commands: Commands) { .observe(scroll); } -/// Updates the scroll position of scrollable nodes in response to mouse input -pub fn scroll( - trigger: Trigger>, - mut scrollers: Query<&mut ScrollPosition>, -) { - let Pointer { event: Scroll { unit, x, y, .. }, .. } = trigger.event(); - - let (dx, dy) = match unit { - MouseScrollUnit::Line => (x * LINE_HEIGHT, y * LINE_HEIGHT), - MouseScrollUnit::Pixel => (x * 1., y * 1.), - }; +fn setup_nav_tree(mut commands: Commands) { + // Nav Tree + commands + .spawn(( + Node { + position_type: PositionType::Absolute, + right: Val::Px(0.0), + width: Val::Px(100.0), + height: Val::Px(100.0), + ..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()) { - pos.offset_x -= dx; - pos.offset_y -= dy; - } +fn run_tree( + q: Query<(Entity, &NavTree), Changed>, + 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; + } + }), + } + }); } diff --git a/src/ui.rs b/src/ui.rs index 0049660..7f20d8e 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -4,7 +4,10 @@ pub(crate) struct BaseUiPlugin; impl Plugin for BaseUiPlugin { fn build(&self, app: &mut App) { - // Nope! + app.add_systems( + Update, + nav_tree.run_if(on_event::>.or(on_event::>)), + ); } } @@ -23,3 +26,88 @@ pub fn sync_resource_to_ui( t.0 = format!("{}", *r); }); } + +/// Updates the scroll position of scrollable nodes in response to mouse input +pub fn scroll(trigger: Trigger>, 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>, + mut out: EventReader>, + 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 = 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(); + }); + }); + } + } + } + }); +}