Almost there with basic UI -- just need to fix a text bug

main
Elijah Voigt 3 months ago
parent d17f5e70d1
commit 35cc3d1f91

@ -10,7 +10,7 @@ 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, setup_button))
.add_systems( .add_systems(
Update, Update,
( (
@ -24,18 +24,16 @@ fn main() {
fn setup_list(mut commands: Commands) { fn setup_list(mut commands: Commands) {
// Scrolling list // Scrolling list
commands commands
.spawn(( .spawn((Node {
Node {
flex_direction: FlexDirection::Column, flex_direction: FlexDirection::Column,
// If height is not set, we need both align_self: Stetch and overflow: scroll() // If height is not set, we need both align_self: Stetch and overflow: scroll()
height: Val::Percent(50.0), height: Val::Percent(50.0),
// align_self: AlignSelf::Stretch, // align_self: AlignSelf::Stretch,
overflow: Overflow::scroll(), overflow: Overflow::scroll(),
..default() ..default()
}, },))
BackgroundColor(RED.into()),
))
.with_children(|parent| { .with_children(|parent| {
parent.spawn(Text("This srolls".into()));
// List items // List items
(0..250).for_each(|i| { (0..250).for_each(|i| {
parent.spawn(Text(format!("Item {i}"))); parent.spawn(Text(format!("Item {i}")));
@ -44,11 +42,23 @@ fn setup_list(mut commands: Commands) {
.observe(scroll); .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) { fn setup_nav_tree(mut commands: Commands) {
// Nav Tree // Nav Tree
commands commands
.spawn(( .spawn((
Text::new("+"),
Node { Node {
align_self: AlignSelf::Start, align_self: AlignSelf::Start,
justify_self: JustifySelf::End, justify_self: JustifySelf::End,
@ -58,7 +68,8 @@ fn setup_nav_tree(mut commands: Commands) {
justify_content: JustifyContent::Center, justify_content: JustifyContent::Center,
..default() ..default()
}, },
BackgroundColor(RED.into()), Button,
children![Text::new("+Menu"),],
)) ))
.with_children(|parent| { .with_children(|parent| {
parent parent
@ -72,8 +83,6 @@ fn setup_nav_tree(mut commands: Commands) {
width: Val::Auto, width: Val::Auto,
..default() ..default()
}, },
Visibility::Hidden,
BackgroundColor(ORANGE.into()),
)) ))
.with_children(|parent| { .with_children(|parent| {
(0..10).for_each(|_| { (0..10).for_each(|_| {
@ -83,13 +92,11 @@ fn setup_nav_tree(mut commands: Commands) {
.intersperse(" ") .intersperse(" ")
.collect(); .collect();
parent.spawn(( parent.spawn((
Text::new(title), children![Text::new(title),],
Node { Node {
width: Val::Auto, width: Val::Auto,
margin: UiRect::all(Val::Px(5.0)),
..default() ..default()
}, },
BackgroundColor(ORANGE_RED.into()),
Button, Button,
)); ));
}); });
@ -98,22 +105,22 @@ fn setup_nav_tree(mut commands: Commands) {
parent.spawn(( parent.spawn((
Name::new("Preview"), Name::new("Preview"),
NavState::default(), NavState::default(),
Text::new(lipsum_with_rng(thread_rng(), 50)), children![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::Percent(100.0),
width: Val::Percent(100.0),
max_height: Val::Percent(100.0),
..default() ..default()
}, },
BackgroundColor(YELLOW.into()),
Visibility::Hidden,
)); ));
}); });
}); });
} }
// When you pointer over the '+' make the entire menu visible // When you pointer over the '+' make the entire menu visible
fn hide_menu(mut nodes: Query<(Entity, &mut Visibility, &NavState), Changed<NavState>>) { fn hide_menu(mut nodes: Query<(&mut Visibility, &NavState), Changed<NavState>>) {
nodes.iter_mut().for_each(|(e, mut v, n)| { nodes.iter_mut().for_each(|(mut v, n)| {
*v = match n { *v = match n {
NavState::Open => Visibility::Inherited, NavState::Open => Visibility::Inherited,
NavState::Closed => Visibility::Hidden, NavState::Closed => Visibility::Hidden,

@ -122,7 +122,8 @@ struct MonologuePreview;
/// Panel for selecting which monologue tree to spawn /// Panel for selecting which monologue tree to spawn
fn init_debug_ui(mut commands: Commands) { fn init_debug_ui(mut commands: Commands) {
commands.spawn(( commands
.spawn((
Name::new("Tree Planter"), Name::new("Tree Planter"),
Text::new("+Tree"), Text::new("+Tree"),
Node { Node {
@ -136,7 +137,8 @@ fn init_debug_ui(mut commands: Commands) {
DebuggingState::On, DebuggingState::On,
MonologuesContainer, MonologuesContainer,
Button, Button,
)).observe(spawn_tree); ))
.observe(spawn_tree);
let monologue_button = commands let monologue_button = commands
.spawn(( .spawn((
@ -167,8 +169,10 @@ fn init_debug_ui(mut commands: Commands) {
..default() ..default()
}, },
BackgroundColor(BLACK.into()), BackgroundColor(BLACK.into()),
)).with_children(|parent| { ))
parent.spawn(( .with_children(|parent| {
parent
.spawn((
Name::new("Buttons"), Name::new("Buttons"),
Node { Node {
flex_direction: FlexDirection::Column, flex_direction: FlexDirection::Column,
@ -180,7 +184,8 @@ fn init_debug_ui(mut commands: Commands) {
ScrollPosition::default(), ScrollPosition::default(),
BackgroundColor(ORANGE.into()), BackgroundColor(ORANGE.into()),
MonologuesList, MonologuesList,
)).observe(scroll); ))
.observe(scroll);
parent.spawn(( parent.spawn((
Name::new("Preview"), Name::new("Preview"),
@ -271,16 +276,16 @@ enum DialogState {
None, None,
} }
/// Start dialog, will expand later with handle to monologue asset /// Start dialog
fn start_dialog( fn start_dialog(
mut click_events: EventReader<Pointer<Click>>, mut click_events: EventReader<Pointer<Click>>,
mut dialog_events: EventWriter<DialogEvent>, mut dialog_events: EventWriter<DialogEvent>,
query: Query<&TreeMonologue, With<Tree>>, query: Query<&TreeMonologue, With<Tree>>,
) { ) {
click_events.read().for_each(|event| { 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) { 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::Start(event.target, handle.clone()));
dialog_events.write(DialogEvent::NextBatch); dialog_events.write(DialogEvent::NextBatch);
} }
@ -483,19 +488,15 @@ fn monologue_asset_tooltip(
over_events over_events
.read() .read()
.filter_map(|Pointer { target, .. }| trees.contains(*target).then_some(*target)) .filter_map(|Pointer { target, .. }| trees.contains(*target).then_some(*target))
.for_each(|e| { .for_each(|e| match trees.get(e) {
match trees.get(e) { Ok((_tree, Some(TreeMonologue(handle)))) => match handle.path() {
Ok((_tree, Some(TreeMonologue(handle)))) => {
match handle.path() {
Some(p) => tooltip.insert("Script", format!("{p}")), Some(p) => tooltip.insert("Script", format!("{p}")),
None => tooltip.insert("Script", "A".into()), None => tooltip.insert("Script", "A".into()),
}
}, },
Ok((_tree, None)) => { Ok((_tree, None)) => {
tooltip.insert("Script", "N/A".into()); tooltip.insert("Script", "N/A".into());
},
_ => ()
} }
_ => (),
}); });
} }
@ -594,7 +595,14 @@ 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()
.trim_prefix("trees/")
.trim_suffix(".mono"),
),
TextLayout::new(JustifyText::Left, LineBreak::WordBoundary), TextLayout::new(JustifyText::Left, LineBreak::WordBoundary),
TreeMonologue(handle.clone()), TreeMonologue(handle.clone()),
MonologuesList, MonologuesList,
@ -636,7 +644,6 @@ 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>();
@ -738,7 +745,7 @@ fn handle_plant_tree(
}); });
} }
#[derive(Event)] #[derive(Event, Debug)]
struct AssignMonologue(Handle<Monologue>); struct AssignMonologue(Handle<Monologue>);
/// Assign the given monologue to a tree /// Assign the given monologue to a tree
@ -749,11 +756,14 @@ fn assign_monologue_to_tree(
) { ) {
events.read().for_each(|event| { events.read().for_each(|event| {
// Get a valid tree to assign an entity to // Get a valid tree to assign an entity to
let tree = query.iter().next().unwrap(); if let Some(tree) = query.iter().next() {
// Create the TreeMonologue component // Create the TreeMonologue component
let monologue = TreeMonologue(event.0.clone()); let monologue = TreeMonologue(event.0.clone());
// Insert the component to the entity // Insert the component to the entity
commands.entity(tree).insert(monologue); 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 // When you pointer over the '+' make the entire menu visible
fn hide_menu( fn hide_menu(mut nodes: Query<(Entity, &mut Visibility, &NavState), Changed<NavState>>) {
mut nodes: Query<(Entity, &mut Visibility, &NavState), Changed<NavState>>
) {
nodes.iter_mut().for_each(|(e, mut v, n)| { nodes.iter_mut().for_each(|(e, mut v, n)| {
*v = match n { *v = match n {
NavState::Open => Visibility::Inherited, NavState::Open => Visibility::Inherited,
@ -794,7 +802,10 @@ fn hide_menu(
} }
fn clear_monologue( fn clear_monologue(
mut nodes: Query<(Entity, &NavState), (Changed<NavState>, With<MonologuePreview>, Without<Text>)>, mut nodes: Query<
(Entity, &NavState),
(Changed<NavState>, With<MonologuePreview>, Without<Text>),
>,
mut commands: Commands, mut commands: Commands,
) { ) {
nodes.iter_mut().for_each(|(e, n)| { nodes.iter_mut().for_each(|(e, n)| {
@ -843,7 +854,6 @@ fn control_menu(
.iter_descendants(root) .iter_descendants(root)
.any(|child| is_hovered.contains(&&child)); .any(|child| is_hovered.contains(&&child));
if !tree_still_hovered { if !tree_still_hovered {
if let Ok(mut n) = nav.get_mut(root) { if let Ok(mut n) = nav.get_mut(root) {
*n = NavState::Closed; *n = NavState::Closed;
} }
@ -857,10 +867,7 @@ fn control_menu(
} }
/// Observer for the "Plant a new tree" button in the debug UI /// Observer for the "Plant a new tree" button in the debug UI
fn spawn_tree( fn spawn_tree(_trigger: Trigger<Pointer<Click>>, mut events: EventWriter<PlantTree>) {
_trigger: Trigger<Pointer<Click>>,
mut events: EventWriter<PlantTree>,
) {
events.write(PlantTree); events.write(PlantTree);
} }

@ -4,10 +4,157 @@ pub(crate) struct BaseUiPlugin;
impl Plugin for BaseUiPlugin { impl Plugin for BaseUiPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
// TODO app.init_resource::<Style>().add_systems(
Update,
(
add_ui_node.run_if(any_component_added::<Node>),
add_ui_button
.run_if(any_component_added::<Button>)
.after(add_ui_node),
add_ui_text
.run_if(any_component_added::<Text>)
.after(add_ui_node),
),
);
} }
} }
/// A resource for supplying style info, e.g., color pallette
#[derive(Resource)]
pub struct Style {
primary: Color,
secondary: Color,
accent: Color,
}
impl Default for Style {
fn default() -> Self {
Style {
primary: WHITE.into(),
secondary: BLACK.into(),
accent: ORANGE_RED.into(),
}
}
}
fn add_ui_button(added: Query<Entity, Added<Button>>, mut commands: Commands) {
fn over(
trigger: Trigger<Pointer<Over>>,
mut query: Query<&mut BorderColor>,
style: Res<Style>,
) {
if let Ok(mut bc) = query.get_mut(trigger.target()) {
debug!("pointer over {:?}", trigger.target());
bc.0 = style.accent;
}
}
fn out(trigger: Trigger<Pointer<Out>>, mut query: Query<&mut BorderColor>, style: Res<Style>) {
if let Ok(mut bc) = query.get_mut(trigger.target()) {
debug!("pointer out {:?}", trigger.target());
bc.0 = style.secondary;
}
}
fn pressed(
trigger: Trigger<Pointer<Pressed>>,
mut query: Query<&mut BackgroundColor>,
style: Res<Style>,
) {
if let Ok(mut bg) = query.get_mut(trigger.target()) {
debug!("pointer pressed {:?}", trigger.target());
bg.0 = style.accent;
}
}
fn released(
trigger: Trigger<Pointer<Released>>,
mut query: Query<&mut BackgroundColor>,
style: Res<Style>,
) {
if let Ok(mut bg) = query.get_mut(trigger.target()) {
debug!("pointer released {:?}", trigger.target());
bg.0 = style.primary;
}
}
added.iter().for_each(|e| {
debug!("Updating button: {:?}", e);
// Observe with hover over/out + click reactors
commands
.entity(e)
.observe(over)
.observe(out)
.observe(pressed)
.observe(released);
})
}
fn add_ui_text(added: Query<Entity, Added<Text>>, mut commands: Commands) {
fn pressed(
trigger: Trigger<Pointer<Pressed>>,
mut query: Query<(&mut TextColor, &ChildOf)>,
style: Res<Style>,
) {
// ONLY DO THIS IF CHILD OF BUTTON
if let Ok((mut tc, ChildOf(p))) = query.get_mut(trigger.target()) {
if buttons.contains(p) {
debug!("pointer pressed {:?}", trigger.target());
tc.0 = style.primary;
}
}
}
fn released(
trigger: Trigger<Pointer<Released>>,
mut query: Query<(&mut TextColor, &ChildOf)>,
buttons: Query<Entity, With<Button>>,
style: Res<Style>,
) {
// ONLY DO THIS IF CHILD OF BUTTON
if let Ok((mut tc, ChildOf(p))) = query.get_mut(trigger.target()) {
if buttons.contains(p) {
debug!("pointer released {:?}", trigger.target());
tc.0 = style.secondary;
}
}
}
added.iter().for_each(|e| {
debug!("Updating text: {:?}", e);
// Observe with hover over/out + click reactors
commands.entity(e).observe(pressed).observe(released);
})
}
fn add_ui_node(
mut added: Query<(Entity, &mut Node), Added<Node>>,
style: Res<Style>,
text: Query<Entity, With<Text>>,
mut commands: Commands,
) {
added.iter_mut().for_each(|(e, mut n)| {
debug!("Updating node: {:?}", e);
n.padding = UiRect::all(Val::Px(3.0));
// Update Node border
n.border = UiRect::all(Val::Px(1.0));
let mut this = commands.entity(e);
if text.contains(e) {
// Only change text color for text
this.insert(TextColor(style.secondary));
} else {
// Add extra stuff for non-text nodes
this.insert(BackgroundColor(style.primary));
this.insert(BorderColor(style.secondary));
this.insert(BorderRadius::all(Val::Px(5.0)));
}
})
}
/// Marker component for handling Resource -> Ui Sync /// Marker component for handling Resource -> Ui Sync
#[derive(Component, Default, Debug)] #[derive(Component, Default, Debug)]
pub struct SyncResource<R: Resource + Default + Display>(R); pub struct SyncResource<R: Resource + Default + Display>(R);

Loading…
Cancel
Save