Compare commits

..

No commits in common. 'a4dc81b4fb04c024cdde6834130e038f15108e81' and 'ca3db16a48da6cb3e754469964246ded6d836dda' have entirely different histories.

2
Cargo.lock generated

@ -2226,9 +2226,7 @@ dependencies = [
"bevy", "bevy",
"bevy_rapier3d", "bevy_rapier3d",
"chrono", "chrono",
"itertools 0.13.0",
"lipsum", "lipsum",
"rand",
"serde", "serde",
"thiserror 2.0.12", "thiserror 2.0.12",
"walkdir", "walkdir",

@ -19,9 +19,7 @@ features = ["wayland", "dynamic_linking"]
[dev-dependencies] [dev-dependencies]
lipsum = "*" lipsum = "*"
rand = "*"
itertools = "*"
[build-dependencies] [build-dependencies]
walkdir = "*"
chrono = "*" chrono = "*"
walkdir = "*"

@ -1,23 +1,11 @@
#![feature(iter_intersperse)]
//! This example illustrates scrolling in Bevy UI. //! This example illustrates scrolling in Bevy UI.
use bevy::picking::hover::HoverMap;
use games::*; use games::*;
use lipsum::*;
use rand::*;
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_list, setup_nav_tree)) .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(); app.run();
} }
@ -48,14 +36,12 @@ fn setup_nav_tree(mut commands: Commands) {
// Nav Tree // Nav Tree
commands commands
.spawn(( .spawn((
Text::new("+"),
Node { Node {
align_self: AlignSelf::Start, position_type: PositionType::Absolute,
justify_self: JustifySelf::End, right: Val::Px(0.0),
top: Val::Percent(50.0),
min_width: Val::Px(25.0), min_width: Val::Px(25.0),
min_height: Val::Px(25.0), min_height: Val::Px(25.0),
align_content: AlignContent::Center,
justify_content: JustifyContent::Center,
..default() ..default()
}, },
BackgroundColor(RED.into()), BackgroundColor(RED.into()),
@ -63,106 +49,26 @@ fn setup_nav_tree(mut commands: Commands) {
.with_children(|parent| { .with_children(|parent| {
parent parent
.spawn(( .spawn((
Name::new("Buttons"),
NavState::default(),
Node { Node {
position_type: PositionType::Absolute, position_type: PositionType::Absolute,
flex_direction: FlexDirection::Column, right: Val::Px(25.0),
right: Val::Percent(100.0), min_width: Val::Px(25.0),
width: Val::Auto, min_height: Val::Px(25.0),
..default() ..default()
}, },
Visibility::Hidden,
BackgroundColor(ORANGE.into()), 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| { .with_children(|parent| {
parent.spawn(( parent.spawn((
Name::new("Preview"),
NavState::default(),
Text::new(lipsum_with_rng(thread_rng(), 50)),
Node { Node {
position_type: PositionType::Absolute, position_type: PositionType::Absolute,
right: Val::Percent(100.0), right: Val::Px(25.0),
min_width: Val::Px(25.0),
min_height: Val::Px(25.0),
..default() ..default()
}, },
BackgroundColor(YELLOW.into()), 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;
}
})
}
})
}

@ -1,11 +1,10 @@
#![allow(dead_code)] #![allow(dead_code)]
#![allow(clippy::type_complexity)] #![allow(clippy::type_complexity)]
#![allow(clippy::too_many_arguments)] #![allow(clippy::too_many_arguments)]
#![feature(trim_prefix_suffix)]
mod mono; mod mono;
use bevy::{picking::hover::HoverMap, platform::hash::RandomState}; use bevy::platform::hash::RandomState;
use games::*; use games::*;
use mono::*; use mono::*;
use std::hash::BuildHasher; use std::hash::BuildHasher;
@ -44,19 +43,18 @@ fn main() {
.run_if(on_event::<Pointer<Click>>), .run_if(on_event::<Pointer<Click>>),
spawn_debug_buttons.run_if(on_event::<AssetEvent<Monologue>>), spawn_debug_buttons.run_if(on_event::<AssetEvent<Monologue>>),
dialog_engine.run_if(on_event::<DialogEvent>), dialog_engine.run_if(on_event::<DialogEvent>),
mouse_wheel_scroll.run_if(on_event::<MouseWheel>),
auto_scroll.run_if(any_component_added::<DialogOption>), auto_scroll.run_if(any_component_added::<DialogOption>),
dialog_box_visibility.run_if(state_changed::<DialogState>), dialog_box_visibility.run_if(state_changed::<DialogState>),
monologue_asset_tooltip monologue_asset_tooltip
.run_if(on_event::<Pointer<Over>>.or(on_event::<Pointer<Out>>)), .run_if(on_event::<Pointer<Over>>.or(on_event::<Pointer<Out>>)),
scale_window.run_if(on_event::<WindowResized>), scale_window.run_if(on_event::<WindowResized>),
hide_menu.run_if(any_component_changed::<NavState>),
clear_monologue.run_if(any_component_changed::<NavState>),
control_menu.run_if(on_event::<Pointer<Over>>.or(on_event::<Pointer<Out>>)),
), ),
) )
.add_observer(add_dialog_option) .add_observer(add_dialog_option)
.add_observer(add_tree_monologue) .add_observer(add_tree_monologue)
.add_observer(remove_tree_monologue) .add_observer(remove_tree_monologue)
.add_observer(hide_monologue_preview)
.add_observer(populate_tree) .add_observer(populate_tree)
.add_observer(show_monologue_list) .add_observer(show_monologue_list)
.add_observer(hide_monologue_list) .add_observer(hide_monologue_list)
@ -103,7 +101,6 @@ fn init_ui(mut commands: Commands) {
..default() ..default()
}, },
)) ))
.observe(scroll)
.observe(hover_dialog_box_over) .observe(hover_dialog_box_over)
.observe(hover_dialog_box_out); .observe(hover_dialog_box_out);
} }
@ -118,62 +115,44 @@ struct MonologuesList;
struct MonologuePreview; struct MonologuePreview;
/// Panel for selecting which monologue tree to spawn /// Panel for selecting which monologue tree to spawn
/// TODO: When monologue is loaded, add a button for it in this
/// TODO: When mouse over button, preview it in the "MonologuePreview" box
fn init_debug_ui(mut commands: Commands) { fn init_debug_ui(mut commands: Commands) {
let button = commands commands
.spawn(( .spawn((
Name::new("Monologue Assignment Menu"),
Text::new("+ Monologue"),
Node { Node {
align_self: AlignSelf::Start, max_height: Val::Percent(90.0),
align_self: AlignSelf::Center,
justify_self: JustifySelf::Start, justify_self: JustifySelf::Start,
min_width: Val::Px(25.0),
min_height: Val::Px(25.0),
..default() ..default()
}, },
BackgroundColor(RED.into()),
DebuggingState::On,
MonologuesContainer, MonologuesContainer,
GlobalZIndex(i32::MAX - 1),
BackgroundColor(PINK.into()),
DebuggingState::On,
)) ))
.id(); .with_children(|parent| {
commands
.spawn((
NavParent(button),
NavState::default(),
Name::new("Container"),
Node {
flex_direction: FlexDirection::Row,
top: Val::Px(25.0),
height: Val::Percent(90.0),
..default()
},
BackgroundColor(BLACK.into()),
)).with_children(|parent| {
parent.spawn(( parent.spawn((
Name::new("Buttons"),
Node { Node {
flex_direction: FlexDirection::Column,
height: Val::Percent(100.0), height: Val::Percent(100.0),
flex_direction: FlexDirection::Column,
padding: UiRect::all(Val::Px(10.0)),
overflow: Overflow::scroll_y(), overflow: Overflow::scroll_y(),
..default() ..default()
}, },
ScrollPosition::default(), BackgroundColor(PINK.with_alpha(0.9).into()),
BackgroundColor(ORANGE.into()),
MonologuesList, MonologuesList,
)).observe(scroll); ));
parent.spawn(( parent.spawn((
Name::new("Preview"),
MonologuePreview,
NavParent(button),
NavState::default(),
Node { Node {
height: Val::Percent(100.0),
flex_direction: FlexDirection::Column, flex_direction: FlexDirection::Column,
padding: UiRect::all(Val::Px(10.0)), padding: UiRect::all(Val::Px(10.0)),
overflow: Overflow::scroll_y(), overflow: Overflow::scroll_y(),
max_width: Val::Percent(50.0),
..default() ..default()
}, },
BackgroundColor(ORANGE_RED.into()), BackgroundColor(ORANGE.with_alpha(0.9).into()),
MonologuePreview,
)); ));
}); });
} }
@ -207,6 +186,22 @@ fn position_camera(mut query: Query<&mut Transform, (With<Camera>, With<Camera3d
}) })
} }
/// Allows user to scroll (with mouse whell) through old dialog
fn mouse_wheel_scroll(
mut events: EventReader<MouseWheel>,
mut scroll_position: Query<&mut ScrollPosition>,
) {
events.read().for_each(|MouseWheel { unit, y, .. }| {
let offset_y = match unit {
MouseScrollUnit::Line => 10.0 * y,
MouseScrollUnit::Pixel => 1.0 * y,
};
scroll_position
.iter_mut()
.for_each(|mut pos| pos.offset_y += offset_y);
});
}
/// Automatically scrolls dialog when a new batch of options are added /// Automatically scrolls dialog when a new batch of options are added
fn auto_scroll( fn auto_scroll(
added: Query<Entity, Added<DialogOption>>, added: Query<Entity, Added<DialogOption>>,
@ -567,7 +562,7 @@ fn spawn_debug_buttons(
parent parent
.spawn(( .spawn((
Button, Button,
Text::new(handle.path().unwrap().to_string().trim_prefix("trees/").trim_suffix(".mono")), Text::new(handle.path().unwrap().to_string()),
TextLayout::new(JustifyText::Left, LineBreak::WordBoundary), TextLayout::new(JustifyText::Left, LineBreak::WordBoundary),
TreeMonologue(handle.clone()), TreeMonologue(handle.clone()),
MonologuesList, MonologuesList,
@ -578,6 +573,9 @@ fn spawn_debug_buttons(
.observe(toggle_debug_button_color_over) .observe(toggle_debug_button_color_over)
.observe(toggle_debug_button_color_out); .observe(toggle_debug_button_color_out);
}); });
// Spawn a tree too to make the game feel like it's doing something at startup
commands.spawn((Tree, TreeMonologue(handle.clone())));
} }
}); });
} }
@ -609,29 +607,22 @@ fn preview_monologue(
) { ) {
// Get the handle for this button's monologuie // Get the handle for this button's monologuie
if let Ok(TreeMonologue(handle)) = tree_monologue.get(trigger.target()) { if let Ok(TreeMonologue(handle)) = tree_monologue.get(trigger.target()) {
// Get the monologue data // Get the monologue data
if let Some(monologue) = monologues.get(handle) { if let Some(monologue) = monologues.get(handle) {
commands.entity(*container).despawn_related::<Children>(); commands.entity(*container).despawn_related::<Children>();
// Spawn the monologue // Spawn the monologue
commands.entity(*container).with_children(|parent| {
let mut i = 0; let mut i = 0;
commands.entity(*container).with_children(|parent| {
while let Some(batch) = monologue.get(i) { while let Some(batch) = monologue.get(i) {
parent.spawn((Text::new("---"), MonologuePreview)); parent.spawn((Text::new("---"), MonologuePreview));
debug!("---");
for (n, item) in batch.lines.iter().enumerate() { for (n, item) in batch.lines.iter().enumerate() {
parent.spawn((Text::new(format!("{i}.{n}: {item}")), MonologuePreview)); parent.spawn((Text::new(format!("{i}.{n}: {item}")), MonologuePreview));
debug!("{i}.{n}: {item}");
} }
// TODO: Just implement iter_batches or something // TODO: Just implement iter_batches or something
i += 1; i += 1;
} }
parent.spawn((Text::new("---"), MonologuePreview)); parent.spawn((Text::new("---"), MonologuePreview));
debug!("---");
}); });
} }
} }
@ -723,6 +714,26 @@ fn populate_tree(
.observe(drag_tree); .observe(drag_tree);
} }
fn hide_monologue_preview(
trigger: Trigger<Pointer<Over>>,
preview: Single<Entity, (With<MonologuePreview>, Without<Button>, Without<Text>)>,
query: Query<
Entity,
Or<(
With<MonologuePreview>,
With<MonologuesList>,
With<MonologuesContainer>,
With<Window>,
)>,
>,
mut commands: Commands,
) {
if !query.contains(trigger.target()) {
info!("Processing over: {:?}", trigger.target());
commands.entity(*preview).despawn_related::<Children>();
}
}
fn drag_tree( fn drag_tree(
trigger: Trigger<Pointer<Drag>>, trigger: Trigger<Pointer<Drag>>,
state: Res<State<DebuggingState>>, state: Res<State<DebuggingState>>,
@ -746,78 +757,3 @@ fn drag_tree(
} }
} }
} }
// 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,
};
});
}
fn clear_monologue(
mut nodes: Query<(Entity, &NavState), (Changed<NavState>, With<MonologuePreview>, Without<Text>)>,
mut commands: Commands,
) {
nodes.iter_mut().for_each(|(e, n)| {
if matches!(n, NavState::Closed) {
commands.entity(e).despawn_related::<Children>();
}
});
}
// 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>>,
nav_children: Query<&NavParent>,
children: Query<&ChildOf>,
nav_parents: Query<&NavChildren>,
parents: Query<&Children>,
mut nav: Query<&mut NavState>,
hover_map: Res<HoverMap>,
) {
over_events.read().for_each(|over| {
let root = nav_children.root_ancestor(over.target);
nav_parents.iter_descendants(root).for_each(|child| {
if let Ok(mut n) = nav.get_mut(child) {
*n = NavState::Open;
}
});
});
// Gives us the enities 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 {
if let Ok(mut n) = nav.get_mut(root) {
*n = NavState::Closed;
}
parents.iter_descendants(root).for_each(|child| {
if let Ok(mut n) = nav.get_mut(child) {
*n = NavState::Closed;
}
})
}
})
}

