From cd9daa38c56af4f6a4bd57fc151e29f5be6c7e14 Mon Sep 17 00:00:00 2001 From: Elijah Voigt Date: Mon, 21 Jul 2025 11:10:00 -0700 Subject: [PATCH] The example works! yipee! --- Cargo.lock | 2 + Cargo.toml | 4 +- examples/ui.rs | 112 ++++++++++++++++++++++++++++++++++++++++++++----- src/debug.rs | 11 +---- src/ui.rs | 20 ++++++--- 5 files changed, 124 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3cc04c4..1f1a78a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2226,7 +2226,9 @@ dependencies = [ "bevy", "bevy_rapier3d", "chrono", + "itertools 0.13.0", "lipsum", + "rand", "serde", "thiserror 2.0.12", "walkdir", diff --git a/Cargo.toml b/Cargo.toml index 01f8fe5..fac453a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,9 @@ features = ["wayland", "dynamic_linking"] [dev-dependencies] lipsum = "*" +rand = "*" +itertools = "*" [build-dependencies] -chrono = "*" walkdir = "*" +chrono = "*" diff --git a/examples/ui.rs b/examples/ui.rs index ec1ba88..b2357a2 100644 --- a/examples/ui.rs +++ b/examples/ui.rs @@ -1,11 +1,20 @@ +#![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(Startup, (setup_list, setup_nav_tree)) + .add_systems(Update, (hide_menu, control_menu.run_if(on_event::>.or(on_event::>)))); app.run(); } @@ -36,12 +45,14 @@ fn setup_nav_tree(mut commands: Commands) { // Nav Tree commands .spawn(( + Text::new("+"), Node { - position_type: PositionType::Absolute, - right: Val::Px(0.0), - top: Val::Percent(50.0), + 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()), @@ -49,26 +60,107 @@ fn setup_nav_tree(mut commands: Commands) { .with_children(|parent| { parent .spawn(( + Name::new("Buttons"), + NavState::default(), Node { position_type: PositionType::Absolute, - right: Val::Px(25.0), - min_width: Val::Px(25.0), - min_height: Val::Px(25.0), + 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::Px(25.0), - min_width: Val::Px(25.0), - min_height: Val::Px(25.0), + 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>, +) { + 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>, + mut out_events: EventReader>, + children: Query<&ChildOf>, + parents: Query<&Children>, + mut nav: Query<&mut NavState>, + hover_map: Res, +) { + 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; + } + }) + } + }) +} diff --git a/src/debug.rs b/src/debug.rs index 7fc68b8..c3c5d43 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -309,11 +309,7 @@ impl Display for Fps { } } -fn track_fps( - time: Res