feat: Replace old popup with new UI element

This commit is contained in:
demenik
2025-12-09 13:21:51 +01:00
parent c50601c23a
commit d9a1c9c2a7
11 changed files with 115 additions and 322 deletions

View File

@@ -15,7 +15,6 @@ pub enum TextType {
#[derive(Component)] #[derive(Component)]
pub enum ButtonType { pub enum ButtonType {
SettingsOpen, SettingsOpen,
SettingsClose,
SettingsExit, SettingsExit,
SettingsSave, SettingsSave,
SettingsTimerChange { SettingsTimerChange {

View File

@@ -1,6 +1,5 @@
use crate::features::phase::components::TimerSettings; use crate::features::phase::components::TimerSettings;
use crate::features::savegame::messages::SavegameDumpMessage; use crate::features::savegame::messages::SavegameDumpMessage;
use crate::features::ui::messages::ClosePopupMessage;
use crate::features::{inventory, shop}; use crate::features::{inventory, shop};
use crate::prelude::*; use crate::prelude::*;
use components::*; use components::*;
@@ -17,27 +16,11 @@ impl Plugin for HudPlugin {
app.add_systems(OnExit(AppState::GameScreen), cleanup); app.add_systems(OnExit(AppState::GameScreen), cleanup);
app.add_systems( app.add_systems(
Update, Update,
(update_status, buttons, update_timer_settings, close_popup) (update_status, buttons, update_timer_settings).run_if(in_state(AppState::GameScreen)),
.run_if(in_state(AppState::GameScreen)),
); );
} }
} }
fn close_popup(
mut commands: Commands,
mut close_popup_reader: MessageReader<ClosePopupMessage>,
root_query: Query<(Entity, &RootMarker)>,
) {
for _ in close_popup_reader.read() {
for (entity, root) in root_query.iter() {
match *root {
RootMarker::Settings => commands.entity(entity).despawn(),
_ => {}
}
}
}
}
fn setup(mut commands: Commands) { fn setup(mut commands: Commands) {
commands.spawn(( commands.spawn((
RootMarker::Status, RootMarker::Status,
@@ -106,7 +89,6 @@ fn update_status(phase_res: Res<CurrentPhase>, mut text_query: Query<(&mut Text,
fn buttons( fn buttons(
mut commands: Commands, mut commands: Commands,
mut interaction_query: Query<(&Interaction, &ButtonType), (Changed<Interaction>, With<Button>)>, mut interaction_query: Query<(&Interaction, &ButtonType), (Changed<Interaction>, With<Button>)>,
root_query: Query<(Entity, &RootMarker)>,
mut savegame_messages: MessageWriter<SavegameDumpMessage>, mut savegame_messages: MessageWriter<SavegameDumpMessage>,
mut next_state: ResMut<NextState<AppState>>, mut next_state: ResMut<NextState<AppState>>,
mut timer_settings: ResMut<TimerSettings>, mut timer_settings: ResMut<TimerSettings>,
@@ -117,14 +99,6 @@ fn buttons(
ButtonType::SettingsOpen => { ButtonType::SettingsOpen => {
open_settings(&mut commands); open_settings(&mut commands);
} }
ButtonType::SettingsClose => {
for (entity, root) in root_query.iter() {
match *root {
RootMarker::Settings => commands.entity(entity).despawn(),
_ => {}
}
}
}
ButtonType::SettingsExit => { ButtonType::SettingsExit => {
savegame_messages.write(SavegameDumpMessage); savegame_messages.write(SavegameDumpMessage);
next_state.set(AppState::StartScreen); next_state.set(AppState::StartScreen);

View File

@@ -199,5 +199,4 @@ pub enum RootMarker {
#[derive(Component)] #[derive(Component)]
pub enum ButtonType { pub enum ButtonType {
InventoryOpen, InventoryOpen,
InventoryClose,
} }

View File

@@ -1,4 +1,3 @@
use crate::features::ui::messages::ClosePopupMessage;
use crate::{features::inventory::ui::open_inventory, prelude::*}; use crate::{features::inventory::ui::open_inventory, prelude::*};
use components::*; use components::*;
@@ -11,35 +10,17 @@ impl Plugin for InventoryPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.init_resource::<Inventory>(); app.init_resource::<Inventory>();
app.add_systems( app.add_systems(Update, buttons.run_if(in_state(AppState::GameScreen)));
Update,
(buttons, close_popup).run_if(in_state(AppState::GameScreen)),
);
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
app.add_systems(Update, debug_modify_berries); app.add_systems(Update, debug_modify_berries);
} }
} }
fn close_popup(
mut commands: Commands,
mut close_popup_reader: MessageReader<ClosePopupMessage>,
root_query: Query<(Entity, &RootMarker)>,
) {
for _ in close_popup_reader.read() {
for (entity, root) in root_query.iter() {
match *root {
RootMarker::Inventory => commands.entity(entity).despawn(),
}
}
}
}
fn buttons( fn buttons(
mut commands: Commands, mut commands: Commands,
mut interaction_query: Query<(&Interaction, &ButtonType), (Changed<Interaction>, With<Button>)>, mut interaction_query: Query<(&Interaction, &ButtonType), (Changed<Interaction>, With<Button>)>,
itemstack_query: Query<&ItemStack>, itemstack_query: Query<&ItemStack>,
root_query: Query<(Entity, &RootMarker)>,
game_config: Res<GameConfig>, game_config: Res<GameConfig>,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
) { ) {
@@ -49,13 +30,6 @@ fn buttons(
ButtonType::InventoryOpen => { ButtonType::InventoryOpen => {
open_inventory(&mut commands, itemstack_query, &game_config, &asset_server); open_inventory(&mut commands, itemstack_query, &game_config, &asset_server);
} }
ButtonType::InventoryClose => {
for (entity, root) in root_query.iter() {
match *root {
RootMarker::Inventory => commands.entity(entity).despawn(),
}
}
}
}, },
_ => {} _ => {}
} }

View File

@@ -1,4 +1,4 @@
use super::super::components::{ButtonType, RootMarker}; use super::super::components::RootMarker;
use crate::prelude::GameConfig; use crate::prelude::GameConfig;
use crate::{features::inventory::ui::list_itemstack, prelude::*}; use crate::{features::inventory::ui::list_itemstack, prelude::*};
@@ -8,63 +8,26 @@ pub fn open_inventory(
game_config: &Res<GameConfig>, game_config: &Res<GameConfig>,
asset_server: &Res<AssetServer>, asset_server: &Res<AssetServer>,
) { ) {
commands spawn_popup(
.spawn(( commands,
RootMarker::Inventory, RootMarker::Inventory,
Node { "Inventar",
position_type: PositionType::Absolute, Node::default(),
width: percent(100), |parent| {
height: percent(100),
..Node::center()
},
ZIndex(1),
BackgroundColor(Color::srgba(0.0, 0.0, 0.0, 0.8)),
GlobalTransform::default(),
))
.with_children(|parent| {
parent parent
.spawn(( .spawn(Node {
Node { width: percent(100),
padding: UiRect::all(px(20.0)), margin: UiRect::top(px(10.0)),
..Node::vstack(px(0)) ..Node::vstack(px(10))
}, })
BackgroundColor(Color::srgb(0.2, 0.2, 0.2)),
BorderRadius::all(px(10.0)),
))
.with_children(|parent| { .with_children(|parent| {
parent.spawn(( items
Node { .iter()
width: percent(100.0), .map(|item| list_itemstack(item, game_config, asset_server))
justify_content: JustifyContent::SpaceBetween, .for_each(|itemstack| {
margin: UiRect::bottom(px(20.0)), parent.spawn(itemstack);
..Node::hstack(px(20))
},
children![
text("Inventar", 40.0, Color::WHITE),
pill_button(
ButtonType::InventoryClose,
ButtonVariant::Destructive,
Node {
width: px(40),
height: px(40),
..default()
},
|color| text("X", 24.0, color)
),
],
));
parent
.spawn(Node {
width: percent(100),
margin: UiRect::top(px(10.0)),
..Node::vstack(px(10))
})
.with_children(|parent| {
for itemstack in items.iter() {
parent.spawn(list_itemstack(itemstack, game_config, asset_server));
}
}); });
}); });
}); },
);
} }

View File

@@ -102,5 +102,4 @@ pub enum RootMarker {
pub enum ButtonType { pub enum ButtonType {
SavegameLoad { savegame_path: SavegamePath }, SavegameLoad { savegame_path: SavegamePath },
SavegameDelete { savegame_path: SavegamePath }, SavegameDelete { savegame_path: SavegamePath },
PopupClose,
} }

View File

@@ -1,6 +1,5 @@
use crate::features::phase::components::{SessionTracker, TimerSettings}; use crate::features::phase::components::{SessionTracker, TimerSettings};
use crate::features::savegame::ui::load_popup_handler; use crate::features::savegame::ui::load_popup_handler;
use crate::features::ui::messages::ClosePopupMessage;
use crate::prelude::*; use crate::prelude::*;
use components::*; use components::*;
use messages::*; use messages::*;
@@ -21,21 +20,7 @@ impl Plugin for SavegamePlugin {
app.add_systems(Update, dump_savegame.run_if(in_state(AppState::GameScreen))); 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_savegame.run_if(in_state(AppState::GameScreen)));
app.add_systems(Update, (load_popup_handler, close_popup)); app.add_systems(Update, load_popup_handler);
}
}
fn close_popup(
mut commands: Commands,
mut close_popup_reader: MessageReader<ClosePopupMessage>,
root_query: Query<(Entity, &RootMarker)>,
) {
for _ in close_popup_reader.read() {
for (entity, root) in root_query.iter() {
match *root {
RootMarker::PopupSavegameLoad => commands.entity(entity).despawn(),
}
}
} }
} }

View File

@@ -2,126 +2,90 @@ use super::super::components::{ButtonType, RootMarker};
use crate::{features::savegame::messages::SavegameLoadMessage, prelude::*}; use crate::{features::savegame::messages::SavegameLoadMessage, prelude::*};
pub fn spawn_load_popup(commands: &mut Commands) { pub fn spawn_load_popup(commands: &mut Commands) {
commands spawn_popup(
.spawn(( commands,
RootMarker::PopupSavegameLoad, RootMarker::PopupSavegameLoad,
Node { "Spielstand wählen",
position_type: PositionType::Absolute, Node {
width: percent(100), width: px(600),
height: percent(100), height: px(500),
..Node::center() ..default()
}, },
ZIndex(1), |parent| {
BackgroundColor(Color::srgba(0.0, 0.0, 0.0, 0.8)),
GlobalTransform::default(),
))
.with_children(|parent| {
parent parent
.spawn(( .spawn(Node {
Node { width: percent(100),
width: px(600.0), flex_direction: FlexDirection::Column,
height: px(500.0), overflow: Overflow::scroll_y(),
padding: UiRect::all(px(20.0)), padding: UiRect::all(px(10)),
align_items: AlignItems::Center, row_gap: px(10),
..Node::vstack(px(10)) ..default()
}, })
BackgroundColor(Color::srgb(0.2, 0.2, 0.2)),
BorderRadius::all(px(10.0)),
))
.with_children(|parent| { .with_children(|parent| {
parent.spawn(( for savegame in SavegamePath::list() {
Node { parent.spawn(button(
width: percent(100.0), ButtonType::SavegameLoad {
justify_content: JustifyContent::SpaceBetween, savegame_path: savegame.path.clone(),
align_items: AlignItems::Center, },
margin: UiRect::bottom(px(20.0)), ButtonVariant::Secondary,
..default() Node {
}, width: percent(100),
children![ padding: UiRect::all(px(10)),
text("Spielstand Auswahl", 40.0, Color::WHITE), ..Node::center()
pill_button( },
ButtonType::PopupClose, |color| {
ButtonVariant::Destructive, (
Node { Node {
width: px(40), width: percent(100),
height: px(40), align_items: AlignItems::Center,
..default() ..Node::hstack(px(10))
}, },
|color| text("X", 24.0, color) children![
) (
],
));
parent
.spawn(Node {
width: percent(100),
flex_direction: FlexDirection::Column,
overflow: Overflow::scroll_y(),
margin: UiRect::all(px(20.0)),
row_gap: px(10.0),
..default()
})
.with_children(|parent| {
for savegame in SavegamePath::list() {
parent.spawn(
button(
ButtonType::SavegameLoad { savegame_path: savegame.path.clone() },
ButtonVariant::Secondary,
Node {
width: percent(100),
padding: UiRect::all(px(10)),
..Node::center()
},
|color| (
Node { Node {
width: percent(100), width: percent(100),
align_items: AlignItems::Center, height: percent(100),
..Node::hstack(px(10)) flex_direction: FlexDirection::Column,
justify_content: JustifyContent::Center,
..default()
}, },
children![( children![
Node { text(
width: percent(100), format!("Spielstand {}", savegame.index + 1),
height: percent(100), 24.0,
flex_direction: FlexDirection::Column, color
justify_content: JustifyContent::Center, ),
..default() text(
}, format!(
children![ "Beeren: {}, Fokusphasen abgeschlossen: {}",
text( savegame.total_berries,
format!("Spielstand {}", savegame.index + 1), savegame.completed_focus
24.0,
color
), ),
text( 18.0,
format!( Color::WHITE,
"Beeren: {}, Fokusphasen abgeschlossen: {}", ),
savegame.total_berries, ]
savegame.completed_focus ),
), pill_button(
18.0, ButtonType::SavegameDelete {
Color::WHITE, savegame_path: savegame.path.clone()
), },
] ButtonVariant::Destructive,
), Node {
pill_button( width: px(40),
ButtonType::SavegameDelete { height: px(40),
savegame_path: savegame.path.clone() ..default()
}, },
ButtonVariant::Destructive, |color| text("X", 24.0, color)
Node {
width: px(40),
height: px(40),
..default()
},
|color| text("X", 24.0, color)
)]
) )
), ],
); )
} },
}); ));
}
}); });
}); },
);
} }
pub fn load_popup_handler( pub fn load_popup_handler(
@@ -135,7 +99,6 @@ pub fn load_popup_handler(
match *interaction { match *interaction {
Interaction::Pressed => { Interaction::Pressed => {
match button_type { match button_type {
ButtonType::PopupClose => {}
ButtonType::SavegameLoad { savegame_path } => { ButtonType::SavegameLoad { savegame_path } => {
commands.insert_resource(savegame_path.clone()); commands.insert_resource(savegame_path.clone());
next_state.set(AppState::GameScreen); next_state.set(AppState::GameScreen);

View File

@@ -8,7 +8,6 @@ pub enum RootMarker {
#[derive(Component)] #[derive(Component)]
pub enum ButtonType { pub enum ButtonType {
ShopOpen, ShopOpen,
ShopClose,
ShopBuyItem(ShopOffer), ShopBuyItem(ShopOffer),
} }
@@ -71,4 +70,3 @@ impl ShopOffer {
} }
} }
} }

View File

@@ -1,4 +1,3 @@
use crate::features::ui::messages::ClosePopupMessage;
use crate::prelude::*; use crate::prelude::*;
use components::*; use components::*;
use ui::open_shop; use ui::open_shop;
@@ -10,24 +9,7 @@ pub struct ShopPlugin;
impl Plugin for ShopPlugin { impl Plugin for ShopPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems( app.add_systems(Update, buttons.run_if(in_state(AppState::GameScreen)));
Update,
(buttons, close_popup).run_if(in_state(AppState::GameScreen)),
);
}
}
fn close_popup(
mut commands: Commands,
mut close_popup_reader: MessageReader<ClosePopupMessage>,
root_query: Query<(Entity, &RootMarker)>,
) {
for _ in close_popup_reader.read() {
for (entity, root) in root_query.iter() {
match *root {
RootMarker::Shop => commands.entity(entity).despawn(),
}
}
} }
} }
@@ -46,13 +28,6 @@ fn buttons(
ButtonType::ShopOpen => { ButtonType::ShopOpen => {
open_shop(&mut commands, &game_config, &asset_server); open_shop(&mut commands, &game_config, &asset_server);
} }
ButtonType::ShopClose => {
for (entity, root) in root_query.iter() {
match *root {
RootMarker::Shop => commands.entity(entity).despawn(),
}
}
}
ButtonType::ShopBuyItem(offer) => { ButtonType::ShopBuyItem(offer) => {
if offer.buy(&mut inventory, &mut commands, &mut items) { if offer.buy(&mut inventory, &mut commands, &mut items) {
// Item bought, exit the menu // Item bought, exit the menu

View File

@@ -9,56 +9,20 @@ pub fn open_shop(
// TODO: calculate tile_count // TODO: calculate tile_count
let offers = ShopOffer::list_all(game_config, 0); let offers = ShopOffer::list_all(game_config, 0);
commands spawn_popup(
.spawn(( commands,
RootMarker::Shop, RootMarker::Shop,
Node { "Einkaufsladen",
position_type: PositionType::Absolute, Node {
width: percent(100), width: px(700),
height: percent(100), ..default()
..Node::center() },
}, |parent| {
ZIndex(1), parent.spawn(Node::vstack(px(10))).with_children(|parent| {
BackgroundColor(Color::srgba(0.0, 0.0, 0.0, 0.8)), for offer in offers {
GlobalTransform::default(), parent.spawn(shop_offer(&offer, game_config, asset_server));
)) }
.with_children(|parent| { });
parent },
.spawn(( );
Node {
width: px(700),
padding: UiRect::all(px(20.0)),
..Node::vstack(px(20))
},
BackgroundColor(Color::srgb(0.2, 0.2, 0.2)),
BorderRadius::all(px(10.0)),
))
.with_children(|parent| {
parent.spawn((
Node {
justify_content: JustifyContent::SpaceBetween,
..Node::hstack(px(20))
},
children![
text("Shop", 40.0, Color::WHITE),
pill_button(
ButtonType::ShopClose,
ButtonVariant::Destructive,
Node {
width: px(40),
height: px(40),
..default()
},
|color| text("X", 24.0, color)
),
],
));
parent.spawn(Node::vstack(px(10))).with_children(|parent| {
for offer in offers {
parent.spawn(shop_offer(&offer, game_config, asset_server));
}
});
});
});
} }