Merge branch '56-add-ui-library' into 'dev'

Add UI Library

See merge request softwaregrundprojekt/2025-2026/einzelprojekt/tutorium-moritz/bernroider-dominik/bernroider-dominik!15
This commit is contained in:
Dominik Bernroider
2025-11-28 14:57:03 +00:00
18 changed files with 377 additions and 363 deletions

View File

@@ -39,37 +39,27 @@ fn setup(mut commands: Commands) {
},
BackgroundColor(Color::srgba(0.0, 0.0, 0.0, 0.8)),
children![
(
TextType::Phase,
Text::new("..."),
TextFont::from_font_size(16.0),
TextColor(Color::WHITE)
),
(
TextType::Timer,
Text::new("--:--"),
TextFont::from_font_size(16.0),
TextColor(Color::WHITE)
),
(
Button,
text_with_component(TextType::Phase, "...", 16.0, Color::WHITE),
text_with_component(TextType::Timer, "...", 16.0, Color::WHITE),
button(
inventory::components::ButtonType::InventoryOpen,
Node::default(),
children![(
Text::new("Inventar"),
TextFont::from_font_size(16.0),
TextColor(Color::WHITE)
)]
ButtonVariant::Secondary,
Node {
padding: UiRect::all(px(10)),
..default()
},
"Inventar",
16.0
),
(
Button,
button(
ButtonType::SettingsOpen,
Node::default(),
children![(
Text::new("Einstellungen"),
TextFont::from_font_size(16.0),
TextColor(Color::WHITE)
)]
ButtonVariant::Secondary,
Node {
padding: UiRect::all(px(10)),
..default()
},
"Einstellungen",
16.0
)
],
));

View File

