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};
|
use bevy::{prelude::*, window::PrimaryWindow};
|
||||||
|
|
||||||
pub struct GameUiPlugin;
|
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(
|
app.add_systems(PreUpdate, (spawn_nav, spawn_tab, spawn_set, spawn_button))
|
||||||
Update,
|
.add_systems(Update, (manage_names, manage_tab));
|
||||||
(
|
|
||||||
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,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_name(
|
/// Component for marking UI Nodes
|
||||||
events: Query<
|
#[derive(Component, PartialEq)]
|
||||||
(Entity, &Name),
|
pub enum GameUi {
|
||||||
(
|
/// Navigation container
|
||||||
Added<Name>,
|
Nav,
|
||||||
Or<(
|
/// Tab container
|
||||||
With<GameUiNav>,
|
Tab,
|
||||||
With<GameUiTab>,
|
/// Game UI Set
|
||||||
With<GameUiSet>,
|
Set,
|
||||||
With<GameUiList>,
|
/// Game UI Button
|
||||||
With<GameUiButton>,
|
Button,
|
||||||
)>,
|
|
||||||
),
|
|
||||||
>,
|
|
||||||
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()
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ui Navigation
|
fn spawn_nav(events: Query<(Entity, &GameUi), Added<GameUi>>, mut commands: Commands) {
|
||||||
#[derive(Debug, Component)]
|
events
|
||||||
pub struct GameUiNav;
|
.iter()
|
||||||
|
.filter(|(_, ui)| **ui == GameUi::Nav)
|
||||||
fn init_ui_nav(events: Query<Entity, Added<GameUiNav>>, mut commands: Commands) {
|
.for_each(|(entity, _)| {
|
||||||
events.iter().for_each(|entity| {
|
info!("Spawning Nav UI");
|
||||||
let parent = commands
|
commands.entity(entity).insert(NodeBundle {
|
||||||
.spawn(NodeBundle {
|
|
||||||
style: Style {
|
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()
|
..default()
|
||||||
},
|
},
|
||||||
background_color: BackgroundColor(Color::PINK),
|
background_color: BackgroundColor(Color::PURPLE),
|
||||||
..default()
|
|
||||||
})
|
|
||||||
.id();
|
|
||||||
|
|
||||||
commands
|
|
||||||
.entity(entity)
|
|
||||||
.insert(NodeBundle {
|
|
||||||
style: Style { ..default() },
|
|
||||||
..default()
|
..default()
|
||||||
})
|
});
|
||||||
.set_parent(parent);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ui Tab Element
|
fn spawn_tab(
|
||||||
#[derive(Debug, Component)]
|
events: Query<(Entity, &GameUi, &Name, &Parent), Added<GameUi>>,
|
||||||
pub struct GameUiTab;
|
mut commands: Commands,
|
||||||
|
) {
|
||||||
fn init_ui_tab(events: Query<Entity, Added<GameUiTab>>, mut commands: Commands) {
|
events
|
||||||
events.iter().for_each(|entity| {
|
.iter()
|
||||||
let parent = commands
|
.filter(|(_, ui, _, _)| **ui == GameUi::Tab)
|
||||||
|
.for_each(|(entity, _, name, parent)| {
|
||||||
|
info!("Spawning Tab");
|
||||||
|
// Create container for this tab
|
||||||
|
let parent_id = commands
|
||||||
.spawn(NodeBundle {
|
.spawn(NodeBundle {
|
||||||
style: Style {
|
style: Style {
|
||||||
flex_direction: FlexDirection::Column,
|
flex_direction: FlexDirection::Column,
|
||||||
|
padding: UiRect::all(Val::Px(3.0)),
|
||||||
|
margin: UiRect::all(Val::Px(3.0)),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
|
background_color: BackgroundColor(Color::GREEN),
|
||||||
..default()
|
..default()
|
||||||
})
|
})
|
||||||
|
.set_parent(parent.get())
|
||||||
|
.with_children(|parent| {
|
||||||
|
parent.spawn((GameUi::Button, name.clone(), NodeBundle { ..default() }));
|
||||||
|
})
|
||||||
.id();
|
.id();
|
||||||
|
|
||||||
|
// Insert spawn container with tab
|
||||||
commands
|
commands
|
||||||
.entity(entity)
|
.entity(entity)
|
||||||
|
.remove::<Name>()
|
||||||
.insert(NodeBundle {
|
.insert(NodeBundle {
|
||||||
style: Style {
|
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()
|
..default()
|
||||||
},
|
},
|
||||||
background_color: BackgroundColor(Color::TEAL),
|
background_color: BackgroundColor(Color::PINK),
|
||||||
..default()
|
..default()
|
||||||
})
|
})
|
||||||
.set_parent(parent);
|
.set_parent(parent_id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes the state of an element
|
fn spawn_set(events: Query<(Entity, &GameUi), Added<GameUi>>, mut commands: Commands) {
|
||||||
#[derive(Debug, Component)]
|
events
|
||||||
pub enum UiElementState {
|
.iter()
|
||||||
Enabled,
|
.filter(|(_, ui)| **ui == GameUi::Set)
|
||||||
Disabled,
|
.for_each(|(entity, _)| {
|
||||||
Active,
|
info!("Spawning UI Set");
|
||||||
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| {
|
|
||||||
commands.entity(entity).insert(NodeBundle {
|
commands.entity(entity).insert(NodeBundle {
|
||||||
style: Style {
|
style: Style {
|
||||||
flex_direction: FlexDirection::Column,
|
flex_direction: FlexDirection::Row,
|
||||||
justify_items: JustifyItems::Center,
|
flex_wrap: FlexWrap::Wrap,
|
||||||
border: UiRect::all(Val::Px(2.0)),
|
padding: UiRect::all(Val::Px(3.0)),
|
||||||
margin: UiRect::all(Val::Px(2.0)),
|
margin: UiRect::all(Val::Px(3.0)),
|
||||||
padding: UiRect::all(Val::Px(2.0)),
|
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
background_color: BackgroundColor(Color::RED),
|
background_color: BackgroundColor(Color::RED),
|
||||||
border_color: BorderColor(Color::BLACK),
|
|
||||||
..default()
|
..default()
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GameUiSet Component for holding collections of objects
|
fn spawn_button(events: Query<(Entity, &GameUi), Added<GameUi>>, mut commands: Commands) {
|
||||||
#[derive(Debug, Component)]
|
events
|
||||||
pub struct GameUiSet;
|
.iter()
|
||||||
|
.filter(|(_, ui)| **ui == GameUi::Button)
|
||||||
/// Manage UI Sets: collections of UI entities.
|
.for_each(|(entity, _)| {
|
||||||
fn init_ui_set(events: Query<Entity, Added<GameUiSet>>, mut commands: Commands) {
|
info!("Spawning UI Button");
|
||||||
events.iter().for_each(|entity| {
|
commands.entity(entity).insert(ButtonBundle {
|
||||||
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 {
|
|
||||||
style: Style {
|
style: Style {
|
||||||
flex_direction: FlexDirection::Row,
|
padding: UiRect::all(Val::Px(3.0)),
|
||||||
flex_wrap: FlexWrap::Wrap,
|
margin: UiRect::all(Val::Px(3.0)),
|
||||||
border: UiRect::all(Val::Px(2.0)),
|
|
||||||
margin: UiRect::all(Val::Px(2.0)),
|
|
||||||
padding: UiRect::all(Val::Px(2.0)),
|
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
background_color: BackgroundColor(Color::BLUE),
|
background_color: BackgroundColor(Color::BLUE),
|
||||||
border_color: BorderColor(Color::BLACK),
|
|
||||||
..default()
|
..default()
|
||||||
})
|
});
|
||||||
.set_parent(parent);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GameUiButton for interactive elements
|
fn manage_names(
|
||||||
#[derive(Debug, Component)]
|
events: Query<(Entity, &Name), (With<GameUi>, Or<(Added<Name>, Changed<Name>)>)>,
|
||||||
pub struct GameUiButton;
|
mut commands: Commands,
|
||||||
|
) {
|
||||||
/// Manage UI Buttons. interactive buttons.
|
events.iter().for_each(|(entity, name)| {
|
||||||
fn init_ui_button(events: Query<Entity, Added<GameUiButton>>, mut commands: Commands) {
|
commands.entity(entity).with_children(|parent| {
|
||||||
events.iter().for_each(|entity| {
|
parent
|
||||||
commands.entity(entity).insert((
|
.spawn(NodeBundle {
|
||||||
ButtonBundle {
|
|
||||||
style: Style {
|
style: Style {
|
||||||
margin: UiRect::all(Val::Px(2.0)),
|
align_self: AlignSelf::FlexStart,
|
||||||
padding: UiRect::all(Val::Px(2.0)),
|
padding: UiRect::all(Val::Px(3.0)),
|
||||||
border: UiRect::all(Val::Px(2.0)),
|
margin: UiRect::all(Val::Px(3.0)),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
background_color: BackgroundColor(Color::GREEN),
|
background_color: BackgroundColor(Color::CRIMSON),
|
||||||
border_color: BorderColor(Color::BLACK),
|
|
||||||
..default()
|
..default()
|
||||||
},
|
})
|
||||||
UiElementState::Enabled,
|
.with_children(|parent| {
|
||||||
|
parent.spawn(TextBundle::from_section(
|
||||||
|
name.as_str(),
|
||||||
|
TextStyle { ..default() },
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Manage button style for interactivity
|
fn manage_tab(
|
||||||
fn manage_button_interaction(
|
events: Query<
|
||||||
mut events: Query<
|
(&Interaction, Entity, &Parent),
|
||||||
(&Interaction, &UiElementState, &mut BackgroundColor),
|
(Changed<Interaction>, With<Button>, With<GameUi>),
|
||||||
(Changed<Interaction>, With<Button>),
|
|
||||||
>,
|
>,
|
||||||
|
children: Query<&Children>,
|
||||||
|
parents: Query<&Parent>,
|
||||||
|
ui_elements: Query<(Entity, &GameUi)>,
|
||||||
|
mut styles: Query<&mut Style>,
|
||||||
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
events
|
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,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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()
|
.iter()
|
||||||
.find(|&event| *event != Interaction::None)
|
.for_each(|(interaction, interacted_entity, parent)| {
|
||||||
.map_or(CursorIcon::Default, |event| match event {
|
// Checks:
|
||||||
Interaction::Hovered | Interaction::Pressed => CursorIcon::Hand,
|
// entity pressed
|
||||||
Interaction::None => CursorIcon::Help, // Shouldn't be reachable
|
// 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
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
// 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