You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

251 lines
7.4 KiB
Rust

/// TODO: Sorted list/set
///
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,
),
);
}
}
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()
}),
);
});
});
}
/// 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 {
style: Style {
flex_direction: FlexDirection::Row,
..default()
},
background_color: BackgroundColor(Color::PINK),
..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
.spawn(NodeBundle {
style: Style {
flex_direction: FlexDirection::Column,
..default()
},
..default()
})
.id();
commands
.entity(entity)
.insert(NodeBundle {
style: Style {
flex_wrap: FlexWrap::Wrap,
..default()
},
background_color: BackgroundColor(Color::TEAL),
..default()
})
.set_parent(parent);
});
}
/// 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| {
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)),
..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 {
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)),
..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 {
style: Style {
margin: UiRect::all(Val::Px(2.0)),
padding: UiRect::all(Val::Px(2.0)),
border: UiRect::all(Val::Px(2.0)),
..default()
},
background_color: BackgroundColor(Color::GREEN),
border_color: BorderColor(Color::BLACK),
..default()
},
UiElementState::Enabled,
));
});
}
/// Manage button style for interactivity
fn manage_button_interaction(
mut events: Query<
(&Interaction, &UiElementState, &mut BackgroundColor),
(Changed<Interaction>, With<Button>),
>,
) {
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()
.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
});
}
}