@ -132,7 +132,6 @@ fn init_debug_ui(mut commands: Commands) {
commands.spawn(( commands.spawn((
DebuggingState::On, DebuggingState::On,
Text("Tooltip Placeholder".into()), Text("Tooltip Placeholder".into()),
Pickable::IGNORE,
TextColor(WHITE.into()), TextColor(WHITE.into()),
SyncResource::<ToolTip>::default(), SyncResource::<ToolTip>::default(),
BackgroundColor(BLACK.with_alpha(0.9).into()), BackgroundColor(BLACK.with_alpha(0.9).into()),
@ -310,7 +309,11 @@ impl Display for Fps {
} }
} }
fn track_fps(time: Res<Time>, mut fps: ResMut<Fps>, mut history: Local<VecDeque<f32>>) { fn track_fps(
time: Res<Time>,
mut fps: ResMut<Fps>,
mut history: Local<VecDeque<f32>>,
) {
// Get the time to render the last frame // Get the time to render the last frame
let d = time.delta_secs(); let d = time.delta_secs();
@ -335,6 +338,9 @@ impl Display for EntityCount {
} }
} }
fn track_entity_count(query: Query<Entity>, mut count: ResMut<EntityCount>) { fn track_entity_count(
query: Query<Entity>,
mut count: ResMut<EntityCount>,
) {
count.0 = query.iter().len(); count.0 = query.iter().len();
} }

@ -42,21 +42,11 @@ pub fn scroll(trigger: Trigger<Pointer<Scroll>>, mut scrollers: Query<&mut Scrol
} }
} }
/// Track who your parent is for navigation in the UI
/// Think the file menu is a child of the "file" button
#[derive(Component, Debug)] #[derive(Component, Debug)]
#[relationship(relationship_target = NavChildren)] #[relationship(relationship_target = NavParent)]
pub struct NavParent(pub Entity); pub(crate) struct NavChild(Entity);
/// Back-relationship for nav-parent
#[derive(Component, Debug)] #[derive(Component, Debug)]
#[relationship_target(relationship = NavParent)] #[relationship_target(relationship = NavChild)]
pub struct NavChildren(Vec<Entity>); pub(crate) struct NavParent(Vec<Entity>);
/// Track if a navigation [sub]tree is open or closed
#[derive(Component, Debug, Default)]
pub enum NavState {
Open,
#[default]
Closed,
}

Loading…
Cancel
Save