|
|
|
@ -9,16 +9,118 @@ impl Plugin for GameUiPlugin {
|
|
|
|
app.add_systems(
|
|
|
|
app.add_systems(
|
|
|
|
Update,
|
|
|
|
Update,
|
|
|
|
(
|
|
|
|
(
|
|
|
|
manage_ui_list,
|
|
|
|
// UI lists
|
|
|
|
manage_ui_set,
|
|
|
|
init_ui_list,
|
|
|
|
manage_ui_button,
|
|
|
|
// UI Set
|
|
|
|
manage_cursor,
|
|
|
|
init_ui_set,
|
|
|
|
|
|
|
|
// Buttons
|
|
|
|
|
|
|
|
init_ui_button,
|
|
|
|
manage_button_interaction,
|
|
|
|
manage_button_interaction,
|
|
|
|
|
|
|
|
manage_button_title,
|
|
|
|
|
|
|
|
// Collapse systems
|
|
|
|
|
|
|
|
init_ui_collapse,
|
|
|
|
|
|
|
|
manage_collapse,
|
|
|
|
|
|
|
|
toggle_collapse,
|
|
|
|
|
|
|
|
// Cursor
|
|
|
|
|
|
|
|
manage_cursor,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Ui Navigation Element
|
|
|
|
|
|
|
|
#[derive(Debug, Component)]
|
|
|
|
|
|
|
|
struct UiNav;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Collapsed UI element
|
|
|
|
|
|
|
|
#[derive(Debug, Component)]
|
|
|
|
|
|
|
|
pub enum UiCollapse {
|
|
|
|
|
|
|
|
Show,
|
|
|
|
|
|
|
|
Hide,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Marker for UiCollapse Button
|
|
|
|
|
|
|
|
#[derive(Debug, Component)]
|
|
|
|
|
|
|
|
struct UiCollapseButton;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// When a UiCollapse entity is created, populate the open/close button
|
|
|
|
|
|
|
|
fn init_ui_collapse(
|
|
|
|
|
|
|
|
events: Query<(Entity, &UiCollapse), Added<UiCollapse>>,
|
|
|
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
events.iter().for_each(|(entity, collapse)| {
|
|
|
|
|
|
|
|
commands.entity(entity).with_children(|parent| {
|
|
|
|
|
|
|
|
let name = match collapse {
|
|
|
|
|
|
|
|
UiCollapse::Show => Name::new("Hide"),
|
|
|
|
|
|
|
|
UiCollapse::Hide => Name::new("Show"),
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
parent.spawn((
|
|
|
|
|
|
|
|
GameUiButton,
|
|
|
|
|
|
|
|
name,
|
|
|
|
|
|
|
|
UiCollapseButton,
|
|
|
|
|
|
|
|
NodeBundle {
|
|
|
|
|
|
|
|
style: Style {
|
|
|
|
|
|
|
|
top: Val::Px(0.0),
|
|
|
|
|
|
|
|
right: Val::Px(0.0),
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn manage_collapse(
|
|
|
|
|
|
|
|
events: Query<(&UiCollapse, &Children), Changed<UiCollapse>>,
|
|
|
|
|
|
|
|
mut visibility: Query<(&mut Visibility, &mut Style), Without<UiCollapseButton>>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
events.iter().for_each(|(collapse, children)| {
|
|
|
|
|
|
|
|
children.iter().for_each(|&child| {
|
|
|
|
|
|
|
|
if let Ok((mut vis, mut style)) = visibility.get_mut(child) {
|
|
|
|
|
|
|
|
match collapse {
|
|
|
|
|
|
|
|
UiCollapse::Show => {
|
|
|
|
|
|
|
|
*vis = Visibility::Inherited;
|
|
|
|
|
|
|
|
style.display = Display::Flex;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
UiCollapse::Hide => {
|
|
|
|
|
|
|
|
*vis = Visibility::Hidden;
|
|
|
|
|
|
|
|
style.display = Display::None;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn toggle_collapse(
|
|
|
|
|
|
|
|
mut events: Query<
|
|
|
|
|
|
|
|
(&Interaction, &Parent, &mut Name),
|
|
|
|
|
|
|
|
(Changed<Interaction>, With<UiCollapseButton>),
|
|
|
|
|
|
|
|
>,
|
|
|
|
|
|
|
|
mut collapses: Query<&mut UiCollapse>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
events
|
|
|
|
|
|
|
|
.iter_mut()
|
|
|
|
|
|
|
|
.for_each(|(interaction, parent, mut name)| match interaction {
|
|
|
|
|
|
|
|
Interaction::Pressed => {
|
|
|
|
|
|
|
|
if let Ok(mut collapse) = collapses.get_mut(parent.get()) {
|
|
|
|
|
|
|
|
match *collapse {
|
|
|
|
|
|
|
|
UiCollapse::Show => {
|
|
|
|
|
|
|
|
*name = Name::new("Show");
|
|
|
|
|
|
|
|
*collapse = UiCollapse::Hide;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
UiCollapse::Hide => {
|
|
|
|
|
|
|
|
*name = Name::new("Hide");
|
|
|
|
|
|
|
|
*collapse = UiCollapse::Show
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => (),
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Describes the state of an element
|
|
|
|
/// Describes the state of an element
|
|
|
|
#[derive(Debug, Component)]
|
|
|
|
#[derive(Debug, Component)]
|
|
|
|
pub enum UiElementState {
|
|
|
|
pub enum UiElementState {
|
|
|
|
@ -33,20 +135,23 @@ pub enum UiElementState {
|
|
|
|
pub struct GameUiList;
|
|
|
|
pub struct GameUiList;
|
|
|
|
|
|
|
|
|
|
|
|
/// Manage UI Lists: lists of UI entities.
|
|
|
|
/// Manage UI Lists: lists of UI entities.
|
|
|
|
fn manage_ui_list(events: Query<(Entity, &Name), Added<GameUiList>>, mut commands: Commands) {
|
|
|
|
fn init_ui_list(events: Query<(Entity, &Name), Added<GameUiList>>, mut commands: Commands) {
|
|
|
|
events.iter().for_each(|(entity, name)| {
|
|
|
|
events.iter().for_each(|(entity, name)| {
|
|
|
|
commands
|
|
|
|
commands
|
|
|
|
.entity(entity)
|
|
|
|
.entity(entity)
|
|
|
|
.insert(NodeBundle {
|
|
|
|
.insert(NodeBundle {
|
|
|
|
style: Style {
|
|
|
|
style: Style {
|
|
|
|
// width: Val::Px(100.0),
|
|
|
|
// align_content: AlignContent::FlexStart,
|
|
|
|
margin: UiRect::all(Val::Px(2.0)),
|
|
|
|
align_items: AlignItems::Stretch,
|
|
|
|
padding: UiRect::all(Val::Px(2.0)),
|
|
|
|
|
|
|
|
border: UiRect::all(Val::Px(2.0)),
|
|
|
|
border: UiRect::all(Val::Px(2.0)),
|
|
|
|
flex_direction: FlexDirection::Column,
|
|
|
|
flex_direction: FlexDirection::Column,
|
|
|
|
align_items: AlignItems::Stretch,
|
|
|
|
|
|
|
|
justify_items: JustifyItems::Center,
|
|
|
|
justify_items: JustifyItems::Center,
|
|
|
|
align_content: AlignContent::FlexStart,
|
|
|
|
margin: UiRect::all(Val::Px(2.0)),
|
|
|
|
|
|
|
|
padding: UiRect::all(Val::Px(2.0)),
|
|
|
|
|
|
|
|
// overflow: Overflow {
|
|
|
|
|
|
|
|
// x: OverflowAxis::Clip,
|
|
|
|
|
|
|
|
// y: OverflowAxis::Clip,
|
|
|
|
|
|
|
|
// },
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
background_color: BackgroundColor(Color::RED),
|
|
|
|
background_color: BackgroundColor(Color::RED),
|
|
|
|
@ -54,7 +159,13 @@ fn manage_ui_list(events: Query<(Entity, &Name), Added<GameUiList>>, mut command
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.with_children(|parent| {
|
|
|
|
.with_children(|parent| {
|
|
|
|
parent.spawn(TextBundle::from_section(name, TextStyle { ..default() }));
|
|
|
|
parent.spawn(
|
|
|
|
|
|
|
|
TextBundle::from_section(name, TextStyle { ..default() }).with_style(Style {
|
|
|
|
|
|
|
|
top: Val::Px(0.0),
|
|
|
|
|
|
|
|
left: Val::Px(0.0),
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -64,20 +175,23 @@ fn manage_ui_list(events: Query<(Entity, &Name), Added<GameUiList>>, mut command
|
|
|
|
pub struct GameUiSet;
|
|
|
|
pub struct GameUiSet;
|
|
|
|
|
|
|
|
|
|
|
|
/// Manage UI Sets: collections of UI entities.
|
|
|
|
/// Manage UI Sets: collections of UI entities.
|
|
|
|
fn manage_ui_set(events: Query<(Entity, &Name), Added<GameUiSet>>, mut commands: Commands) {
|
|
|
|
fn init_ui_set(events: Query<(Entity, &Name), Added<GameUiSet>>, mut commands: Commands) {
|
|
|
|
events.iter().for_each(|(entity, name)| {
|
|
|
|
events.iter().for_each(|(entity, name)| {
|
|
|
|
commands
|
|
|
|
commands
|
|
|
|
.entity(entity)
|
|
|
|
.entity(entity)
|
|
|
|
.insert(NodeBundle {
|
|
|
|
.insert(NodeBundle {
|
|
|
|
style: Style {
|
|
|
|
style: Style {
|
|
|
|
width: Val::Px(200.0),
|
|
|
|
// align_content: AlignContent::FlexStart,
|
|
|
|
margin: UiRect::all(Val::Px(2.0)),
|
|
|
|
|
|
|
|
padding: UiRect::all(Val::Px(2.0)),
|
|
|
|
|
|
|
|
border: UiRect::all(Val::Px(2.0)),
|
|
|
|
|
|
|
|
align_items: AlignItems::FlexStart,
|
|
|
|
align_items: AlignItems::FlexStart,
|
|
|
|
align_content: AlignContent::FlexStart,
|
|
|
|
border: UiRect::all(Val::Px(2.0)),
|
|
|
|
flex_direction: FlexDirection::Row,
|
|
|
|
flex_direction: FlexDirection::Row,
|
|
|
|
flex_wrap: FlexWrap::Wrap,
|
|
|
|
flex_wrap: FlexWrap::Wrap,
|
|
|
|
|
|
|
|
margin: UiRect::all(Val::Px(2.0)),
|
|
|
|
|
|
|
|
padding: UiRect::all(Val::Px(2.0)),
|
|
|
|
|
|
|
|
// overflow: Overflow {
|
|
|
|
|
|
|
|
// x: OverflowAxis::Clip,
|
|
|
|
|
|
|
|
// y: OverflowAxis::Clip,
|
|
|
|
|
|
|
|
// },
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
background_color: BackgroundColor(Color::BLUE),
|
|
|
|
background_color: BackgroundColor(Color::BLUE),
|
|
|
|
@ -85,7 +199,13 @@ fn manage_ui_set(events: Query<(Entity, &Name), Added<GameUiSet>>, mut commands:
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.with_children(|parent| {
|
|
|
|
.with_children(|parent| {
|
|
|
|
parent.spawn(TextBundle::from_section(name, TextStyle { ..default() }));
|
|
|
|
parent.spawn(
|
|
|
|
|
|
|
|
TextBundle::from_section(name, TextStyle { ..default() }).with_style(Style {
|
|
|
|
|
|
|
|
top: Val::Px(0.0),
|
|
|
|
|
|
|
|
left: Val::Px(0.0),
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -95,7 +215,7 @@ fn manage_ui_set(events: Query<(Entity, &Name), Added<GameUiSet>>, mut commands:
|
|
|
|
pub struct GameUiButton;
|
|
|
|
pub struct GameUiButton;
|
|
|
|
|
|
|
|
|
|
|
|
/// Manage UI Buttons. interactive buttons.
|
|
|
|
/// Manage UI Buttons. interactive buttons.
|
|
|
|
fn manage_ui_button(events: Query<(Entity, &Name), Added<GameUiButton>>, mut commands: Commands) {
|
|
|
|
fn init_ui_button(events: Query<(Entity, &Name), Added<GameUiButton>>, mut commands: Commands) {
|
|
|
|
events.iter().for_each(|(entity, name)| {
|
|
|
|
events.iter().for_each(|(entity, name)| {
|
|
|
|
commands
|
|
|
|
commands
|
|
|
|
.entity(entity)
|
|
|
|
.entity(entity)
|
|
|
|
@ -120,23 +240,7 @@ fn manage_ui_button(events: Query<(Entity, &Name), Added<GameUiButton>>, mut com
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Manage the cursor icon for better immersion
|
|
|
|
/// Manage button style for interactivity
|
|
|
|
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
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn manage_button_interaction(
|
|
|
|
fn manage_button_interaction(
|
|
|
|
mut events: Query<
|
|
|
|
mut events: Query<
|
|
|
|
(&Interaction, &UiElementState, &mut BackgroundColor),
|
|
|
|
(&Interaction, &UiElementState, &mut BackgroundColor),
|
|
|
|
@ -158,3 +262,33 @@ fn manage_button_interaction(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn manage_button_title(
|
|
|
|
|
|
|
|
events: Query<(&Name, &Children), (Changed<Name>, With<Button>)>,
|
|
|
|
|
|
|
|
mut texts: Query<&mut Text, With<Parent>>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
events.iter().for_each(|(name, children)| {
|
|
|
|
|
|
|
|
children.iter().for_each(|&child| {
|
|
|
|
|
|
|
|
if let Ok(mut text) = texts.get_mut(child) {
|
|
|
|
|
|
|
|
text.sections[0].value = name.into();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// 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
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|