it actually completely fully works praise jesus
parent
fc02df8638
commit
dd912f420f
@ -1,15 +1,232 @@
|
||||
/// TODO:
|
||||
/// * Titles on top left of window/tab
|
||||
/// *
|
||||
/// * Text box w/ clear button
|
||||
/// * Names/Labels management
|
||||
/// * Button color management
|
||||
/// * Move code to submodules
|
||||
///
|
||||
/// BUGS:
|
||||
/// * When selecting one tree, possible to select another without the first closing.
|
||||
///
|
||||
use bevy::{prelude::*, window::PrimaryWindow};
|
||||
|
||||
pub struct GameUiPlugin;
|
||||
|
||||
impl Plugin for GameUiPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app;
|
||||
// .add_systems(PreUpdate, )
|
||||
// .add_systems(Update,)
|
||||
// .add_systems(PostUpdate, );
|
||||
app.add_systems(Update, toggle)
|
||||
.add_systems(PostUpdate, selection);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Bundle)]
|
||||
pub struct UiKitContainer {
|
||||
node_bundle: NodeBundle,
|
||||
select: UiKitSelect,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum UiKitPosition {
|
||||
Top,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
impl UiKitContainer {
|
||||
pub 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: UiKitSelect::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Bundle)]
|
||||
pub struct UiKitButton {
|
||||
button_bundle: ButtonBundle,
|
||||
select: UiKitSelect,
|
||||
}
|
||||
|
||||
impl UiKitButton {
|
||||
pub fn new(color: Color) -> Self {
|
||||
UiKitButton {
|
||||
button_bundle: ButtonBundle {
|
||||
style: Style {
|
||||
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: UiKitSelect::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Component, Copy, Clone)]
|
||||
pub enum UiKitSelect {
|
||||
Active,
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Debug, Bundle)]
|
||||
pub struct UiKitTextInput {
|
||||
text_bundle: TextBundle,
|
||||
}
|
||||
|
||||
impl UiKitTextInput {
|
||||
pub fn new() -> Self {
|
||||
UiKitTextInput {
|
||||
text_bundle: TextBundle { ..default() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// When an item is selected/de-selected change it's display accordingly
|
||||
fn selection(
|
||||
mut events: Query<
|
||||
(&mut BackgroundColor, &UiKitSelect, &Children),
|
||||
(Changed<UiKitSelect>, With<Button>),
|
||||
>,
|
||||
mut styles: Query<&mut Style>,
|
||||
) {
|
||||
events
|
||||
.iter_mut()
|
||||
.for_each(|(mut bg_color, select, children)| {
|
||||
bg_color.0 = match select {
|
||||
UiKitSelect::Active => Color::RED,
|
||||
UiKitSelect::None => Color::WHITE,
|
||||
};
|
||||
children.iter().for_each(|&child| {
|
||||
if let Ok(mut style) = styles.get_mut(child) {
|
||||
style.display = match select {
|
||||
UiKitSelect::Active => Display::Flex,
|
||||
UiKitSelect::None => Display::None,
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// 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, With<Button>>,
|
||||
mut selects: Query<&mut UiKitSelect>,
|
||||
parents: Query<&Parent>,
|
||||
children: Query<&Children>,
|
||||
) {
|
||||
events.iter().for_each(|entity| {
|
||||
// Otherwise, update nav tree(s)
|
||||
if let Ok(interaction) = interactions.get(entity) {
|
||||
match interaction {
|
||||
Interaction::Pressed | Interaction::Hovered => {
|
||||
{
|
||||
let parent = parents.get(entity).expect("entity has parent");
|
||||
children
|
||||
.get(parent.get())
|
||||
.expect("parent has children")
|
||||
.iter()
|
||||
.filter(|&e| *e != entity)
|
||||
.for_each(|sibling| {
|
||||
if let Ok(mut select) = selects.get_mut(*sibling) {
|
||||
*select = UiKitSelect::None
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if let Ok(mut select) = selects.get_mut(entity) {
|
||||
*select = UiKitSelect::Active
|
||||
}
|
||||
}
|
||||
Interaction::None => {
|
||||
// Find the ancestor which does not have a parent
|
||||
let root_parent = parents
|
||||
.iter_ancestors(entity)
|
||||
.find(|&e| parents.get(e).is_err())
|
||||
.expect("entity has root parent");
|
||||
let family: Vec<Entity> = children.iter_descendants(root_parent).collect();
|
||||
let family_inactive = family
|
||||
.iter()
|
||||
.filter_map(|member| interactions.get(*member).ok())
|
||||
.all(|&interaction| interaction == Interaction::None);
|
||||
|
||||
// Zero out children
|
||||
let descendants: Vec<Entity> = children.iter_descendants(entity).collect();
|
||||
let descendants_inactive = descendants
|
||||
.iter()
|
||||
.filter_map(|child| interactions.get(*child).ok())
|
||||
.all(|&interaction| interaction == Interaction::None);
|
||||
|
||||
// The entire tree is inactive
|
||||
if family_inactive {
|
||||
family.iter().for_each(|member| {
|
||||
if let Ok(mut select) = selects.get_mut(*member) {
|
||||
*select = UiKitSelect::None
|
||||
}
|
||||
});
|
||||
if let Ok(mut select) = selects.get_mut(entity) {
|
||||
*select = UiKitSelect::None
|
||||
}
|
||||
// Just the sub-tree is inactive
|
||||
} else if descendants_inactive {
|
||||
descendants.iter().for_each(|child| {
|
||||
if let Ok(mut select) = selects.get_mut(*child) {
|
||||
*select = UiKitSelect::None
|
||||
}
|
||||
});
|
||||
if let Ok(mut select) = selects.get_mut(entity) {
|
||||
*select = UiKitSelect::None
|
||||
}
|
||||
// This node is active (usually a parent of an active child)
|
||||
} else if let Ok(mut select) = selects.get_mut(entity) {
|
||||
*select = UiKitSelect::Active
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue