making progressgit diff
parent
bb9b37ef17
commit
41be0f42bf
@ -1,250 +1,210 @@
|
||||
/// TODO: Sorted list/set
|
||||
///
|
||||
/// TODO:
|
||||
/// * Titles on top left of window/tab
|
||||
/// *
|
||||
use bevy::{prelude::*, window::PrimaryWindow};
|
||||
|
||||
pub struct GameUiPlugin;
|
||||
|
||||
impl Plugin for GameUiPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(
|
||||
Update,
|
||||
(
|
||||
init_ui_nav,
|
||||
init_ui_tab,
|
||||
// UI lists
|
||||
init_ui_list,
|
||||
// UI Set
|
||||
init_ui_set,
|
||||
// Buttons
|
||||
init_ui_button,
|
||||
manage_button_interaction,
|
||||
// Cursor
|
||||
manage_cursor,
|
||||
// Initialize name labels
|
||||
init_name,
|
||||
),
|
||||
);
|
||||
app.add_systems(PreUpdate, (spawn_nav, spawn_tab, spawn_set, spawn_button))
|
||||
.add_systems(Update, (manage_names, manage_tab));
|
||||
}
|
||||
}
|
||||
|
||||
fn init_name(
|
||||
events: Query<
|
||||
(Entity, &Name),
|
||||
(
|
||||
Added<Name>,
|
||||
Or<(
|
||||
With<GameUiNav>,
|
||||
With<GameUiTab>,
|
||||
With<GameUiSet>,
|
||||
With<GameUiList>,
|
||||
With<GameUiButton>,
|
||||
)>,
|
||||
),
|
||||
>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
events.iter().for_each(|(entity, name)| {
|
||||
commands.entity(entity).with_children(|parent| {
|
||||
parent.spawn(
|
||||
TextBundle::from_section(name, TextStyle { ..default() }).with_style(Style {
|
||||
top: Val::Px(0.0),
|
||||
left: Val::Px(0.0),
|
||||
..default()
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
/// Component for marking UI Nodes
|
||||
#[derive(Component, PartialEq)]
|
||||
pub enum GameUi {
|
||||
/// Navigation container
|
||||
Nav,
|
||||
/// Tab container
|
||||
Tab,
|
||||
/// Game UI Set
|
||||
Set,
|
||||
/// Game UI Button
|
||||
Button,
|
||||
}
|
||||
|
||||
/// Ui Navigation
|
||||
#[derive(Debug, Component)]
|
||||
pub struct GameUiNav;
|
||||
|
||||
fn init_ui_nav(events: Query<Entity, Added<GameUiNav>>, mut commands: Commands) {
|
||||
events.iter().for_each(|entity| {
|
||||
let parent = commands
|
||||
.spawn(NodeBundle {
|
||||
fn spawn_nav(events: Query<(Entity, &GameUi), Added<GameUi>>, mut commands: Commands) {
|
||||
events
|
||||
.iter()
|
||||
.filter(|(_, ui)| **ui == GameUi::Nav)
|
||||
.for_each(|(entity, _)| {
|
||||
info!("Spawning Nav UI");
|
||||
commands.entity(entity).insert(NodeBundle {
|
||||
style: Style {
|
||||
flex_direction: FlexDirection::Row,
|
||||
flex_direction: FlexDirection::Row, // ?
|
||||
padding: UiRect::all(Val::Px(3.0)),
|
||||
margin: UiRect::all(Val::Px(3.0)),
|
||||
width: Val::Percent(80.0),
|
||||
max_height: Val::Percent(80.0),
|
||||
overflow: Overflow::clip(),
|
||||
justify_content: JustifyContent::Center,
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
},
|
||||
background_color: BackgroundColor(Color::PINK),
|
||||
background_color: BackgroundColor(Color::PURPLE),
|
||||
..default()
|
||||
})
|
||||
.id();
|
||||
|
||||
commands
|
||||
.entity(entity)
|
||||
.insert(NodeBundle {
|
||||
style: Style { ..default() },
|
||||
..default()
|
||||
})
|
||||
.set_parent(parent);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Ui Tab Element
|
||||
#[derive(Debug, Component)]
|
||||
pub struct GameUiTab;
|
||||
|
||||
fn init_ui_tab(events: Query<Entity, Added<GameUiTab>>, mut commands: Commands) {
|
||||
events.iter().for_each(|entity| {
|
||||
let parent = commands
|
||||
fn spawn_tab(
|
||||
events: Query<(Entity, &GameUi, &Name, &Parent), Added<GameUi>>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
events
|
||||
.iter()
|
||||
.filter(|(_, ui, _, _)| **ui == GameUi::Tab)
|
||||
.for_each(|(entity, _, name, parent)| {
|
||||
info!("Spawning Tab");
|
||||
// Create container for this tab
|
||||
let parent_id = commands
|
||||
.spawn(NodeBundle {
|
||||
style: Style {
|
||||
flex_direction: FlexDirection::Column,
|
||||
padding: UiRect::all(Val::Px(3.0)),
|
||||
margin: UiRect::all(Val::Px(3.0)),
|
||||
..default()
|
||||
},
|
||||
background_color: BackgroundColor(Color::GREEN),
|
||||
..default()
|
||||
})
|
||||
.set_parent(parent.get())
|
||||
.with_children(|parent| {
|
||||
parent.spawn((GameUi::Button, name.clone(), NodeBundle { ..default() }));
|
||||
})
|
||||
.id();
|
||||
|
||||
// Insert spawn container with tab
|
||||
commands
|
||||
.entity(entity)
|
||||
.remove::<Name>()
|
||||
.insert(NodeBundle {
|
||||
style: Style {
|
||||
flex_wrap: FlexWrap::Wrap,
|
||||
flex_direction: FlexDirection::Column,
|
||||
padding: UiRect::all(Val::Px(3.0)),
|
||||
margin: UiRect::all(Val::Px(3.0)),
|
||||
display: Display::None,
|
||||
..default()
|
||||
},
|
||||
background_color: BackgroundColor(Color::TEAL),
|
||||
background_color: BackgroundColor(Color::PINK),
|
||||
..default()
|
||||
})
|
||||
.set_parent(parent);
|
||||
.set_parent(parent_id);
|
||||
});
|
||||
}
|
||||
|
||||
/// Describes the state of an element
|
||||
#[derive(Debug, Component)]
|
||||
pub enum UiElementState {
|
||||
Enabled,
|
||||
Disabled,
|
||||
Active,
|
||||
Error,
|
||||
}
|
||||
|
||||
/// GameUiList for holding ordered collections of objects
|
||||
#[derive(Debug, Component)]
|
||||
pub struct GameUiList;
|
||||
|
||||
/// Manage UI Lists: lists of UI entities.
|
||||
fn init_ui_list(events: Query<Entity, Added<GameUiList>>, mut commands: Commands) {
|
||||
events.iter().for_each(|entity| {
|
||||
fn spawn_set(events: Query<(Entity, &GameUi), Added<GameUi>>, mut commands: Commands) {
|
||||
events
|
||||
.iter()
|
||||
.filter(|(_, ui)| **ui == GameUi::Set)
|
||||
.for_each(|(entity, _)| {
|
||||
info!("Spawning UI Set");
|
||||
commands.entity(entity).insert(NodeBundle {
|
||||
style: Style {
|
||||
flex_direction: FlexDirection::Column,
|
||||
justify_items: JustifyItems::Center,
|
||||
border: UiRect::all(Val::Px(2.0)),
|
||||
margin: UiRect::all(Val::Px(2.0)),
|
||||
padding: UiRect::all(Val::Px(2.0)),
|
||||
flex_direction: FlexDirection::Row,
|
||||
flex_wrap: FlexWrap::Wrap,
|
||||
padding: UiRect::all(Val::Px(3.0)),
|
||||
margin: UiRect::all(Val::Px(3.0)),
|
||||
..default()
|
||||
},
|
||||
background_color: BackgroundColor(Color::RED),
|
||||
border_color: BorderColor(Color::BLACK),
|
||||
..default()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// GameUiSet Component for holding collections of objects
|
||||
#[derive(Debug, Component)]
|
||||
pub struct GameUiSet;
|
||||
|
||||
/// Manage UI Sets: collections of UI entities.
|
||||
fn init_ui_set(events: Query<Entity, Added<GameUiSet>>, mut commands: Commands) {
|
||||
events.iter().for_each(|entity| {
|
||||
let parent = commands
|
||||
.spawn(NodeBundle {
|
||||
style: Style {
|
||||
flex_direction: FlexDirection::Column,
|
||||
padding: UiRect::all(Val::Px(5.0)),
|
||||
margin: UiRect::all(Val::Px(5.0)),
|
||||
..default()
|
||||
},
|
||||
background_color: BackgroundColor(Color::BLUE),
|
||||
border_color: BorderColor(Color::BLACK),
|
||||
..default()
|
||||
})
|
||||
.id();
|
||||
|
||||
commands
|
||||
.entity(entity)
|
||||
.insert(NodeBundle {
|
||||
fn spawn_button(events: Query<(Entity, &GameUi), Added<GameUi>>, mut commands: Commands) {
|
||||
events
|
||||
.iter()
|
||||
.filter(|(_, ui)| **ui == GameUi::Button)
|
||||
.for_each(|(entity, _)| {
|
||||
info!("Spawning UI Button");
|
||||
commands.entity(entity).insert(ButtonBundle {
|
||||
style: Style {
|
||||
flex_direction: FlexDirection::Row,
|
||||
flex_wrap: FlexWrap::Wrap,
|
||||
border: UiRect::all(Val::Px(2.0)),
|
||||
margin: UiRect::all(Val::Px(2.0)),
|
||||
padding: UiRect::all(Val::Px(2.0)),
|
||||
padding: UiRect::all(Val::Px(3.0)),
|
||||
margin: UiRect::all(Val::Px(3.0)),
|
||||
..default()
|
||||
},
|
||||
background_color: BackgroundColor(Color::BLUE),
|
||||
border_color: BorderColor(Color::BLACK),
|
||||
..default()
|
||||
})
|
||||
.set_parent(parent);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// GameUiButton for interactive elements
|
||||
#[derive(Debug, Component)]
|
||||
pub struct GameUiButton;
|
||||
|
||||
/// Manage UI Buttons. interactive buttons.
|
||||
fn init_ui_button(events: Query<Entity, Added<GameUiButton>>, mut commands: Commands) {
|
||||
events.iter().for_each(|entity| {
|
||||
commands.entity(entity).insert((
|
||||
ButtonBundle {
|
||||
fn manage_names(
|
||||
events: Query<(Entity, &Name), (With<GameUi>, 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 {
|
||||
margin: UiRect::all(Val::Px(2.0)),
|
||||
padding: UiRect::all(Val::Px(2.0)),
|
||||
border: UiRect::all(Val::Px(2.0)),
|
||||
align_self: AlignSelf::FlexStart,
|
||||
padding: UiRect::all(Val::Px(3.0)),
|
||||
margin: UiRect::all(Val::Px(3.0)),
|
||||
..default()
|
||||
},
|
||||
background_color: BackgroundColor(Color::GREEN),
|
||||
border_color: BorderColor(Color::BLACK),
|
||||
background_color: BackgroundColor(Color::CRIMSON),
|
||||
..default()
|
||||
},
|
||||
UiElementState::Enabled,
|
||||
})
|
||||
.with_children(|parent| {
|
||||
parent.spawn(TextBundle::from_section(
|
||||
name.as_str(),
|
||||
TextStyle { ..default() },
|
||||
));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Manage button style for interactivity
|
||||
fn manage_button_interaction(
|
||||
mut events: Query<
|
||||
(&Interaction, &UiElementState, &mut BackgroundColor),
|
||||
(Changed<Interaction>, With<Button>),
|
||||
fn manage_tab(
|
||||
events: Query<
|
||||
(&Interaction, Entity, &Parent),
|
||||
(Changed<Interaction>, With<Button>, With<GameUi>),
|
||||
>,
|
||||
children: Query<&Children>,
|
||||
parents: Query<&Parent>,
|
||||
ui_elements: Query<(Entity, &GameUi)>,
|
||||
mut styles: Query<&mut Style>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
events
|
||||
.iter_mut()
|
||||
.for_each(|(interaction, state, mut bg_color)| {
|
||||
bg_color.0 = match state {
|
||||
UiElementState::Enabled => match interaction {
|
||||
Interaction::Pressed => Color::BLACK,
|
||||
Interaction::Hovered => Color::ORANGE,
|
||||
Interaction::None => Color::GREEN,
|
||||
},
|
||||
UiElementState::Active => Color::PINK,
|
||||
UiElementState::Error => Color::RED,
|
||||
UiElementState::Disabled => Color::GRAY,
|
||||
.iter()
|
||||
.for_each(|(interaction, interacted_entity, parent)| {
|
||||
// Checks:
|
||||
// entity pressed
|
||||
// entity is button
|
||||
// entity parent is a tab
|
||||
//
|
||||
// Find all other tabs
|
||||
// Display::None all except for tab button
|
||||
|
||||
match interaction {
|
||||
Interaction::Pressed => {
|
||||
children.get(parent.get()).iter().for_each(|&children| {
|
||||
children
|
||||
.iter()
|
||||
.filter(|&child| *child != interacted_entity)
|
||||
.for_each(|&child| {
|
||||
let mut style = styles.get_mut(child).expect("child has style");
|
||||
style.display = match style.display {
|
||||
Display::Flex => Display::None,
|
||||
Display::None => Display::Flex,
|
||||
_ => Display::Flex, // Not Reachable
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/// Manage the cursor icon for better immersion
|
||||
fn manage_cursor(
|
||||
mut primary_window: Query<&mut Window, With<PrimaryWindow>>,
|
||||
events: Query<&Interaction, (Changed<Interaction>, With<Button>)>,
|
||||
) {
|
||||
if !events.is_empty() {
|
||||
let mut window = primary_window.single_mut();
|
||||
window.cursor.icon = events
|
||||
.iter()
|
||||
.find(|&event| *event != Interaction::None)
|
||||
.map_or(CursorIcon::Default, |event| match event {
|
||||
Interaction::Hovered | Interaction::Pressed => CursorIcon::Hand,
|
||||
Interaction::None => CursorIcon::Help, // Shouldn't be reachable
|
||||
});
|
||||
_ => (),
|
||||
}
|
||||
// When tab button is clicked
|
||||
// For all children of parent (the tab)
|
||||
// If selected:
|
||||
// display: Display::Flex,
|
||||
// else:
|
||||
// Set display: Display::None,
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue