From 35cc3d1f9104760f5f5a72ecacbff407af599209 Mon Sep 17 00:00:00 2001 From: Elijah Voigt Date: Tue, 22 Jul 2025 12:24:09 -0700 Subject: [PATCH] Almost there with basic UI -- just need to fix a text bug --- examples/ui.rs | 55 +++++++++------- src/bin/trees/main.rs | 131 +++++++++++++++++++------------------ src/ui.rs | 149 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 248 insertions(+), 87 deletions(-) diff --git a/examples/ui.rs b/examples/ui.rs index f04af3c..3d3235b 100644 --- a/examples/ui.rs +++ b/examples/ui.rs @@ -10,7 +10,7 @@ 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, setup_button)) .add_systems( Update, ( @@ -24,18 +24,16 @@ fn main() { 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()), - )) + .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() + },)) .with_children(|parent| { + parent.spawn(Text("This srolls".into())); // List items (0..250).for_each(|i| { parent.spawn(Text(format!("Item {i}"))); @@ -44,11 +42,23 @@ fn setup_list(mut commands: Commands) { .observe(scroll); } +fn setup_button(mut commands: Commands) { + // Spawn a random button at the center of the screen for testing + commands.spawn(( + Node { + align_self: AlignSelf::Center, + justify_self: JustifySelf::Center, + ..default() + }, + Button, + children![Text("Hello World".into()),], + )); +} + fn setup_nav_tree(mut commands: Commands) { // Nav Tree commands .spawn(( - Text::new("+"), Node { align_self: AlignSelf::Start, justify_self: JustifySelf::End, @@ -58,7 +68,8 @@ fn setup_nav_tree(mut commands: Commands) { justify_content: JustifyContent::Center, ..default() }, - BackgroundColor(RED.into()), + Button, + children![Text::new("+Menu"),], )) .with_children(|parent| { parent @@ -72,8 +83,6 @@ fn setup_nav_tree(mut commands: Commands) { width: Val::Auto, ..default() }, - Visibility::Hidden, - BackgroundColor(ORANGE.into()), )) .with_children(|parent| { (0..10).for_each(|_| { @@ -83,13 +92,11 @@ fn setup_nav_tree(mut commands: Commands) { .intersperse(" ") .collect(); parent.spawn(( - Text::new(title), + children![Text::new(title),], Node { width: Val::Auto, - margin: UiRect::all(Val::Px(5.0)), ..default() }, - BackgroundColor(ORANGE_RED.into()), Button, )); }); @@ -98,22 +105,22 @@ fn setup_nav_tree(mut commands: Commands) { parent.spawn(( Name::new("Preview"), NavState::default(), - Text::new(lipsum_with_rng(thread_rng(), 50)), + children![Text::new(lipsum_with_rng(thread_rng(), 50)),], Node { position_type: PositionType::Absolute, right: Val::Percent(100.0), + width: Val::Percent(100.0), + max_height: 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)| { +fn hide_menu(mut nodes: Query<(&mut Visibility, &NavState), Changed>) { + nodes.iter_mut().for_each(|(mut v, n)| { *v = match n { NavState::Open => Visibility::Inherited, NavState::Closed => Visibility::Hidden, diff --git a/src/bin/trees/main.rs b/src/bin/trees/main.rs index ba525bb..0c8c38b 100644 --- a/src/bin/trees/main.rs +++ b/src/bin/trees/main.rs @@ -122,21 +122,23 @@ struct MonologuePreview; /// Panel for selecting which monologue tree to spawn fn init_debug_ui(mut commands: Commands) { - commands.spawn(( - Name::new("Tree Planter"), - Text::new("+Tree"), - Node { - top: Val::Px(0.0), - left: Val::Px(0.0), - min_width: Val::Px(25.0), - min_height: Val::Px(25.0), - ..default() - }, - BackgroundColor(RED.into()), - DebuggingState::On, - MonologuesContainer, - Button, - )).observe(spawn_tree); + commands + .spawn(( + Name::new("Tree Planter"), + Text::new("+Tree"), + Node { + top: Val::Px(0.0), + left: Val::Px(0.0), + min_width: Val::Px(25.0), + min_height: Val::Px(25.0), + ..default() + }, + BackgroundColor(RED.into()), + DebuggingState::On, + MonologuesContainer, + Button, + )) + .observe(spawn_tree); let monologue_button = commands .spawn(( @@ -167,20 +169,23 @@ fn init_debug_ui(mut commands: Commands) { ..default() }, BackgroundColor(BLACK.into()), - )).with_children(|parent| { - parent.spawn(( - Name::new("Buttons"), - Node { - flex_direction: FlexDirection::Column, - height: Val::Percent(100.0), - width: Val::Percent(40.0), - overflow: Overflow::scroll_y(), - ..default() - }, - ScrollPosition::default(), - BackgroundColor(ORANGE.into()), - MonologuesList, - )).observe(scroll); + )) + .with_children(|parent| { + parent + .spawn(( + Name::new("Buttons"), + Node { + flex_direction: FlexDirection::Column, + height: Val::Percent(100.0), + width: Val::Percent(40.0), + overflow: Overflow::scroll_y(), + ..default() + }, + ScrollPosition::default(), + BackgroundColor(ORANGE.into()), + MonologuesList, + )) + .observe(scroll); parent.spawn(( Name::new("Preview"), @@ -196,7 +201,7 @@ fn init_debug_ui(mut commands: Commands) { }, BackgroundColor(ORANGE_RED.into()), )); - }); + }); } fn hover_dialog_box_over( @@ -271,16 +276,16 @@ enum DialogState { None, } -/// Start dialog, will expand later with handle to monologue asset +/// Start dialog fn start_dialog( mut click_events: EventReader>, mut dialog_events: EventWriter, query: Query<&TreeMonologue, With>, ) { click_events.read().for_each(|event| { - debug!("Click event detected"); + info!("Click event detected in start dialog systme"); if let Ok(TreeMonologue(handle)) = query.get(event.target) { - debug!("Tree Monologue received, sending start dialog event"); + info!("Tree Monologue received, sending start dialog event"); dialog_events.write(DialogEvent::Start(event.target, handle.clone())); dialog_events.write(DialogEvent::NextBatch); } @@ -483,19 +488,15 @@ fn monologue_asset_tooltip( over_events .read() .filter_map(|Pointer { target, .. }| trees.contains(*target).then_some(*target)) - .for_each(|e| { - match trees.get(e) { - Ok((_tree, Some(TreeMonologue(handle)))) => { - match handle.path() { - Some(p) => tooltip.insert("Script", format!("{p}")), - None => tooltip.insert("Script", "A".into()), - } - }, - Ok((_tree, None)) => { - tooltip.insert("Script", "N/A".into()); - }, - _ => () + .for_each(|e| match trees.get(e) { + Ok((_tree, Some(TreeMonologue(handle)))) => match handle.path() { + Some(p) => tooltip.insert("Script", format!("{p}")), + None => tooltip.insert("Script", "A".into()), + }, + Ok((_tree, None)) => { + tooltip.insert("Script", "N/A".into()); } + _ => (), }); } @@ -594,7 +595,14 @@ fn spawn_debug_buttons( parent .spawn(( Button, - Text::new(handle.path().unwrap().to_string().trim_prefix("trees/").trim_suffix(".mono")), + Text::new( + handle + .path() + .unwrap() + .to_string() + .trim_prefix("trees/") + .trim_suffix(".mono"), + ), TextLayout::new(JustifyText::Left, LineBreak::WordBoundary), TreeMonologue(handle.clone()), MonologuesList, @@ -636,7 +644,6 @@ fn preview_monologue( ) { // Get the handle for this button's monologuie if let Ok(TreeMonologue(handle)) = tree_monologue.get(trigger.target()) { - // Get the monologue data if let Some(monologue) = monologues.get(handle) { commands.entity(*container).despawn_related::(); @@ -738,7 +745,7 @@ fn handle_plant_tree( }); } -#[derive(Event)] +#[derive(Event, Debug)] struct AssignMonologue(Handle); /// Assign the given monologue to a tree @@ -749,11 +756,14 @@ fn assign_monologue_to_tree( ) { events.read().for_each(|event| { // Get a valid tree to assign an entity to - let tree = query.iter().next().unwrap(); - // Create the TreeMonologue component - let monologue = TreeMonologue(event.0.clone()); - // Insert the component to the entity - commands.entity(tree).insert(monologue); + if let Some(tree) = query.iter().next() { + // Create the TreeMonologue component + let monologue = TreeMonologue(event.0.clone()); + // Insert the component to the entity + commands.entity(tree).insert(monologue); + } else { + error!("No trees avaliable for {:?}", event.0.path().unwrap()); + } }) } @@ -782,9 +792,7 @@ fn drag_tree( } // When you pointer over the '+' make the entire menu visible -fn hide_menu( - mut nodes: Query<(Entity, &mut Visibility, &NavState), Changed> -) { +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, @@ -794,7 +802,10 @@ fn hide_menu( } fn clear_monologue( - mut nodes: Query<(Entity, &NavState), (Changed, With, Without)>, + mut nodes: Query< + (Entity, &NavState), + (Changed, With, Without), + >, mut commands: Commands, ) { nodes.iter_mut().for_each(|(e, n)| { @@ -843,7 +854,6 @@ fn control_menu( .iter_descendants(root) .any(|child| is_hovered.contains(&&child)); if !tree_still_hovered { - if let Ok(mut n) = nav.get_mut(root) { *n = NavState::Closed; } @@ -857,10 +867,7 @@ fn control_menu( } /// Observer for the "Plant a new tree" button in the debug UI -fn spawn_tree( - _trigger: Trigger>, - mut events: EventWriter, -) { +fn spawn_tree(_trigger: Trigger>, mut events: EventWriter) { events.write(PlantTree); } diff --git a/src/ui.rs b/src/ui.rs index 42678a7..2f5e01a 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -4,10 +4,157 @@ pub(crate) struct BaseUiPlugin; impl Plugin for BaseUiPlugin { fn build(&self, app: &mut App) { - // TODO + app.init_resource::