@@ -10,9 +10,7 @@ pub fn open_settings(commands: &mut Commands) {
position_type: PositionType::Absolute,
width: percent(100),
height: percent(100),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
..Node::center()
},
ZIndex(1),
BackgroundColor(Color::srgba(0.0, 0.0, 0.0, 0.8)),
@@ -21,10 +19,9 @@ pub fn open_settings(commands: &mut Commands) {
parent
.spawn((
Node {
flex_direction: FlexDirection::Column,
align_items: AlignItems::Center,
width: px(700),
padding: UiRect::all(px(20.0)),
..default()
..Node::vstack(px(20))
},
BackgroundColor(Color::srgb(0.2, 0.2, 0.2)),
BorderRadius::all(px(10.0)),
@@ -32,145 +29,97 @@ pub fn open_settings(commands: &mut Commands) {
.with_children(|parent| {
parent.spawn((
Node {
width: percent(100.0),
justify_content: JustifyContent::SpaceBetween,
align_items: AlignItems::Center,
margin: UiRect::bottom(px(20.0)),
column_gap: px(20.0),
..default()
..Node::hstack(px(20))
},
children![
(
Text::new("Spiel Einstellungen"),
TextFont::from_font_size(40.0),
TextColor(Color::WHITE),
),
(
Button,
text("Spiel Einstellungen", 40.0, Color::WHITE),
pill_button(
ButtonType::SettingsClose,
ButtonVariant::Destructive,
Node {
width: px(40.0),
height: px(40.0),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
width: px(40),
height: px(40),
..default()
},
BackgroundColor(Color::srgb(0.8, 0.2, 0.2)),
BorderRadius::MAX,
children![(
Text::new("X"),
TextFont::from_font_size(24.0),
TextColor(Color::WHITE),
)]
)
"X",
24.0
),
],
));
parent
.spawn(Node {
width: percent(100),
flex_direction: FlexDirection::Column,
margin: UiRect::top(px(10.0)),
row_gap: px(10.0),
..default()
})
.spawn(Node::vstack(px(10)))
.with_children(|parent| {
parent.spawn((
Button,
parent.spawn(button(
ButtonType::SettingsExit,
ButtonVariant::Secondary,
Node {
width: percent(100),
height: px(80),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
padding: UiRect::horizontal(px(10.0)),
padding: UiRect::all(px(10)),
..default()
},
BackgroundColor(NORMAL_BUTTON),
BorderRadius::all(px(10)),
children![(
Text::new("Spiel verlassen"),
TextFont::from_font_size(24.0),
TextColor(Color::WHITE)
)],
"Spiel verlassen",
24.0,
));
parent.spawn((
Button,
parent.spawn(button(
ButtonType::SettingsSave,
ButtonVariant::Secondary,
Node {
width: percent(100),
height: px(80),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
padding: UiRect::horizontal(px(10.0)),
padding: UiRect::all(px(10)),
..default()
},
BackgroundColor(NORMAL_BUTTON),
BorderRadius::all(px(10)),
children![(
Text::new("Spiel speichern"),
TextFont::from_font_size(24.0),
TextColor(Color::WHITE)
)],
"Spiel speichern",
24.0,
));
parent.spawn((
Node {
width: percent(100),
flex_direction: FlexDirection::Row,
justify_content: JustifyContent::SpaceBetween,
align_items: AlignItems::Center,
column_gap: px(10),
padding: UiRect::horizontal(px(10.0)),
..default()
justify_content: JustifyContent::Center,
..Node::hstack(px(30))
},
children![
(
Node {
flex_direction: FlexDirection::Column,
align_items: AlignItems::Center,
row_gap: px(10),
..default()
width: percent(40),
..Node::vstack(px(10))
},
children![
(
Text::new("Fokus Phase"),
TextFont::from_font_size(12.0),
TextColor(Color::WHITE)
text("Spiel Einstellungen", 18.0, Color::WHITE),
text(
"Tipp: Benutze [Umstellen] um in 10er Schritten zu inkrementieren oder dekrementieren!",
16.0,
Color::WHITE
),
]
),
(
Node {
align_items: AlignItems::Center,
..Node::vstack(px(10))
},
children![
text("Fokus Phase", 12.0, Color::WHITE),
timer_settings(TimerType::Focus)
]
),
(
Node {
flex_direction: FlexDirection::Column,
align_items: AlignItems::Center,
row_gap: px(10),
..default()
..Node::vstack(px(10))
},
children![
(
Text::new("Kurze Pause"),
TextFont::from_font_size(12.0),
TextColor(Color::WHITE)
),
text("Kurze Pause", 12.0, Color::WHITE),
timer_settings(TimerType::ShortBreak)
]
),
(
Node {
flex_direction: FlexDirection::Column,
align_items: AlignItems::Center,
row_gap: px(10),
..default()
..Node::vstack(px(10))
},
children![
(
Text::new("Lange Pause"),
TextFont::from_font_size(12.0),
TextColor(Color::WHITE)
),
text("Lange Pause", 12.0, Color::WHITE),
timer_settings(TimerType::LongBreak)
]
)

View File

@@ -4,17 +4,12 @@ use crate::prelude::*;
pub fn timer_settings(timer_type: TimerType) -> impl Bundle {
(
Node {
flex_direction: FlexDirection::Row,
align_items: AlignItems::Center,
..default()
..Node::hstack(px(0))
},
children![
timer_settings_part(SettingsTimerInput::Minutes(timer_type.clone()), 1),
(
Text::new(":"),
TextFont::from_font_size(24.0),
TextColor(Color::WHITE)
),
text(":", 24.0, Color::WHITE),
timer_settings_part(SettingsTimerInput::Seconds(timer_type.clone()), 1),
],
)
@@ -22,52 +17,34 @@ pub fn timer_settings(timer_type: TimerType) -> impl Bundle {
fn timer_settings_part(input: SettingsTimerInput, amount: u32) -> impl Bundle {
(
Node {
flex_direction: FlexDirection::Column,
..default()
},
Node::vstack(px(0)),
children![
(
Button,
button(
ButtonType::SettingsTimerChange {
input: input.clone(),
amount: amount as i32
},
ButtonVariant::Secondary,
Node {
width: auto(),
justify_content: JustifyContent::Center,
width: percent(100),
..default()
},
BackgroundColor(NORMAL_BUTTON),
children![
Text::new("+"),
TextFont::from_font_size(12.0),
TextColor(Color::WHITE)
]
"+",
12.0
),
(
input.clone(),
Text::new("--"),
TextFont::from_font_size(24.0),
TextColor(Color::WHITE)
),
(
Button,
text_with_component(input.clone(), "--", 24.0, Color::WHITE),
button(
ButtonType::SettingsTimerChange {
input: input.clone(),
amount: -(amount as i32),
amount: -(amount as i32)
},
ButtonVariant::Secondary,
Node {
width: auto(),
justify_content: JustifyContent::Center,
width: percent(100),
..default()
},
BackgroundColor(NORMAL_BUTTON),
children![
Text::new("-"),
TextFont::from_font_size(12.0),
TextColor(Color::WHITE)
]
"-",
12.0
),
],
)

View File

@@ -9,9 +9,7 @@ pub fn open_inventory(commands: &mut Commands, items: Query<&ItemStack>) {
position_type: PositionType::Absolute,
width: percent(100),
height: percent(100),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
..Node::center()
},
ZIndex(1),
BackgroundColor(Color::srgba(0.0, 0.0, 0.0, 0.8)),
@@ -20,10 +18,8 @@ pub fn open_inventory(commands: &mut Commands, items: Query<&ItemStack>) {
parent
.spawn((
Node {
flex_direction: FlexDirection::Column,
align_items: AlignItems::Center,
padding: UiRect::all(px(20.0)),
..default()
..Node::vstack(px(0))
},
BackgroundColor(Color::srgb(0.2, 0.2, 0.2)),
BorderRadius::all(px(10.0)),
@@ -33,45 +29,30 @@ pub fn open_inventory(commands: &mut Commands, items: Query<&ItemStack>) {
Node {
width: percent(100.0),
justify_content: JustifyContent::SpaceBetween,
align_items: AlignItems::Center,
margin: UiRect::bottom(px(20.0)),
column_gap: px(20.0),
..default()
..Node::hstack(px(20))
},
children![
(
Text::new("Inventar"),
TextFont::from_font_size(40.0),
TextColor(Color::WHITE),
),
(
Button,
text("Inventar", 40.0, Color::WHITE),
pill_button(
ButtonType::InventoryClose,
ButtonVariant::Destructive,
Node {
width: px(40.0),
height: px(40.0),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
width: px(40),
height: px(40),
..default()
},
BackgroundColor(Color::srgb(0.8, 0.2, 0.2)),
BorderRadius::MAX,
children![(
Text::new("X"),
TextFont::from_font_size(24.0),
TextColor(Color::WHITE),
)]
)
"X",
24.0
),
],
));
parent
.spawn(Node {
width: percent(100),
flex_direction: FlexDirection::Column,
margin: UiRect::top(px(10.0)),
row_gap: px(10.0),
..default()
..Node::vstack(px(10))
})
.with_children(|parent| {
for itemstack in items.iter() {

View File

@@ -8,13 +8,10 @@ pub fn list_itemstack(itemstack: &ItemStack) -> impl Bundle {
(
Node {
flex_direction: FlexDirection::Row,
column_gap: px(8),
align_items: AlignItems::Center,
padding: UiRect::all(px(4)),
..default()
..Node::hstack(px(8))
},
BackgroundColor(NORMAL_BUTTON),
BackgroundColor(ButtonVariant::Secondary.normal_background()),
BorderRadius::all(px(10)),
children![
(
@@ -24,28 +21,22 @@ pub fn list_itemstack(itemstack: &ItemStack) -> impl Bundle {
aspect_ratio: Some(1.0),
..default()
},
BackgroundColor(HOVERED_BUTTON),
BackgroundColor(ButtonVariant::Secondary.hover_background()),
BorderRadius::all(px(10))
),
(
Node {
flex_direction: FlexDirection::Column,
justify_content: JustifyContent::Center,
row_gap: px(4),
padding: UiRect::vertical(px(4)),
..default()
..Node::vstack(px(4))
},
children![
(
Text::new(format!("{} ({})", name, itemstack.amount)),
TextFont::from_font_size(14.0),
TextColor(Color::WHITE)
text(
format!("{} ({})", name, itemstack.amount),
14.0,
Color::WHITE
),
(
Text::new(itemstack.item_type.description()),
TextFont::from_font_size(10.0),
TextColor(Color::WHITE)
)
text(itemstack.item_type.description(), 10.0, Color::WHITE)
]
)
],

View File

@@ -92,3 +92,15 @@ impl SavegamePath {
Self::new(next_index)
}
}
#[derive(Component)]
pub enum RootMarker {
PopupSavegameLoad,
}
#[derive(Component)]
pub enum ButtonType {
SavegameLoad { savegame_path: SavegamePath },
SavegameDelete { savegame_path: SavegamePath },
PopupClose,
}

View File

@@ -1,4 +1,5 @@
use crate::features::phase::components::{SessionTracker, TimerSettings};
use crate::features::savegame::ui::load_popup_handler;
use crate::prelude::*;
use messages::*;
use std::fs::File;
@@ -6,6 +7,7 @@ use std::io::{Read, Write};
pub mod components;
pub mod messages;
pub mod ui;
pub struct SavegamePlugin;
@@ -16,6 +18,8 @@ impl Plugin for SavegamePlugin {
app.add_systems(Update, dump_savegame.run_if(in_state(AppState::GameScreen)));
app.add_systems(Update, load_savegame.run_if(in_state(AppState::GameScreen)));
app.add_systems(Update, load_popup_handler);
}
}

View File

@@ -1,5 +1,5 @@
use super::super::components::*;
use crate::prelude::*;
use super::super::components::{ButtonType, RootMarker};
use crate::{features::savegame::messages::SavegameLoadMessage, prelude::*};
pub fn spawn_load_popup(commands: &mut Commands) {
commands
@@ -9,9 +9,7 @@ pub fn spawn_load_popup(commands: &mut Commands) {
position_type: PositionType::Absolute,
width: percent(100),
height: percent(100),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
..Node::center()
},
ZIndex(1),
BackgroundColor(Color::srgba(0.0, 0.0, 0.0, 0.8)),
@@ -22,13 +20,12 @@ pub fn spawn_load_popup(commands: &mut Commands) {
Node {
width: px(600.0),
height: px(500.0),
flex_direction: FlexDirection::Column,
align_items: AlignItems::Center,
padding: UiRect::all(px(20.0)),
..default()
align_items: AlignItems::Center,
..Node::vstack(px(10))
},
BackgroundColor(Color::srgb(0.2, 0.2, 0.2)),
BorderRadius::all(Val::Px(10.0)),
BorderRadius::all(px(10.0)),
))
.with_children(|parent| {
parent.spawn((
@@ -40,27 +37,17 @@ pub fn spawn_load_popup(commands: &mut Commands) {
..default()
},
children![
(
Text::new("Spielstand Auswahl"),
TextFont::from_font_size(40.0),
TextColor(Color::WHITE),
),
(
Button,
text("Spielstand Auswahl", 40.0, Color::WHITE),
pill_button(
ButtonType::PopupClose,
ButtonVariant::Destructive,
Node {
width: px(40.0),
height: px(40.0),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
width: px(40),
height: px(40),
..default()
},
BackgroundColor(Color::srgb(0.8, 0.2, 0.2)),
children![(
Text::new("X"),
TextFont::from_font_size(24.0),
TextColor(Color::WHITE),
)]
"X",
24.0
)
],
));
@@ -78,20 +65,20 @@ pub fn spawn_load_popup(commands: &mut Commands) {
for savegame in SavegamePath::list() {
parent.spawn((
Button,
ButtonType::PopupSavegameLoad {
ButtonType::SavegameLoad {
savegame_path: savegame.path.clone(),
},
ButtonVariant::Secondary,
Node {
width: percent(100),
height: px(80),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
flex_direction: FlexDirection::Row,
column_gap: px(10.0),
padding: UiRect::horizontal(px(10.0)),
..default()
..Node::center()
},
BackgroundColor(NORMAL_BUTTON),
BackgroundColor(ButtonVariant::Secondary.normal_background()),
BorderRadius::all(px(10)),
children![
(
Node {
@@ -102,43 +89,35 @@ pub fn spawn_load_popup(commands: &mut Commands) {
..default()
},
children![
(
Text::new(format!(
"Spielstand {}",
savegame.index + 1
)),
TextFont::from_font_size(24.0),
TextColor(Color::srgb(0.9, 0.9, 0.9))
text(
format!("Spielstand {}", savegame.index + 1),
24.0,
Color::WHITE
),
(
Text::new(format!(
text(
format!(
"Beeren: {}, Fokusphasen abgeschlossen: {}",
savegame.total_berries,
savegame.completed_focus
)),
TextFont::from_font_size(18.0),
TextColor(Color::srgb(0.9, 0.9, 0.9))
)
),
18.0,
Color::WHITE,
),
]
),
(
Button,
ButtonType::PopupSavegameDelete {
pill_button(
ButtonType::SavegameDelete {
savegame_path: savegame.path.clone()
},
ButtonVariant::Destructive,
Node {
width: px(40.0),
height: px(40.0),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
width: px(40),
height: px(40),
..default()
},
children![(
Text::new("X"),
TextFont::from_font_size(24.0),
TextColor(Color::srgb(0.9, 0.9, 0.9))
)]
)
"X",
24.0
),
],
));
}
@@ -146,3 +125,38 @@ pub fn spawn_load_popup(commands: &mut Commands) {
});
});
}
pub fn load_popup_handler(
mut commands: Commands,
mut next_state: ResMut<NextState<AppState>>,
mut interaction_query: Query<(&Interaction, &ButtonType), (Changed<Interaction>, With<Button>)>,
root_query: Query<(Entity, &RootMarker)>,
mut savegame_messages: MessageWriter<SavegameLoadMessage>,
) {
for (interaction, button_type) in &mut interaction_query {
match *interaction {
Interaction::Pressed => {
match button_type {
ButtonType::PopupClose => {}
ButtonType::SavegameLoad { savegame_path } => {
commands.insert_resource(savegame_path.clone());
next_state.set(AppState::GameScreen);
savegame_messages.write(SavegameLoadMessage);
}
ButtonType::SavegameDelete { savegame_path } => {
if let Err(e) = std::fs::remove_file(savegame_path.clone().0) {
println!("Error while deleting savegame: {:?}", e);
}
}
};
for (entity, root) in root_query.iter() {
match *root {
RootMarker::PopupSavegameLoad => commands.entity(entity).despawn(),
}
}
}
_ => (),
}
}
}

View File

@@ -3,7 +3,6 @@ use crate::prelude::*;
#[derive(Component)]
pub enum RootMarker {
MainMenu,
PopupSavegameLoad,
}
#[derive(Component)]
@@ -11,7 +10,4 @@ pub enum ButtonType {
LoadGame,
NewGame,
Settings,
PopupSavegameLoad { savegame_path: SavegamePath },
PopupSavegameDelete { savegame_path: SavegamePath },
PopupClose,
}

View File

@@ -1,9 +1,8 @@
use crate::{features::savegame::messages::SavegameLoadMessage, prelude::*};
use crate::features::savegame::ui::spawn_load_popup;
use crate::prelude::*;
use components::*;
use ui::*;
pub mod components;
pub mod ui;
pub struct StartScreenPlugin;
@@ -21,68 +20,45 @@ fn setup(mut commands: Commands) {
Node {
width: percent(100),
height: percent(100),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
flex_direction: FlexDirection::Column,
..default()
row_gap: px(10),
..Node::center()
},
children![
(
Text::new("Pomonon Garten"),
TextFont::from_font_size(64.0),
TextColor(Color::srgb(0.9, 0.9, 0.9))
),
(
Button,
text("Pomomon Garden", 64.0, Color::WHITE),
button(
ButtonType::LoadGame,
ButtonVariant::Primary,
Node {
width: px(300),
height: px(65),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
width: px(280),
padding: UiRect::all(px(10)),
..default()
},
BackgroundColor(NORMAL_BUTTON),
children![(
Text::new("Spiel laden"),
TextFont::from_font_size(33.0),
TextColor(Color::srgb(0.9, 0.9, 0.9))
)]
"Spiel laden",
33.0
),
(
Button,
button(
ButtonType::NewGame,
ButtonVariant::Primary,
Node {
width: px(300),
height: px(65),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
width: px(280),
padding: UiRect::all(px(10)),
..default()
},
BackgroundColor(NORMAL_BUTTON),
children![(
Text::new("Neues Spiel"),
TextFont::from_font_size(33.0),
TextColor(Color::srgb(0.9, 0.9, 0.9))
)]
"Neues Spiel",
33.0,
),
(
Button,
button(
ButtonType::Settings,
ButtonVariant::Secondary,
Node {
width: px(300),
height: px(65),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
width: px(280),
padding: UiRect::all(px(10)),
..default()
},
BackgroundColor(NORMAL_BUTTON),
children![(
Text::new("Einstellungen"),
TextFont::from_font_size(33.0),
TextColor(Color::srgb(0.9, 0.9, 0.9))
)]
)
"Einstellungen",
33.0
),
],
));
}
@@ -90,18 +66,11 @@ fn setup(mut commands: Commands) {
fn menu(
mut commands: Commands,
mut next_state: ResMut<NextState<AppState>>,
mut interaction_query: Query<
(&Interaction, &ButtonType, &mut BackgroundColor),
(Changed<Interaction>, With<Button>),
>,
root_query: Query<(Entity, &RootMarker)>,
mut savegame_messages: MessageWriter<SavegameLoadMessage>,
mut interaction_query: Query<(&Interaction, &ButtonType), (Changed<Interaction>, With<Button>)>,
) {
for (interaction, button_type, mut color) in &mut interaction_query {
for (interaction, button_type) in &mut interaction_query {
match *interaction {
Interaction::Pressed => {
*color = PRESSED_BUTTON.into();
match button_type {
ButtonType::LoadGame => {
spawn_load_popup(&mut commands);
@@ -110,40 +79,10 @@ fn menu(
commands.insert_resource(SavegamePath::next());
next_state.set(AppState::GameScreen);
}
ButtonType::PopupClose => {
for (entity, root) in root_query.iter() {
match *root {
RootMarker::PopupSavegameLoad => commands.entity(entity).despawn(),
_ => {}
}
}
}
ButtonType::PopupSavegameLoad { savegame_path } => {
commands.insert_resource(savegame_path.clone());
next_state.set(AppState::GameScreen);
savegame_messages.write(SavegameLoadMessage);
}
ButtonType::PopupSavegameDelete { savegame_path } => {
if let Err(e) = std::fs::remove_file(savegame_path.clone().0) {
println!("Error while deleting savegame: {:?}", e);
}
for (entity, root) in root_query.iter() {
match *root {
RootMarker::PopupSavegameLoad => commands.entity(entity).despawn(),
_ => {}
}
}
}
_ => (),
ButtonType::Settings => todo!(),
};
}
Interaction::Hovered => {
*color = HOVERED_BUTTON.into();
}
Interaction::None => {
*color = NORMAL_BUTTON.into();
}
_ => (),
}
}
}

View File

@@ -6,3 +6,43 @@ pub struct Scroll {
pub entity: Entity,
pub delta: Vec2,
}
#[derive(Component, Clone)]
pub enum ButtonVariant {
Primary,
Secondary,
Destructive,
}
impl ButtonVariant {
pub fn normal_background(&self) -> Color {
match self {
ButtonVariant::Primary => Color::srgb(0.35, 0.17, 0.78),
ButtonVariant::Secondary => Color::srgb(0.15, 0.15, 0.15),
ButtonVariant::Destructive => Color::srgb(0.79, 0.17, 0.20),
}
}
pub fn hover_background(&self) -> Color {
match self {
ButtonVariant::Primary => Color::srgb(0.45, 0.27, 0.88),
ButtonVariant::Secondary => Color::srgb(0.25, 0.25, 0.25),
ButtonVariant::Destructive => Color::srgb(0.89, 0.27, 0.30),
}
}
pub fn pressed_background(&self) -> Color {
match self {
ButtonVariant::Primary => Color::srgb(0.55, 0.37, 0.98),
ButtonVariant::Secondary => Color::srgb(0.35, 0.35, 0.35),
ButtonVariant::Destructive => Color::srgb(0.99, 0.37, 0.40),
}
}
pub fn text_color(&self) -> Color {
match self {
ButtonVariant::Primary | ButtonVariant::Destructive => Color::srgb(0.1, 0.1, 0.1),
ButtonVariant::Secondary => Color::WHITE,
}
}
}

View File

@@ -1,8 +1,9 @@
use crate::prelude::*;
use crate::prelude::{button::update_buttons, *};
use bevy::{input::mouse::*, picking::hover::HoverMap};
pub mod components;
pub mod consts;
pub mod ui;
pub struct UiPlugin;
@@ -10,6 +11,8 @@ impl Plugin for UiPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Update, scroll_events);
app.add_observer(on_scroll_handler);
app.add_systems(Update, update_buttons);
}
}

View File

@@ -0,0 +1,59 @@
use crate::prelude::*;
pub fn button(
button_type: impl Component,
variant: ButtonVariant,
mut node: Node,
title: impl Into<String>,
font_size: f32,
) -> impl Bundle {
node.justify_content = JustifyContent::Center;
node.align_items = AlignItems::Center;
(
Button,
button_type,
variant.clone(),
node,
BackgroundColor(variant.normal_background()),
BorderRadius::all(px(10)),
children![text(title, font_size, variant.text_color())],
)
}
pub fn pill_button(
button_type: impl Component,
variant: ButtonVariant,
mut node: Node,
title: impl Into<String>,
font_size: f32,
) -> impl Bundle {
node.justify_content = JustifyContent::Center;
node.align_items = AlignItems::Center;
(
Button,
button_type,
variant.clone(),
node,
BackgroundColor(variant.normal_background()),
BorderRadius::MAX,
children![text(title, font_size, variant.text_color())],
)
}
pub fn update_buttons(
mut interaction_query: Query<
(&Interaction, &ButtonVariant, &mut BackgroundColor),
(Changed<Interaction>, With<Button>),
>,
) {
for (interaction, variant, mut color) in &mut interaction_query {
*color = match *interaction {
Interaction::None => variant.normal_background(),
Interaction::Hovered => variant.hover_background(),
Interaction::Pressed => variant.pressed_background(),
}
.into()
}
}

View File

@@ -0,0 +1,33 @@
use crate::prelude::*;
pub trait Flexbox {
fn hstack(spacing: Val) -> Self;
fn vstack(spacing: Val) -> Self;
fn center() -> Self;
}
impl Flexbox for Node {
fn hstack(spacing: Val) -> Self {
Self {
flex_direction: FlexDirection::Row,
column_gap: spacing,
..default()
}
}
fn vstack(spacing: Val) -> Self {
Self {
flex_direction: FlexDirection::Column,
row_gap: spacing,
..default()
}
}
fn center() -> Self {
Self {
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
}
}
}

View File

@@ -0,0 +1,7 @@
pub mod button;
pub mod flexbox;
pub mod texts;
pub use button::{button, pill_button};
pub use flexbox::Flexbox;
pub use texts::{text, text_with_component};

View File

@@ -0,0 +1,19 @@
pub use crate::prelude::*;
pub fn text(content: impl Into<String>, size: f32, color: Color) -> (Text, TextFont, TextColor) {
(
Text::new(content),
TextFont::from_font_size(size),
TextColor(color),
)
}
pub fn text_with_component(
component: impl Component,
content: impl Into<String>,
size: f32,
color: Color,
) -> impl Bundle {
let (a, b, c) = text(content, size, color);
(component, a, b, c)
}

View File

@@ -14,7 +14,7 @@ pub use crate::features::{
messages::{InteractStartMessage, MoveMessage},
},
savegame::components::SavegamePath,
ui::consts::*,
ui::{components::ButtonVariant, consts::*, ui::*},
};
pub use crate::utils::path::get_internal_path;
pub use bevy::prelude::*;