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() {
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<NavState>>) {
nodes.iter_mut().for_each(|(e, mut v, n)| {
fn hide_menu(mut nodes: Query<(&mut Visibility, &NavState), Changed<NavState>>) {
nodes.iter_mut().for_each(|(mut v, n)| {
*v = match n {
NavState::Open => Visibility::Inherited,
NavState::Closed => Visibility::Hidden,

@ -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<Pointer<Click>>,
mut dialog_events: EventWriter<DialogEvent>,
query: Query<&TreeMonologue, With<Tree>>,
) {
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::<Children>();
@ -738,7 +745,7 @@ fn handle_plant_tree(
});
}
#[derive(Event)]
#[derive(Event, Debug)]
struct AssignMonologue(Handle<Monologue>);
/// 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<NavState>>
) {
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,
@ -794,7 +802,10 @@ fn hide_menu(
}
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,
) {
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<Pointer<Click>>,
mut events: EventWriter<PlantTree>,
) {
fn spawn_tree(_trigger: Trigger<Pointer<Click>>, mut events: EventWriter<PlantTree>) {
events.write(PlantTree);
}

@ -4,10 +4,157 @@ pub(crate) struct BaseUiPlugin;
impl Plugin for BaseUiPlugin {
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
#[derive(Component, Default, Debug)]
pub struct SyncResource<R: Resource + Default + Display>(R);

Loading…
Cancel
Save