Merge branch '59-popup-and-context-menu-ui-element' into 'dev'
Popup and Context Menu UI Element See merge request softwaregrundprojekt/2025-2026/einzelprojekt/tutorium-moritz/bernroider-dominik/bernroider-dominik!30
This commit is contained in:
@@ -24,10 +24,10 @@
|
||||
|
||||
rust-toolchain = with fenix.packages.${system};
|
||||
combine [
|
||||
stable.rustc
|
||||
stable.cargo
|
||||
stable.rust-src
|
||||
stable.rust-analyzer
|
||||
beta.rustc
|
||||
beta.cargo
|
||||
beta.rust-src
|
||||
beta.rust-analyzer
|
||||
];
|
||||
|
||||
bevyDeps = with pkgs; [
|
||||
|
||||
@@ -15,7 +15,6 @@ pub enum TextType {
|
||||
#[derive(Component)]
|
||||
pub enum ButtonType {
|
||||
SettingsOpen,
|
||||
SettingsClose,
|
||||
SettingsExit,
|
||||
SettingsSave,
|
||||
SettingsTimerChange {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::features::phase::components::TimerSettings;
|
||||
use crate::features::savegame::messages::SavegameDumpMessage;
|
||||
use crate::features::ui::messages::ClosePopupMessage;
|
||||
use crate::features::{inventory, shop};
|
||||
use crate::prelude::*;
|
||||
use components::*;
|
||||
@@ -17,27 +16,11 @@ impl Plugin for HudPlugin {
|
||||
app.add_systems(OnExit(AppState::GameScreen), cleanup);
|
||||
app.add_systems(
|
||||
Update,
|
||||
(update_status, buttons, update_timer_settings, close_popup)
|
||||
.run_if(in_state(AppState::GameScreen)),
|
||||
(update_status, buttons, update_timer_settings).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) {
|
||||
commands.spawn((
|
||||
RootMarker::Status,
|
||||
@@ -61,28 +44,19 @@ fn setup(mut commands: Commands) {
|
||||
button(
|
||||
shop::components::ButtonType::ShopOpen,
|
||||
ButtonVariant::Secondary,
|
||||
Node {
|
||||
padding: UiRect::all(px(10)),
|
||||
..default()
|
||||
},
|
||||
Node::from_padding(UiRect::all(px(10))),
|
||||
|color| text("Shop [P]", 16.0, color)
|
||||
),
|
||||
button(
|
||||
inventory::components::ButtonType::InventoryOpen,
|
||||
ButtonVariant::Secondary,
|
||||
Node {
|
||||
padding: UiRect::all(px(10)),
|
||||
..default()
|
||||
},
|
||||
Node::from_padding(UiRect::all(px(10))),
|
||||
|color| text("Inventar", 16.0, color)
|
||||
),
|
||||
button(
|
||||
ButtonType::SettingsOpen,
|
||||
ButtonVariant::Secondary,
|
||||
Node {
|
||||
padding: UiRect::all(px(10)),
|
||||
..default()
|
||||
},
|
||||
Node::from_padding(UiRect::all(px(10))),
|
||||
|color| text("Einstellungen", 16.0, color)
|
||||
)
|
||||
],
|
||||
@@ -106,7 +80,6 @@ fn update_status(phase_res: Res<CurrentPhase>, mut text_query: Query<(&mut Text,
|
||||
fn buttons(
|
||||
mut commands: Commands,
|
||||
mut interaction_query: Query<(&Interaction, &ButtonType), (Changed<Interaction>, With<Button>)>,
|
||||
root_query: Query<(Entity, &RootMarker)>,
|
||||
mut savegame_messages: MessageWriter<SavegameDumpMessage>,
|
||||
mut next_state: ResMut<NextState<AppState>>,
|
||||
mut timer_settings: ResMut<TimerSettings>,
|
||||
@@ -117,14 +90,6 @@ fn buttons(
|
||||
ButtonType::SettingsOpen => {
|
||||
open_settings(&mut commands);
|
||||
}
|
||||
ButtonType::SettingsClose => {
|
||||
for (entity, root) in root_query.iter() {
|
||||
match *root {
|
||||
RootMarker::Settings => commands.entity(entity).despawn(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
ButtonType::SettingsExit => {
|
||||
savegame_messages.write(SavegameDumpMessage);
|
||||
next_state.set(AppState::StartScreen);
|
||||
|
||||
@@ -3,75 +3,30 @@ use super::timer_settings::timer_settings;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn open_settings(commands: &mut Commands) {
|
||||
commands
|
||||
.spawn((
|
||||
spawn_popup(
|
||||
commands,
|
||||
RootMarker::Settings,
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
width: percent(100),
|
||||
height: percent(100),
|
||||
..Node::center()
|
||||
},
|
||||
ZIndex(1),
|
||||
BackgroundColor(Color::srgba(0.0, 0.0, 0.0, 0.8)),
|
||||
GlobalTransform::default(),
|
||||
))
|
||||
.with_children(|parent| {
|
||||
parent
|
||||
.spawn((
|
||||
"Spiel Einstellungen",
|
||||
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("Spiel Einstellungen", 40.0, Color::WHITE),
|
||||
pill_button(
|
||||
ButtonType::SettingsClose,
|
||||
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| {
|
||||
parent.spawn(button(
|
||||
|parent| {
|
||||
parent.spawn((
|
||||
Node::vstack(px(10)),
|
||||
children![
|
||||
button(
|
||||
ButtonType::SettingsExit,
|
||||
ButtonVariant::Secondary,
|
||||
Node {
|
||||
padding: UiRect::all(px(10)),
|
||||
..default()
|
||||
},
|
||||
Node::from_padding(UiRect::all(px(10))),
|
||||
|color| text("Spiel verlassen", 24.0, color)
|
||||
));
|
||||
|
||||
parent.spawn(button(
|
||||
),
|
||||
button(
|
||||
ButtonType::SettingsSave,
|
||||
ButtonVariant::Secondary,
|
||||
Node {
|
||||
padding: UiRect::all(px(10)),
|
||||
..default()
|
||||
},
|
||||
Node::from_padding(UiRect::all(px(10))),
|
||||
|color| text("Spiel speichern", 24.0, color)
|
||||
));
|
||||
|
||||
parent.spawn((
|
||||
),(
|
||||
Node {
|
||||
justify_content: JustifyContent::Center,
|
||||
..Node::hstack(px(30))
|
||||
@@ -122,8 +77,9 @@ pub fn open_settings(commands: &mut Commands) {
|
||||
]
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
));
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -199,5 +199,4 @@ pub enum RootMarker {
|
||||
#[derive(Component)]
|
||||
pub enum ButtonType {
|
||||
InventoryOpen,
|
||||
InventoryClose,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use crate::features::ui::messages::ClosePopupMessage;
|
||||
use crate::{features::inventory::ui::open_inventory, prelude::*};
|
||||
use components::*;
|
||||
|
||||
@@ -11,35 +10,17 @@ impl Plugin for InventoryPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_resource::<Inventory>();
|
||||
|
||||
app.add_systems(
|
||||
Update,
|
||||
(buttons, close_popup).run_if(in_state(AppState::GameScreen)),
|
||||
);
|
||||
app.add_systems(Update, buttons.run_if(in_state(AppState::GameScreen)));
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
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(
|
||||
mut commands: Commands,
|
||||
mut interaction_query: Query<(&Interaction, &ButtonType), (Changed<Interaction>, With<Button>)>,
|
||||
itemstack_query: Query<&ItemStack>,
|
||||
root_query: Query<(Entity, &RootMarker)>,
|
||||
game_config: Res<GameConfig>,
|
||||
asset_server: Res<AssetServer>,
|
||||
) {
|
||||
@@ -49,13 +30,6 @@ fn buttons(
|
||||
ButtonType::InventoryOpen => {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::super::components::{ButtonType, RootMarker};
|
||||
use super::super::components::RootMarker;
|
||||
use crate::prelude::GameConfig;
|
||||
use crate::{features::inventory::ui::list_itemstack, prelude::*};
|
||||
|
||||
@@ -8,52 +8,12 @@ pub fn open_inventory(
|
||||
game_config: &Res<GameConfig>,
|
||||
asset_server: &Res<AssetServer>,
|
||||
) {
|
||||
commands
|
||||
.spawn((
|
||||
spawn_popup(
|
||||
commands,
|
||||
RootMarker::Inventory,
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
width: percent(100),
|
||||
height: percent(100),
|
||||
..Node::center()
|
||||
},
|
||||
ZIndex(1),
|
||||
BackgroundColor(Color::srgba(0.0, 0.0, 0.0, 0.8)),
|
||||
GlobalTransform::default(),
|
||||
))
|
||||
.with_children(|parent| {
|
||||
parent
|
||||
.spawn((
|
||||
Node {
|
||||
padding: UiRect::all(px(20.0)),
|
||||
..Node::vstack(px(0))
|
||||
},
|
||||
BackgroundColor(Color::srgb(0.2, 0.2, 0.2)),
|
||||
BorderRadius::all(px(10.0)),
|
||||
))
|
||||
.with_children(|parent| {
|
||||
parent.spawn((
|
||||
Node {
|
||||
width: percent(100.0),
|
||||
justify_content: JustifyContent::SpaceBetween,
|
||||
margin: UiRect::bottom(px(20.0)),
|
||||
..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)
|
||||
),
|
||||
],
|
||||
));
|
||||
|
||||
"Inventar",
|
||||
Node::default(),
|
||||
|parent| {
|
||||
parent
|
||||
.spawn(Node {
|
||||
width: percent(100),
|
||||
@@ -61,10 +21,13 @@ pub fn open_inventory(
|
||||
..Node::vstack(px(10))
|
||||
})
|
||||
.with_children(|parent| {
|
||||
for itemstack in items.iter() {
|
||||
parent.spawn(list_itemstack(itemstack, game_config, asset_server));
|
||||
}
|
||||
});
|
||||
items
|
||||
.iter()
|
||||
.map(|item| list_itemstack(item, game_config, asset_server))
|
||||
.for_each(|itemstack| {
|
||||
parent.spawn(itemstack);
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ pub enum ButtonType {
|
||||
Cancel,
|
||||
}
|
||||
|
||||
pub fn spawn_context_menu(
|
||||
pub fn open_context_menu(
|
||||
mut commands: Commands,
|
||||
mut tile_click_messages: MessageReader<TileClickMessage>,
|
||||
root_query: Query<Entity, With<RootMarker>>,
|
||||
@@ -57,22 +57,11 @@ pub fn spawn_context_menu(
|
||||
let options =
|
||||
InteractionAction::list_options(tile_state, &inventory, item_query, &game_config);
|
||||
|
||||
commands
|
||||
.spawn((
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
left: px(screen_pos.x),
|
||||
top: px(screen_pos.y),
|
||||
padding: UiRect::all(px(5.0)),
|
||||
..Node::vstack(px(5.0))
|
||||
},
|
||||
ZIndex(100),
|
||||
BackgroundColor(Color::srgb(0.1, 0.1, 0.1)),
|
||||
BorderRadius::all(px(5)),
|
||||
spawn_context_menu(
|
||||
&mut commands,
|
||||
RootMarker::ContextMenu,
|
||||
GlobalTransform::default(),
|
||||
))
|
||||
.with_children(|parent| {
|
||||
screen_pos,
|
||||
|parent| {
|
||||
for option in options {
|
||||
parent.spawn(button(
|
||||
ButtonType::Interact {
|
||||
@@ -81,10 +70,7 @@ pub fn spawn_context_menu(
|
||||
action: option.clone(),
|
||||
},
|
||||
ButtonVariant::Primary,
|
||||
Node {
|
||||
padding: UiRect::all(px(5)),
|
||||
..default()
|
||||
},
|
||||
Node::from_padding(UiRect::all(px(5))),
|
||||
|c| text(option.clone().get_name(&game_config), 20.0, c), // TODO: add sprite
|
||||
));
|
||||
}
|
||||
@@ -92,13 +78,11 @@ pub fn spawn_context_menu(
|
||||
parent.spawn(button(
|
||||
ButtonType::Cancel,
|
||||
ButtonVariant::Destructive,
|
||||
Node {
|
||||
padding: UiRect::all(px(5)),
|
||||
..default()
|
||||
},
|
||||
Node::from_padding(UiRect::all(px(5))),
|
||||
|c| text("Abbrechen", 20.0, c),
|
||||
));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ impl Plugin for PomUiPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(
|
||||
Update,
|
||||
context_menu::spawn_context_menu.run_if(in_state(AppState::GameScreen)),
|
||||
context_menu::open_context_menu.run_if(in_state(AppState::GameScreen)),
|
||||
);
|
||||
app.add_systems(
|
||||
Update,
|
||||
|
||||
@@ -102,5 +102,4 @@ pub enum RootMarker {
|
||||
pub enum ButtonType {
|
||||
SavegameLoad { savegame_path: SavegamePath },
|
||||
SavegameDelete { savegame_path: SavegamePath },
|
||||
PopupClose,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::features::phase::components::{SessionTracker, TimerSettings};
|
||||
use crate::features::savegame::ui::load_popup_handler;
|
||||
use crate::features::ui::messages::ClosePopupMessage;
|
||||
use crate::prelude::*;
|
||||
use components::*;
|
||||
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, load_savegame.run_if(in_state(AppState::GameScreen)));
|
||||
|
||||
app.add_systems(Update, (load_popup_handler, close_popup));
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
app.add_systems(Update, load_popup_handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,83 +2,46 @@ use super::super::components::{ButtonType, RootMarker};
|
||||
use crate::{features::savegame::messages::SavegameLoadMessage, prelude::*};
|
||||
|
||||
pub fn spawn_load_popup(commands: &mut Commands) {
|
||||
commands
|
||||
.spawn((
|
||||
spawn_popup(
|
||||
commands,
|
||||
RootMarker::PopupSavegameLoad,
|
||||
"Spielstand wählen",
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
width: percent(100),
|
||||
height: percent(100),
|
||||
..Node::center()
|
||||
},
|
||||
ZIndex(1),
|
||||
BackgroundColor(Color::srgba(0.0, 0.0, 0.0, 0.8)),
|
||||
GlobalTransform::default(),
|
||||
))
|
||||
.with_children(|parent| {
|
||||
parent
|
||||
.spawn((
|
||||
Node {
|
||||
width: px(600.0),
|
||||
height: px(500.0),
|
||||
padding: UiRect::all(px(20.0)),
|
||||
align_items: AlignItems::Center,
|
||||
..Node::vstack(px(10))
|
||||
},
|
||||
BackgroundColor(Color::srgb(0.2, 0.2, 0.2)),
|
||||
BorderRadius::all(px(10.0)),
|
||||
))
|
||||
.with_children(|parent| {
|
||||
parent.spawn((
|
||||
Node {
|
||||
width: percent(100.0),
|
||||
justify_content: JustifyContent::SpaceBetween,
|
||||
align_items: AlignItems::Center,
|
||||
margin: UiRect::bottom(px(20.0)),
|
||||
width: px(600),
|
||||
height: px(500),
|
||||
..default()
|
||||
},
|
||||
children![
|
||||
text("Spielstand Auswahl", 40.0, Color::WHITE),
|
||||
pill_button(
|
||||
ButtonType::PopupClose,
|
||||
ButtonVariant::Destructive,
|
||||
Node {
|
||||
width: px(40),
|
||||
height: px(40),
|
||||
..default()
|
||||
},
|
||||
|color| text("X", 24.0, color)
|
||||
)
|
||||
],
|
||||
));
|
||||
|
||||
|parent| {
|
||||
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),
|
||||
padding: UiRect::all(px(10)),
|
||||
row_gap: px(10),
|
||||
..default()
|
||||
})
|
||||
.with_children(|parent| {
|
||||
for savegame in SavegamePath::list() {
|
||||
parent.spawn(
|
||||
button(
|
||||
ButtonType::SavegameLoad { savegame_path: savegame.path.clone() },
|
||||
parent.spawn(button(
|
||||
ButtonType::SavegameLoad {
|
||||
savegame_path: savegame.path.clone(),
|
||||
},
|
||||
ButtonVariant::Secondary,
|
||||
Node {
|
||||
width: percent(100),
|
||||
padding: UiRect::all(px(10)),
|
||||
..Node::center()
|
||||
},
|
||||
|color| (
|
||||
|color| {
|
||||
(
|
||||
Node {
|
||||
width: percent(100),
|
||||
align_items: AlignItems::Center,
|
||||
..Node::hstack(px(10))
|
||||
},
|
||||
children![(
|
||||
children![
|
||||
(
|
||||
Node {
|
||||
width: percent(100),
|
||||
height: percent(100),
|
||||
@@ -114,14 +77,15 @@ pub fn spawn_load_popup(commands: &mut Commands) {
|
||||
..default()
|
||||
},
|
||||
|color| text("X", 24.0, color)
|
||||
)]
|
||||
)
|
||||
),
|
||||
);
|
||||
],
|
||||
)
|
||||
},
|
||||
));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn load_popup_handler(
|
||||
@@ -135,7 +99,6 @@ pub fn load_popup_handler(
|
||||
match *interaction {
|
||||
Interaction::Pressed => {
|
||||
match button_type {
|
||||
ButtonType::PopupClose => {}
|
||||
ButtonType::SavegameLoad { savegame_path } => {
|
||||
commands.insert_resource(savegame_path.clone());
|
||||
next_state.set(AppState::GameScreen);
|
||||
|
||||
@@ -8,7 +8,6 @@ pub enum RootMarker {
|
||||
#[derive(Component)]
|
||||
pub enum ButtonType {
|
||||
ShopOpen,
|
||||
ShopClose,
|
||||
ShopBuyItem(ShopOffer),
|
||||
}
|
||||
|
||||
@@ -71,4 +70,3 @@ impl ShopOffer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use crate::features::ui::messages::ClosePopupMessage;
|
||||
use crate::prelude::*;
|
||||
use components::*;
|
||||
use ui::open_shop;
|
||||
@@ -10,24 +9,7 @@ pub struct ShopPlugin;
|
||||
|
||||
impl Plugin for ShopPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(
|
||||
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(),
|
||||
}
|
||||
}
|
||||
app.add_systems(Update, buttons.run_if(in_state(AppState::GameScreen)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,13 +28,6 @@ fn buttons(
|
||||
ButtonType::ShopOpen => {
|
||||
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) => {
|
||||
if offer.buy(&mut inventory, &mut commands, &mut items) {
|
||||
// Item bought, exit the menu
|
||||
|
||||
@@ -9,56 +9,20 @@ pub fn open_shop(
|
||||
// TODO: calculate tile_count
|
||||
let offers = ShopOffer::list_all(game_config, 0);
|
||||
|
||||
commands
|
||||
.spawn((
|
||||
spawn_popup(
|
||||
commands,
|
||||
RootMarker::Shop,
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
width: percent(100),
|
||||
height: percent(100),
|
||||
..Node::center()
|
||||
},
|
||||
ZIndex(1),
|
||||
BackgroundColor(Color::srgba(0.0, 0.0, 0.0, 0.8)),
|
||||
GlobalTransform::default(),
|
||||
))
|
||||
.with_children(|parent| {
|
||||
parent
|
||||
.spawn((
|
||||
"Einkaufsladen",
|
||||
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| {
|
||||
parent.spawn(Node::vstack(px(10))).with_children(|parent| {
|
||||
for offer in offers {
|
||||
parent.spawn(shop_offer(&offer, game_config, asset_server));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::prelude::{button::update_buttons, *};
|
||||
use crate::prelude::{button::update_buttons, popups::handle_popup_close, *};
|
||||
use bevy::{input::mouse::*, picking::hover::HoverMap};
|
||||
|
||||
pub mod components;
|
||||
@@ -15,6 +15,8 @@ impl Plugin for UiPlugin {
|
||||
app.add_observer(on_scroll_handler);
|
||||
|
||||
app.add_systems(Update, update_buttons);
|
||||
|
||||
app.add_systems(Update, handle_popup_close);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ pub trait Flexbox {
|
||||
fn hstack(spacing: Val) -> Self;
|
||||
fn vstack(spacing: Val) -> Self;
|
||||
fn center() -> Self;
|
||||
fn from_padding(padding: UiRect) -> Self;
|
||||
}
|
||||
|
||||
impl Flexbox for Node {
|
||||
@@ -30,4 +31,11 @@ impl Flexbox for Node {
|
||||
..default()
|
||||
}
|
||||
}
|
||||
|
||||
fn from_padding(padding: UiRect) -> Self {
|
||||
Node {
|
||||
padding,
|
||||
..default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
pub mod button;
|
||||
pub mod flexbox;
|
||||
pub mod popups;
|
||||
pub mod texts;
|
||||
|
||||
pub use button::{button, pill_button};
|
||||
pub use flexbox::Flexbox;
|
||||
pub use popups::{spawn_context_menu, spawn_popup};
|
||||
pub use texts::{text, text_with_component};
|
||||
|
||||
114
src/features/ui/ui/popups.rs
Normal file
114
src/features/ui/ui/popups.rs
Normal file
@@ -0,0 +1,114 @@
|
||||
use bevy::ecs::relationship::RelatedSpawnerCommands;
|
||||
|
||||
use super::super::messages::ClosePopupMessage;
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct PopupRoot;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct PopupCloseButton;
|
||||
|
||||
pub fn spawn_popup(
|
||||
commands: &mut Commands,
|
||||
root: impl Component,
|
||||
title: impl Into<String>,
|
||||
mut node: Node,
|
||||
child: impl FnOnce(&mut RelatedSpawnerCommands<ChildOf>),
|
||||
) {
|
||||
node.flex_direction = FlexDirection::Column;
|
||||
node.row_gap = px(10);
|
||||
node.padding = UiRect::all(px(20));
|
||||
|
||||
commands
|
||||
.spawn((
|
||||
PopupRoot,
|
||||
root,
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
width: percent(100),
|
||||
height: percent(100),
|
||||
..Node::center()
|
||||
},
|
||||
ZIndex(100),
|
||||
BackgroundColor(Color::srgba(0., 0., 0., 0.8)),
|
||||
GlobalTransform::default(),
|
||||
))
|
||||
.with_children(|parent| {
|
||||
parent
|
||||
.spawn((
|
||||
node,
|
||||
BackgroundColor(Color::srgb(0.2, 0.2, 0.2)),
|
||||
BorderRadius::all(px(10)),
|
||||
))
|
||||
.with_children(|parent| {
|
||||
parent.spawn((
|
||||
Node {
|
||||
justify_content: JustifyContent::SpaceBetween,
|
||||
align_items: AlignItems::Center,
|
||||
..Node::hstack(px(20))
|
||||
},
|
||||
children![
|
||||
text(title, 40.0, Color::WHITE),
|
||||
button(
|
||||
PopupCloseButton,
|
||||
ButtonVariant::Destructive,
|
||||
Node {
|
||||
width: px(40),
|
||||
height: px(40),
|
||||
..Node::center()
|
||||
},
|
||||
|color| text("X", 24.0, color)
|
||||
)
|
||||
],
|
||||
));
|
||||
|
||||
child(parent);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub fn spawn_context_menu(
|
||||
commands: &mut Commands,
|
||||
root: impl Component,
|
||||
position: Vec2,
|
||||
child: impl FnOnce(&mut RelatedSpawnerCommands<ChildOf>),
|
||||
) {
|
||||
commands
|
||||
.spawn((
|
||||
PopupRoot,
|
||||
root,
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
left: px(position.x),
|
||||
top: px(position.y),
|
||||
padding: UiRect::all(px(5)),
|
||||
..Node::vstack(px(5))
|
||||
},
|
||||
ZIndex(100),
|
||||
BackgroundColor(Color::srgb(0.1, 0.1, 0.1)),
|
||||
BorderRadius::all(px(5)),
|
||||
GlobalTransform::default(),
|
||||
))
|
||||
.with_children(child);
|
||||
}
|
||||
|
||||
pub fn handle_popup_close(
|
||||
mut commands: Commands,
|
||||
root_query: Query<Entity, With<PopupRoot>>,
|
||||
mut messages: MessageReader<ClosePopupMessage>,
|
||||
buttons: Query<&Interaction, (Changed<Interaction>, With<PopupCloseButton>)>,
|
||||
) {
|
||||
for _ in messages.read() {
|
||||
root_query
|
||||
.iter()
|
||||
.for_each(|root| commands.entity(root).despawn());
|
||||
}
|
||||
|
||||
buttons.iter().for_each(|i| match i {
|
||||
Interaction::Pressed => root_query
|
||||
.iter()
|
||||
.for_each(|root| commands.entity(root).despawn()),
|
||||
_ => (),
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user