Technically usable. calling it a night

main
Elijah Voigt 2 years ago
parent 87a4c6fd40
commit fc02df8638

@ -1,9 +1,7 @@
use bevy::{ // TODO: Z-index of children should be higher than parents
input::{keyboard::KeyboardInput, ButtonState},
prelude::*, use bevy::prelude::*;
window::PrimaryWindow, use monologue_trees::ui::*;
};
use monologue_trees::{debug::*, ui::*};
fn main() { fn main() {
App::new() App::new()
@ -21,72 +19,174 @@ fn main() {
// .init_resource::<Icon>() // .init_resource::<Icon>()
.add_systems(Startup, init_ui2) .add_systems(Startup, init_ui2)
.add_systems(Update, toggle) .add_systems(Update, toggle)
.add_systems(PostUpdate, selection)
// .add_systems(Startup, init_ui) // .add_systems(Startup, init_ui)
// .add_systems(Update, (cursors, container())) // .add_systems(Update, (cursors, container))
.run(); .run();
} }
fn container() -> NodeBundle { #[derive(Debug, Bundle)]
NodeBundle { struct UiKitContainer {
style: Style { node_bundle: NodeBundle,
border: UiRect::all(Val::Px(2.0)), select: Select,
right: Val::Percent(-100.0), }
top: Val::Px(-4.0),
flex_direction: FlexDirection::Column, #[derive(Copy, Clone)]
display: Display::None, enum UiKitPosition {
..default() Top,
}, Left,
background_color: BackgroundColor(Color::PURPLE), Right,
border_color: BorderColor(Color::BLACK), }
..default()
impl UiKitContainer {
fn new(position: UiKitPosition) -> Self {
let style = match position {
UiKitPosition::Top => Style {
border: UiRect::all(Val::Px(1.0)),
top: Val::Percent(102.0),
left: Val::Px(-2.0),
flex_direction: FlexDirection::Column,
align_items: AlignItems::FlexStart,
display: Display::None,
..default()
},
UiKitPosition::Left => Style {
border: UiRect::all(Val::Px(1.0)),
left: Val::Percent(100.0),
top: Val::Px(-2.0),
flex_direction: FlexDirection::Column,
justify_items: JustifyItems::Start,
display: Display::None,
..default()
},
UiKitPosition::Right => Style {
border: UiRect::all(Val::Px(1.0)),
right: Val::Percent(104.0),
top: Val::Px(-2.0),
flex_direction: FlexDirection::Column,
justify_items: JustifyItems::Start,
display: Display::None,
..default()
},
};
UiKitContainer {
node_bundle: NodeBundle {
style,
background_color: BackgroundColor(Color::PURPLE),
border_color: BorderColor(Color::BLACK),
..default()
},
select: Select::None,
}
} }
} }
fn spec(color: Color) -> ButtonBundle { #[derive(Debug, Bundle)]
ButtonBundle { struct UiKitButton {
style: Style { button_bundle: ButtonBundle,
border: UiRect::all(Val::Px(2.0)), select: Select,
width: Val::Px(100.0), }
height: Val::Px(50.0),
flex_direction: FlexDirection::Column, impl UiKitButton {
..default() fn new(color: Color) -> Self {
}, UiKitButton {
background_color: BackgroundColor(color), button_bundle: ButtonBundle {
border_color: BorderColor(Color::BLACK), style: Style {
..default() border: UiRect::all(Val::Px(1.0)),
width: Val::Px(100.0),
height: Val::Px(50.0),
flex_direction: FlexDirection::Column,
..default()
},
background_color: BackgroundColor(color),
border_color: BorderColor(Color::BLACK),
..default()
},
select: Select::None,
}
} }
} }
fn toggle( #[derive(Debug, Component, Copy, Clone)]
mut events: Query< enum Select {
(&mut BackgroundColor, &Interaction, &Children), Active,
(Changed<Interaction>, With<Button>), None,
>, }
/// When an item is selected/de-selected change it's display accordingly
fn selection(
mut events: Query<(&mut BackgroundColor, &Select, &Children), (Changed<Select>, With<Button>)>,
mut styles: Query<&mut Style>, mut styles: Query<&mut Style>,
) { ) {
events events
.iter_mut() .iter_mut()
.for_each(|(mut bg_color, interaction, children)| match interaction { .for_each(|(mut bg_color, select, children)| {
Interaction::Pressed => { bg_color.0 = match select {
bg_color.0 = Color::RED; Select::Active => Color::RED,
children.iter().for_each(|&child| { Select::None => Color::WHITE,
if let Ok(mut style) = styles.get_mut(child) { };
style.display = match style.display { children.iter().for_each(|&child| {
Display::Flex => Display::None, if let Ok(mut style) = styles.get_mut(child) {
Display::None | _ => Display::Flex, style.display = match select {
}; Select::Active => Display::Flex,
Select::None => Display::None,
} }
}); }
} });
Interaction::Hovered => {
bg_color.0 = Color::RED;
}
Interaction::None => {
bg_color.0 = Color::PINK;
}
}); });
} }
/// Toggle a UI Nav tree open/closed
///
/// PERF: This is hella not performant, we just usually don't have many elements to iterate over so
/// it's tolerable.
///
// TODO: Should not be able to select multiple children in branch of tree
// TODO: Port to ui.rs
fn toggle(
events: Query<Entity, (Changed<Interaction>, With<Button>)>,
interactions: Query<&Interaction>,
mut selects: Query<&mut Select>,
children: Query<&Children>,
) {
events.iter().for_each(|entity| {
// If all interactions are inactive, set all selections to None
if interactions
.iter()
.all(|&interaction| interaction == Interaction::None)
{
selects.iter_mut().for_each(|mut select| {
*select = Select::None;
});
// Otherwise change this item's selection
} else {
let mut select = selects
.get_mut(entity)
.expect("Entity has Select component");
let interaction = interactions
.get(entity)
.expect("Entity has Interaction component");
*select = match interaction {
Interaction::Pressed | Interaction::Hovered => Select::Active,
Interaction::None => {
let children_inactive = children.iter_descendants(entity).all(|child| {
if let Ok(&c) = interactions.get(child) {
c == Interaction::None
} else {
true
}
});
if children_inactive {
Select::None
} else {
Select::Active
}
}
}
}
});
}
// const CURSORS: [CursorIcon; 35] = [ // const CURSORS: [CursorIcon; 35] = [
// CursorIcon::Default, // CursorIcon::Default,
// CursorIcon::Crosshair, // CursorIcon::Crosshair,
@ -125,6 +225,136 @@ fn toggle(
// CursorIcon::RowResize, // CursorIcon::RowResize,
// ]; // ];
fn spawn_tree(parent: &mut ChildBuilder, pos: UiKitPosition) {
let pos2 = match pos {
UiKitPosition::Top => UiKitPosition::Left,
_ => pos,
};
parent
.spawn(UiKitButton::new(Color::PINK))
.with_children(|parent| {
parent
.spawn(UiKitContainer::new(pos))
.with_children(|parent| {
parent
.spawn(UiKitButton::new(Color::PINK))
.with_children(|parent| {
parent
.spawn(UiKitContainer::new(pos2))
.with_children(|parent| {
parent.spawn(UiKitButton::new(Color::PINK));
parent.spawn(UiKitButton::new(Color::PINK));
parent.spawn(UiKitButton::new(Color::PINK));
});
});
parent
.spawn(UiKitButton::new(Color::PINK))
.with_children(|parent| {
parent
.spawn(UiKitContainer::new(pos2))
.with_children(|parent| {
parent.spawn(UiKitButton::new(Color::PINK));
parent.spawn(UiKitButton::new(Color::PINK));
parent.spawn(UiKitButton::new(Color::PINK));
});
});
parent
.spawn(UiKitButton::new(Color::PINK))
.with_children(|parent| {
parent
.spawn(UiKitContainer::new(pos2))
.with_children(|parent| {
parent.spawn(UiKitButton::new(Color::PINK));
parent.spawn(UiKitButton::new(Color::PINK));
parent.spawn(UiKitButton::new(Color::PINK));
});
});
});
});
parent
.spawn(UiKitButton::new(Color::PINK))
.with_children(|parent| {
parent
.spawn(UiKitContainer::new(pos))
.with_children(|parent| {
parent
.spawn(UiKitButton::new(Color::PINK))
.with_children(|parent| {
parent
.spawn(UiKitContainer::new(pos2))
.with_children(|parent| {
parent.spawn(UiKitButton::new(Color::PINK));
parent.spawn(UiKitButton::new(Color::PINK));
parent.spawn(UiKitButton::new(Color::PINK));
});
});
parent
.spawn(UiKitButton::new(Color::PINK))
.with_children(|parent| {
parent
.spawn(UiKitContainer::new(pos2))
.with_children(|parent| {
parent.spawn(UiKitButton::new(Color::PINK));
parent.spawn(UiKitButton::new(Color::PINK));
parent.spawn(UiKitButton::new(Color::PINK));
});
});
parent
.spawn(UiKitButton::new(Color::PINK))
.with_children(|parent| {
parent
.spawn(UiKitContainer::new(pos2))
.with_children(|parent| {
parent.spawn(UiKitButton::new(Color::PINK));
parent.spawn(UiKitButton::new(Color::PINK));
parent.spawn(UiKitButton::new(Color::PINK));
});
});
});
});
parent
.spawn(UiKitButton::new(Color::PINK))
.with_children(|parent| {
parent
.spawn(UiKitContainer::new(pos))
.with_children(|parent| {
parent
.spawn(UiKitButton::new(Color::PINK))
.with_children(|parent| {
parent
.spawn(UiKitContainer::new(pos2))
.with_children(|parent| {
parent.spawn(UiKitButton::new(Color::PINK));
parent.spawn(UiKitButton::new(Color::PINK));
parent.spawn(UiKitButton::new(Color::PINK));
});
});
parent
.spawn(UiKitButton::new(Color::PINK))
.with_children(|parent| {
parent
.spawn(UiKitContainer::new(pos2))
.with_children(|parent| {
parent.spawn(UiKitButton::new(Color::PINK));
parent.spawn(UiKitButton::new(Color::PINK));
parent.spawn(UiKitButton::new(Color::PINK));
});
});
parent
.spawn(UiKitButton::new(Color::PINK))
.with_children(|parent| {
parent
.spawn(UiKitContainer::new(pos2))
.with_children(|parent| {
parent.spawn(UiKitButton::new(Color::PINK));
parent.spawn(UiKitButton::new(Color::PINK));
parent.spawn(UiKitButton::new(Color::PINK));
});
});
});
});
}
fn init_ui2(mut commands: Commands) { fn init_ui2(mut commands: Commands) {
commands.spawn(( commands.spawn((
Camera2dBundle { ..default() }, Camera2dBundle { ..default() },
@ -134,90 +364,67 @@ fn init_ui2(mut commands: Commands) {
commands commands
.spawn(NodeBundle { .spawn(NodeBundle {
style: Style { style: Style {
border: UiRect::all(Val::Px(2.0)), width: Val::Percent(100.0),
flex_direction: FlexDirection::Column, height: Val::Percent(100.0),
justify_content: JustifyContent::SpaceEvenly,
..default() ..default()
}, },
background_color: BackgroundColor(Color::PURPLE), background_color: BackgroundColor(Color::FUCHSIA),
border_color: BorderColor(Color::BLACK),
..default() ..default()
}) })
.with_children(|parent| { .with_children(|parent| {
parent.spawn(spec(Color::PINK)).with_children(|parent| { parent
parent.spawn(container()).with_children(|parent| { .spawn(NodeBundle {
parent.spawn(spec(Color::PINK)).with_children(|parent| { style: Style {
parent.spawn(container()).with_children(|parent| { border: UiRect::all(Val::Px(1.0)),
parent.spawn(spec(Color::PINK)); left: Val::Px(0.0),
parent.spawn(spec(Color::PINK)); flex_direction: FlexDirection::Column,
parent.spawn(spec(Color::PINK)); position_type: PositionType::Absolute,
}); align_self: AlignSelf::Center,
}); ..default()
parent.spawn(spec(Color::PINK)).with_children(|parent| { },
parent.spawn(container()).with_children(|parent| { background_color: BackgroundColor(Color::PURPLE),
parent.spawn(spec(Color::PINK)); border_color: BorderColor(Color::BLACK),
parent.spawn(spec(Color::PINK)); ..default()
parent.spawn(spec(Color::PINK)); })
}); .with_children(|parent| {
}); spawn_tree(parent, UiKitPosition::Left);
parent.spawn(spec(Color::PINK)).with_children(|parent| {
parent.spawn(container()).with_children(|parent| {
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
});
});
}); });
});
parent.spawn(spec(Color::PINK)).with_children(|parent| { parent
parent.spawn(container()).with_children(|parent| { .spawn(NodeBundle {
parent.spawn(spec(Color::PINK)).with_children(|parent| { style: Style {
parent.spawn(container()).with_children(|parent| { border: UiRect::all(Val::Px(1.0)),
parent.spawn(spec(Color::PINK)); right: Val::Px(0.0),
parent.spawn(spec(Color::PINK)); flex_direction: FlexDirection::Column,
parent.spawn(spec(Color::PINK)); position_type: PositionType::Absolute,
}); align_self: AlignSelf::Center,
}); ..default()
parent.spawn(spec(Color::PINK)).with_children(|parent| { },
parent.spawn(container()).with_children(|parent| { background_color: BackgroundColor(Color::PURPLE),
parent.spawn(spec(Color::PINK)); border_color: BorderColor(Color::BLACK),
parent.spawn(spec(Color::PINK)); ..default()
parent.spawn(spec(Color::PINK)); })
}); .with_children(|parent| {
}); spawn_tree(parent, UiKitPosition::Right);
parent.spawn(spec(Color::PINK)).with_children(|parent| {
parent.spawn(container()).with_children(|parent| {
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
});
});
}); });
});
parent.spawn(spec(Color::PINK)).with_children(|parent| { parent
parent.spawn(container()).with_children(|parent| { .spawn(NodeBundle {
parent.spawn(spec(Color::PINK)).with_children(|parent| { style: Style {
parent.spawn(container()).with_children(|parent| { border: UiRect::all(Val::Px(1.0)),
parent.spawn(spec(Color::PINK)); top: Val::Px(0.0),
parent.spawn(spec(Color::PINK)); flex_direction: FlexDirection::Row,
parent.spawn(spec(Color::PINK)); position_type: PositionType::Absolute,
}); ..default()
}); },
parent.spawn(spec(Color::PINK)).with_children(|parent| { background_color: BackgroundColor(Color::PURPLE),
parent.spawn(container()).with_children(|parent| { border_color: BorderColor(Color::BLACK),
parent.spawn(spec(Color::PINK)); ..default()
parent.spawn(spec(Color::PINK)); })
parent.spawn(spec(Color::PINK)); .with_children(|parent| {
}); spawn_tree(parent, UiKitPosition::Top);
});
parent.spawn(spec(Color::PINK)).with_children(|parent| {
parent.spawn(container()).with_children(|parent| {
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
});
});
}); });
});
}); });
} }
@ -258,7 +465,7 @@ fn init_ui2(mut commands: Commands) {
// )) // ))
// .with_children(|parent| { // .with_children(|parent| {
// parent.spawn(( // parent.spawn((
// container, // UiKitContainer::new,
// GameUiSet, // GameUiSet,
// // Name::new("Grow/Shrink Set"), // // Name::new("Grow/Shrink Set"),
// NodeBundle { ..default() }, // NodeBundle { ..default() },
@ -316,7 +523,7 @@ fn init_ui2(mut commands: Commands) {
// } // }
// //
// #[derive(Debug, Component)] // #[derive(Debug, Component)]
// struct container; // struct Container;
// //
// fn container( // fn container(
// mut events: EventReader<KeyboardInput>, // mut events: EventReader<KeyboardInput>,

@ -7,95 +7,9 @@ pub struct GameUiPlugin;
impl Plugin for GameUiPlugin { impl Plugin for GameUiPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems(PreUpdate, (spawn_nav, spawn_tab, spawn_set, spawn_button)) app;
.add_systems(Update, (manage_names, manage_tab)); // .add_systems(PreUpdate, )
// .add_systems(Update,)
// .add_systems(PostUpdate, );
} }
} }
/// Navigation UI Container
/// Buttons in a nav correspond to Tabs in a navbar/navmenu
#[derive(Component, PartialEq)]
pub struct GameUiNav;
/// Sets contain a variety of elements like action buttons
/// Usually the "leaf" of a navigation tree
#[derive(Component, PartialEq)]
pub struct GameUiSet;
/// Buttons are used for interaction in the UI
#[derive(Component, PartialEq)]
pub struct GameUiButton;
#[derive(Component, PartialEq)]
pub struct GameUiTab;
fn spawn_nav(events: Query<(Entity, &GameUiNav), Added<GameUiNav>>, mut commands: Commands) {
events.iter().for_each(|(entity, _)| {
info!("Spawning Nav UI");
});
}
fn spawn_tab(events: Query<Entity, Added<GameUiTab>>, mut commands: Commands) {
events.iter().for_each(|entity| {});
}
fn spawn_set(events: Query<Entity, Added<GameUiSet>>, mut commands: Commands) {
events.iter().for_each(|entity| {});
}
fn spawn_button(
events: Query<(Entity, &GameUiButton), Added<GameUiButton>>,
mut commands: Commands,
) {
events.iter().for_each(|(entity, _)| {
info!("Spawning UI Button");
commands.entity(entity).insert(ButtonBundle {
style: Style {
padding: UiRect::all(Val::Px(3.0)),
margin: UiRect::all(Val::Px(3.0)),
..default()
},
background_color: BackgroundColor(Color::BLUE),
..default()
});
});
}
fn manage_names(
events: Query<
(Entity, &Name),
(
Or<(With<GameUiNav>, With<GameUiButton>)>,
Or<(Added<Name>, Changed<Name>)>,
),
>,
mut commands: Commands,
) {
events.iter().for_each(|(entity, name)| {
commands.entity(entity).with_children(|parent| {
parent
.spawn(NodeBundle {
style: Style {
align_self: AlignSelf::FlexStart,
padding: UiRect::all(Val::Px(3.0)),
margin: UiRect::all(Val::Px(3.0)),
..default()
},
background_color: BackgroundColor(Color::CRIMSON),
..default()
})
.with_children(|parent| {
parent.spawn(TextBundle::from_section(
name.as_str(),
TextStyle { ..default() },
));
});
});
});
}
fn manage_tab(events: Query<&Interaction, Changed<Interaction>>) {
events.iter().for_each(|interaction| {
info!("When tab is clicked, show/hide content");
});
}

Loading…
Cancel
Save