loby ui impl
This commit is contained in:
+268
-78
@@ -4,10 +4,11 @@ use bevy::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
text::{EditableText, EditableTextFilter, TextCursorStyle},
|
text::{EditableText, EditableTextFilter, TextCursorStyle},
|
||||||
ui::Checked,
|
ui::Checked,
|
||||||
ui_widgets::{RadioButton, RadioGroup, ValueChange},
|
ui_widgets::{Activate, Button, RadioButton, RadioGroup, ValueChange},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
|
use crate::main_menu::InteractionColors;
|
||||||
|
|
||||||
const PANEL_BACKGROUND: Color = Color::srgb(0.4, 0.4, 0.4);
|
const PANEL_BACKGROUND: Color = Color::srgb(0.4, 0.4, 0.4);
|
||||||
const TEXT_COLOR: Color = Color::srgb(0.9, 0.9, 0.9);
|
const TEXT_COLOR: Color = Color::srgb(0.9, 0.9, 0.9);
|
||||||
@@ -23,16 +24,23 @@ const OPTION_SELECTED_PRESSED_BACKGROUND: Color = Color::srgb(0.07, 0.36, 0.44);
|
|||||||
|
|
||||||
pub struct LobbyPlugin;
|
pub struct LobbyPlugin;
|
||||||
|
|
||||||
#[derive(Resource, Debug, Clone, Copy, PartialEq, Eq, Reflect)]
|
#[derive(Resource, Debug, Clone, PartialEq, Eq, Reflect)]
|
||||||
#[reflect(Resource)]
|
#[reflect(Resource)]
|
||||||
pub struct LobbyUiState {
|
pub struct LobbyUiState {
|
||||||
pub privacy: LobbyPrivacy,
|
pub privacy: LobbyPrivacy,
|
||||||
|
pub port: u16,
|
||||||
|
pub password: String,
|
||||||
|
// TODO: replace with actual connection logic
|
||||||
|
pub session_active: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for LobbyUiState {
|
impl Default for LobbyUiState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
privacy: LobbyPrivacy::Public,
|
privacy: LobbyPrivacy::Public,
|
||||||
|
port: 5599,
|
||||||
|
password: String::new(),
|
||||||
|
session_active: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,14 +48,26 @@ impl Default for LobbyUiState {
|
|||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Reflect)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Reflect)]
|
||||||
pub enum LobbyPrivacy {
|
pub enum LobbyPrivacy {
|
||||||
#[default]
|
#[default]
|
||||||
Public,
|
|
||||||
FriendsOnly,
|
|
||||||
InviteOnly,
|
InviteOnly,
|
||||||
|
FriendsOnly,
|
||||||
|
Public,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Debug, Clone, Copy, Default, PartialEq, Eq)]
|
#[derive(Component, Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||||
struct LobbyPrivacyOption(LobbyPrivacy);
|
struct LobbyPrivacyOption(LobbyPrivacy);
|
||||||
|
|
||||||
|
#[derive(Component, Clone, Default)]
|
||||||
|
struct PortField;
|
||||||
|
|
||||||
|
#[derive(Component, Clone, Default)]
|
||||||
|
struct PasswordField;
|
||||||
|
|
||||||
|
#[derive(Component, Clone, Default)]
|
||||||
|
struct ResetPortButton;
|
||||||
|
|
||||||
|
#[derive(Component, Clone, Default)]
|
||||||
|
struct SessionStatusLabel;
|
||||||
|
|
||||||
impl Plugin for LobbyPlugin {
|
impl Plugin for LobbyPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.init_resource::<LobbyUiState>();
|
app.init_resource::<LobbyUiState>();
|
||||||
@@ -58,7 +78,13 @@ impl Plugin for LobbyPlugin {
|
|||||||
)
|
)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Update,
|
Update,
|
||||||
(update_privacy_controls, style_privacy_options).chain(),
|
(
|
||||||
|
update_privacy_controls,
|
||||||
|
session_visuals,
|
||||||
|
sync_lobby_fields,
|
||||||
|
style_privacy_options,
|
||||||
|
)
|
||||||
|
.chain(),
|
||||||
);
|
);
|
||||||
app.add_observer(on_privacy_changed);
|
app.add_observer(on_privacy_changed);
|
||||||
}
|
}
|
||||||
@@ -88,13 +114,12 @@ fn setup_lobby(mut commands: Commands) {
|
|||||||
|
|
||||||
fn build_lobby_ui(mut commands: Commands, root_ui: Single<Entity, With<LobbyUiRoot>>) {
|
fn build_lobby_ui(mut commands: Commands, root_ui: Single<Entity, With<LobbyUiRoot>>) {
|
||||||
let root_ui = *root_ui;
|
let root_ui = *root_ui;
|
||||||
|
|
||||||
commands.spawn_scene(bsn! {
|
commands.spawn_scene(bsn! {
|
||||||
ChildOf(root_ui)
|
ChildOf(root_ui)
|
||||||
Node {
|
Node {
|
||||||
|
width: percent(50),
|
||||||
flex_direction: FlexDirection::Column,
|
flex_direction: FlexDirection::Column,
|
||||||
align_self: AlignSelf::FlexEnd,
|
align_self: AlignSelf::Stretch,
|
||||||
flex_grow: 1.,
|
|
||||||
border_radius: BorderRadius::all(px(24)),
|
border_radius: BorderRadius::all(px(24)),
|
||||||
padding: Val::all(px(24)),
|
padding: Val::all(px(24)),
|
||||||
}
|
}
|
||||||
@@ -106,122 +131,283 @@ fn build_lobby_ui(mut commands: Commands, root_ui: Single<Entity, With<LobbyUiRo
|
|||||||
align_self: AlignSelf::Center,
|
align_self: AlignSelf::Center,
|
||||||
}
|
}
|
||||||
TextFont {
|
TextFont {
|
||||||
font_size: px(100.0),
|
font_size: px(76.),
|
||||||
}
|
}
|
||||||
TextColor(TEXT_COLOR)
|
TextColor(TEXT_COLOR)
|
||||||
TextShadow
|
TextShadow
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
Text("Session Visibility")
|
||||||
|
Node {
|
||||||
|
margin: UiRect::top(px(24)),
|
||||||
|
}
|
||||||
|
TextFont {
|
||||||
|
font_size: px(38.),
|
||||||
|
}
|
||||||
|
TextColor(TEXT_COLOR)
|
||||||
|
),
|
||||||
(
|
(
|
||||||
RadioGroup
|
RadioGroup
|
||||||
Node {
|
Node {
|
||||||
display: Display::Flex,
|
display: Display::Flex,
|
||||||
flex_direction: FlexDirection::Row,
|
flex_direction: FlexDirection::Row,
|
||||||
width: px(900),
|
width: percent(100),
|
||||||
height: px(110),
|
height: px(90),
|
||||||
}
|
}
|
||||||
TabIndex(0)
|
TabIndex(0)
|
||||||
Children [
|
Children [
|
||||||
privacy_option("Public", LobbyPrivacy::Public, BorderRadius::px(16., 0., 0., 16.)),
|
privacy_option(
|
||||||
|
"Public",
|
||||||
|
LobbyPrivacy::Public,
|
||||||
|
BorderRadius::px(16., 0., 0., 16.),
|
||||||
|
),
|
||||||
privacy_option("Friends Only", LobbyPrivacy::FriendsOnly, BorderRadius::ZERO),
|
privacy_option("Friends Only", LobbyPrivacy::FriendsOnly, BorderRadius::ZERO),
|
||||||
privacy_option("Invite Only", LobbyPrivacy::InviteOnly, BorderRadius::px(0., 16., 16., 0.)),
|
privacy_option(
|
||||||
|
"Invite Only",
|
||||||
|
LobbyPrivacy::InviteOnly,
|
||||||
|
BorderRadius::px(0., 16., 16., 0.),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
Text("Save Name")
|
field_label("Port")
|
||||||
Node {
|
|
||||||
margin: UiRect::axes(px(12), px(0)),
|
|
||||||
}
|
|
||||||
TextFont {
|
|
||||||
// font: FontSourceTemplate::Handle("fonts/FiraSans-Bold.ttf"),
|
|
||||||
font_size: px(66.0),
|
|
||||||
}
|
|
||||||
TextColor(TEXT_COLOR)
|
|
||||||
TextShadow
|
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
Node {
|
Node {
|
||||||
border: Val::all(px(6)),
|
flex_direction: FlexDirection::Row,
|
||||||
padding: Val::all(px(12)),
|
column_gap: px(12),
|
||||||
border_radius: BorderRadius::all(px(12)),
|
align_items: AlignItems::Center,
|
||||||
}
|
}
|
||||||
BorderColor::from(SLATE_900)
|
Children [
|
||||||
EditableText {
|
port_field(),
|
||||||
cursor_width: 0.05,
|
(
|
||||||
max_characters: Option::Some(14),
|
Button
|
||||||
|
ResetPortButton
|
||||||
|
Node {
|
||||||
|
width: px(600),
|
||||||
|
height: px(130),
|
||||||
|
border: px(6),
|
||||||
|
justify_content: JustifyContent::Center,
|
||||||
|
align_items: AlignItems::Center,
|
||||||
|
margin: UiRect::all(px(4)),
|
||||||
|
}
|
||||||
|
BorderColor::from(Color::BLACK)
|
||||||
|
InteractionColors::DEFAULT
|
||||||
|
Interaction
|
||||||
|
Children [(
|
||||||
|
Text("Reset")
|
||||||
|
TextFont {
|
||||||
|
font_size: px(66.),
|
||||||
|
}
|
||||||
|
TextColor(TEXT_COLOR)
|
||||||
|
TextShadow
|
||||||
|
)]
|
||||||
|
on(|_event: On<Activate>, mut fields: Query<&mut EditableText, With<PortField>>| {
|
||||||
|
if let Ok(mut field) = fields.single_mut() {
|
||||||
|
field.editor_mut().set_text("5599");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
(field_label("Password")),
|
||||||
|
(password_field()),
|
||||||
|
(
|
||||||
|
Node {
|
||||||
|
flex_grow: 1.,
|
||||||
}
|
}
|
||||||
EditableTextFilter::new(|c| c == ' ' || c.is_ascii_alphanumeric())
|
),
|
||||||
TextLayout::no_wrap()
|
(
|
||||||
TextFont {
|
Button
|
||||||
font_size: FontSize::Px(66.),
|
Node {
|
||||||
|
width: px(600),
|
||||||
|
height: px(130),
|
||||||
|
border: px(6),
|
||||||
|
justify_content: JustifyContent::Center,
|
||||||
|
align_items: AlignItems::Center,
|
||||||
|
margin: UiRect::all(px(4)),
|
||||||
}
|
}
|
||||||
TextCursorStyle {
|
BorderColor::from(Color::BLACK)
|
||||||
color: Color::BLACK
|
InteractionColors::GOOD
|
||||||
}
|
Interaction
|
||||||
TabIndex(0)
|
on(|_event: On<Activate>, mut lobby: ResMut<LobbyUiState>| {
|
||||||
BackgroundColor(TEXT_INPUT_BACKGROUND)
|
info!("Session clicked!");
|
||||||
|
lobby.session_active = !lobby.session_active;
|
||||||
|
})
|
||||||
|
Children [(
|
||||||
|
Text("Start Session")
|
||||||
|
SessionStatusLabel
|
||||||
|
TextFont {
|
||||||
|
font_size: px(66.),
|
||||||
|
}
|
||||||
|
TextColor(TEXT_COLOR)
|
||||||
|
TextShadow
|
||||||
|
)]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
commands.spawn_scene(bsn! {
|
commands.spawn_scene(bsn! {
|
||||||
ChildOf(root_ui)
|
ChildOf(root_ui)
|
||||||
Node {
|
Node {
|
||||||
|
width: percent(50),
|
||||||
flex_direction: FlexDirection::Column,
|
flex_direction: FlexDirection::Column,
|
||||||
align_self: AlignSelf::FlexEnd,
|
align_self: AlignSelf::Stretch,
|
||||||
flex_grow: 1.,
|
|
||||||
border_radius: BorderRadius::all(px(24)),
|
border_radius: BorderRadius::all(px(24)),
|
||||||
padding: Val::all(px(24)),
|
padding: Val::all(px(24)),
|
||||||
}
|
}
|
||||||
BackgroundColor(PANEL_BACKGROUND)
|
BackgroundColor(PANEL_BACKGROUND)
|
||||||
Children [
|
Children [
|
||||||
(
|
(
|
||||||
Text("Lobby")
|
Text("Connected Players")
|
||||||
Node {
|
Node {
|
||||||
align_self: AlignSelf::Center,
|
align_self: AlignSelf::Center,
|
||||||
}
|
}
|
||||||
TextFont {
|
TextFont {
|
||||||
font_size: px(100.0),
|
font_size: px(76.),
|
||||||
}
|
}
|
||||||
TextColor(TEXT_COLOR)
|
TextColor(TEXT_COLOR)
|
||||||
TextShadow
|
TextShadow
|
||||||
),
|
),
|
||||||
(
|
player_row("Joe (you)"),
|
||||||
Text("Save Name")
|
player_row("Alex"),
|
||||||
Node {
|
player_row("Sam"),
|
||||||
margin: UiRect::axes(px(12), px(0)),
|
player_row("Taylor"),
|
||||||
}
|
|
||||||
TextFont {
|
|
||||||
// font: FontSourceTemplate::Handle("fonts/FiraSans-Bold.ttf"),
|
|
||||||
font_size: px(66.0),
|
|
||||||
}
|
|
||||||
TextColor(TEXT_COLOR)
|
|
||||||
TextShadow
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Node {
|
|
||||||
border: Val::all(px(6)),
|
|
||||||
padding: Val::all(px(12)),
|
|
||||||
border_radius: BorderRadius::all(px(12)),
|
|
||||||
}
|
|
||||||
BorderColor::from(SLATE_900)
|
|
||||||
EditableText {
|
|
||||||
cursor_width: 0.05,
|
|
||||||
max_characters: Option::Some(14),
|
|
||||||
}
|
|
||||||
EditableTextFilter::new(|c| c == ' ' || c.is_ascii_alphanumeric())
|
|
||||||
TextLayout::no_wrap()
|
|
||||||
TextFont {
|
|
||||||
font_size: FontSize::Px(66.),
|
|
||||||
}
|
|
||||||
TextCursorStyle {
|
|
||||||
color: Color::BLACK
|
|
||||||
}
|
|
||||||
TabIndex(0)
|
|
||||||
BackgroundColor(TEXT_INPUT_BACKGROUND)
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn field_label(label: &'static str) -> impl Scene {
|
||||||
|
bsn! {
|
||||||
|
Text(label)
|
||||||
|
Node {
|
||||||
|
margin: UiRect::top(px(18)),
|
||||||
|
}
|
||||||
|
TextFont {
|
||||||
|
font_size: px(38.),
|
||||||
|
}
|
||||||
|
TextColor(TEXT_COLOR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn port_field() -> impl Scene {
|
||||||
|
bsn! {
|
||||||
|
PortField
|
||||||
|
Node {
|
||||||
|
width: percent(100),
|
||||||
|
border: Val::all(px(6)),
|
||||||
|
padding: Val::all(px(12)),
|
||||||
|
border_radius: BorderRadius::all(px(12)),
|
||||||
|
}
|
||||||
|
BorderColor::from(SLATE_900)
|
||||||
|
EditableText::new("5599")
|
||||||
|
EditableText {
|
||||||
|
cursor_width: 0.05,
|
||||||
|
max_characters: Option::Some(5),
|
||||||
|
}
|
||||||
|
EditableTextFilter::new(|c| c.is_ascii_digit())
|
||||||
|
TextLayout::no_wrap()
|
||||||
|
TextFont {
|
||||||
|
font_size: FontSize::Px(66.),
|
||||||
|
}
|
||||||
|
TextCursorStyle {
|
||||||
|
color: Color::BLACK,
|
||||||
|
}
|
||||||
|
TabIndex(0)
|
||||||
|
BackgroundColor(TEXT_INPUT_BACKGROUND)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn password_field() -> impl Scene {
|
||||||
|
bsn! {
|
||||||
|
PasswordField
|
||||||
|
Node {
|
||||||
|
width: percent(100),
|
||||||
|
border: Val::all(px(6)),
|
||||||
|
padding: Val::all(px(12)),
|
||||||
|
border_radius: BorderRadius::all(px(12)),
|
||||||
|
}
|
||||||
|
BorderColor::from(SLATE_900)
|
||||||
|
EditableText {
|
||||||
|
cursor_width: 0.05,
|
||||||
|
max_characters: Option::Some(32),
|
||||||
|
}
|
||||||
|
EditableTextFilter::new(|c| c == ' ' || c.is_ascii_alphanumeric() || c == '_' || c == '-')
|
||||||
|
TextLayout::no_wrap()
|
||||||
|
TextFont {
|
||||||
|
font_size: FontSize::Px(66.),
|
||||||
|
}
|
||||||
|
TextCursorStyle {
|
||||||
|
color: Color::BLACK,
|
||||||
|
}
|
||||||
|
TabIndex(1)
|
||||||
|
BackgroundColor(TEXT_INPUT_BACKGROUND)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn player_row(name: &'static str) -> impl Scene {
|
||||||
|
bsn! {
|
||||||
|
Node {
|
||||||
|
width: percent(100),
|
||||||
|
height: px(130),
|
||||||
|
margin: UiRect::top(px(14)),
|
||||||
|
padding: Val::all(px(16)),
|
||||||
|
align_items: AlignItems::Center,
|
||||||
|
column_gap: px(24),
|
||||||
|
border_radius: BorderRadius::all(px(12)),
|
||||||
|
}
|
||||||
|
BackgroundColor(OPTION_BACKGROUND)
|
||||||
|
Children [
|
||||||
|
(
|
||||||
|
Node {
|
||||||
|
width: px(96),
|
||||||
|
height: px(96),
|
||||||
|
border_radius: BorderRadius::MAX,
|
||||||
|
}
|
||||||
|
BackgroundColor(OPTION_SELECTED_BACKGROUND)
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Text(name)
|
||||||
|
TextFont {
|
||||||
|
font_size: px(50.),
|
||||||
|
}
|
||||||
|
TextColor(TEXT_COLOR)
|
||||||
|
TextShadow
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sync_lobby_fields(
|
||||||
|
port_field: Single<&EditableText, With<PortField>>,
|
||||||
|
password_field: Single<&EditableText, With<PasswordField>>,
|
||||||
|
mut lobby: ResMut<LobbyUiState>,
|
||||||
|
) {
|
||||||
|
if let Ok(port) = port_field.value().to_string().parse::<u16>() {
|
||||||
|
lobby.port = port;
|
||||||
|
}
|
||||||
|
lobby.password = password_field.value().to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn session_visuals(
|
||||||
|
lobby_ui: Res<LobbyUiState>,
|
||||||
|
q_session_control_text: Option<Single<&mut Text, With<SessionStatusLabel>>>,
|
||||||
|
) {
|
||||||
|
if !lobby_ui.is_changed() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(mut text) = q_session_control_text else {
|
||||||
|
warn!("Session Control Text not found!");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_text = match lobby_ui.session_active {
|
||||||
|
true => "Stop Session",
|
||||||
|
false => "Start Session",
|
||||||
|
};
|
||||||
|
**text = new_text.into();
|
||||||
|
}
|
||||||
|
|
||||||
fn privacy_option(
|
fn privacy_option(
|
||||||
label: &'static str,
|
label: &'static str,
|
||||||
privacy: LobbyPrivacy,
|
privacy: LobbyPrivacy,
|
||||||
@@ -236,7 +422,7 @@ fn privacy_option(
|
|||||||
flex_grow: 1.,
|
flex_grow: 1.,
|
||||||
justify_content: JustifyContent::Center,
|
justify_content: JustifyContent::Center,
|
||||||
align_items: AlignItems::Center,
|
align_items: AlignItems::Center,
|
||||||
border: UiRect::all(px(3)),
|
border: UiRect::all(px(4)),
|
||||||
border_radius,
|
border_radius,
|
||||||
}
|
}
|
||||||
BorderColor::all(OPTION_BORDER_COLOR)
|
BorderColor::all(OPTION_BORDER_COLOR)
|
||||||
@@ -257,6 +443,10 @@ fn update_privacy_controls(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
options: Query<(Entity, &LobbyPrivacyOption, &Interaction, Has<Checked>), With<RadioButton>>,
|
options: Query<(Entity, &LobbyPrivacyOption, &Interaction, Has<Checked>), With<RadioButton>>,
|
||||||
) {
|
) {
|
||||||
|
if !lobby_ui.is_changed() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (entity, option, interaction, checked) in &options {
|
for (entity, option, interaction, checked) in &options {
|
||||||
let should_be_checked = option.0 == lobby_ui.privacy;
|
let should_be_checked = option.0 == lobby_ui.privacy;
|
||||||
if should_be_checked && !checked {
|
if should_be_checked && !checked {
|
||||||
|
|||||||
+10
-7
@@ -32,7 +32,7 @@ struct Selected;
|
|||||||
|
|
||||||
/// Defines colors that a UI object should turn when interactions occur.
|
/// Defines colors that a UI object should turn when interactions occur.
|
||||||
#[derive(Component, Clone, PartialEq, Default, Debug)]
|
#[derive(Component, Clone, PartialEq, Default, Debug)]
|
||||||
struct InteractionColors {
|
pub(crate) struct InteractionColors {
|
||||||
default: Color,
|
default: Color,
|
||||||
hovered: Color,
|
hovered: Color,
|
||||||
pressed: Color,
|
pressed: Color,
|
||||||
@@ -137,7 +137,7 @@ impl MenuLayer {
|
|||||||
menu_layer(layer)
|
menu_layer(layer)
|
||||||
ChildOf(menu_root)
|
ChildOf(menu_root)
|
||||||
Children [
|
Children [
|
||||||
{entries}
|
{entries},
|
||||||
(
|
(
|
||||||
menu_button("New Game", Some(MenuLayer::NewGame))
|
menu_button("New Game", Some(MenuLayer::NewGame))
|
||||||
InteractionColors::GOOD
|
InteractionColors::GOOD
|
||||||
@@ -163,7 +163,7 @@ impl MenuLayer {
|
|||||||
full_menu_layer(layer)
|
full_menu_layer(layer)
|
||||||
ChildOf(menu_root)
|
ChildOf(menu_root)
|
||||||
Children [
|
Children [
|
||||||
menu_button("Game 1", None),
|
menu_button("Game 1", None)
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
MenuLayer::DirectConnect => Box::new(bsn! {
|
MenuLayer::DirectConnect => Box::new(bsn! {
|
||||||
@@ -320,7 +320,7 @@ fn new_game_menu() -> impl SceneList {
|
|||||||
font_size: FontSize::Px(66.),
|
font_size: FontSize::Px(66.),
|
||||||
}
|
}
|
||||||
TextCursorStyle {
|
TextCursorStyle {
|
||||||
color: Color::BLACK
|
color: Color::BLACK,
|
||||||
}
|
}
|
||||||
TabIndex(0)
|
TabIndex(0)
|
||||||
BackgroundColor(Color::srgb(0.48, 0.84, 0.87))
|
BackgroundColor(Color::srgb(0.48, 0.84, 0.87))
|
||||||
@@ -394,7 +394,7 @@ fn game_info_menu(name: impl Into<String>) -> impl SceneList {
|
|||||||
font_size: FontSize::Px(66.),
|
font_size: FontSize::Px(66.),
|
||||||
}
|
}
|
||||||
TextCursorStyle {
|
TextCursorStyle {
|
||||||
color: Color::BLACK
|
color: Color::BLACK,
|
||||||
}
|
}
|
||||||
TabIndex(0)
|
TabIndex(0)
|
||||||
BackgroundColor(Color::srgb(0.48, 0.84, 0.87))
|
BackgroundColor(Color::srgb(0.48, 0.84, 0.87))
|
||||||
@@ -434,7 +434,7 @@ fn game_info_menu(name: impl Into<String>) -> impl SceneList {
|
|||||||
|
|
||||||
// TODO: refresh listing
|
// TODO: refresh listing
|
||||||
})
|
})
|
||||||
)
|
),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@@ -504,11 +504,14 @@ fn menu_button(label: impl Into<String>, next_layer: Option<MenuLayer>) -> Box<d
|
|||||||
fn button_activate_observer(
|
fn button_activate_observer(
|
||||||
event: On<Activate>,
|
event: On<Activate>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut menu: ResMut<CurrentMenu>,
|
menu: Option<ResMut<CurrentMenu>>,
|
||||||
q_next_layer: Query<(&NextLayer, &ChildOf)>,
|
q_next_layer: Query<(&NextLayer, &ChildOf)>,
|
||||||
q_layer: Query<(&Layer, &Children)>,
|
q_layer: Query<(&Layer, &Children)>,
|
||||||
q_selected: Query<(Entity, Has<Selected>)>,
|
q_selected: Query<(Entity, Has<Selected>)>,
|
||||||
) {
|
) {
|
||||||
|
let Some(mut menu) = menu else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
if let Ok((next_layer, parent)) = q_next_layer.get(event.entity) {
|
if let Ok((next_layer, parent)) = q_next_layer.get(event.entity) {
|
||||||
let Ok((layer, layer_entries)) = q_layer.get(parent.parent()) else {
|
let Ok((layer, layer_entries)) = q_layer.get(parent.parent()) else {
|
||||||
warn!(
|
warn!(
|
||||||
|
|||||||
Reference in New Issue
Block a user