M sdbclient/src/main.rs => sdbclient/src/main.rs +70 -7
@@ 7,14 7,50 @@
*/
use bevy::{
- app::{App, PluginGroup},
- window::{
- CompositeAlphaMode, CursorGrabMode, MonitorSelection, PresentMode, WindowDescriptor,
- WindowMode, WindowPlugin, WindowPosition, WindowResizeConstraints,
- },
- DefaultPlugins,
+ prelude::*,
+ window::{CompositeAlphaMode, CursorGrabMode, PresentMode, WindowResizeConstraints},
};
+use bevy_egui::EguiPlugin;
+use bevy_inspector_egui::WorldInspectorPlugin;
+
+mod menu;
+mod splash;
+mod util;
+
+/// Used to control the state of the game
+#[derive(Debug, Clone, Eq, PartialEq, Hash)]
+pub enum GameState {
+ Splash,
+ MainMenu,
+ Game,
+}
+
+// Various settings that can be changed from the... Settings.
+/// Volume
+#[derive(Resource, Debug, Component, PartialEq, Eq, Clone, Copy)]
+pub struct CfgVolume(u32);
+/// Fullscreen
+#[derive(Resource, Debug, Component, PartialEq, Eq, Clone, Copy)]
+pub struct CfgFullscreen(bool);
+/// Resolution
+#[derive(Resource, Debug, Component, PartialEq, Clone, Copy)]
+pub struct CfgResolution(f32, f32);
+
+// Settings that the user has no access to, or can only access through developer settings
+/// API Server
+#[derive(Resource, Debug, Component, PartialEq, Eq, Clone)]
+pub struct CfgAPIServer(String);
+/// User logged in status
+#[derive(Resource, Debug, Component, PartialEq, Eq, Clone, Copy)]
+pub struct CfgLoggedIn(bool);
+/// Login username inputs
+#[derive(Resource, Debug, Component, PartialEq, Eq, Clone)]
+pub struct CfgUserLoginInputs(String, String);
+/// User Token
+#[derive(Resource, Debug, Component, PartialEq, Eq, Clone)]
+pub struct CfgUserToken(String);
+
fn main() {
let mut app = App::new();
@@ 33,7 69,7 @@ fn main() {
scale_factor_override: Some(1.),
title: "Deck Builder".to_string(),
present_mode: PresentMode::Fifo,
- resizable: true,
+ resizable: false,
decorations: true,
cursor_visible: true,
cursor_grab_mode: CursorGrabMode::None,
@@ 46,5 82,32 @@ fn main() {
..Default::default()
}));
+ app.add_plugin(EguiPlugin);
+ app.add_plugin(WorldInspectorPlugin::new());
+
+ app.insert_resource(CfgVolume(7))
+ .insert_resource(CfgFullscreen(false))
+ .insert_resource(CfgResolution(1280., 720.))
+ .insert_resource(CfgAPIServer("http://localhost:8080".to_string()))
+ .insert_resource(CfgUserLoginInputs("".to_string(), "".to_string()))
+ .insert_resource(CfgLoggedIn(false))
+ .insert_resource(CfgUserToken("".to_string()));
+
+ app.add_startup_system(setup).add_state(GameState::Splash);
+
+ app.add_plugin(splash::SplashPlugin)
+ .add_plugin(menu::MenuPlugin);
+
app.run();
}
+
+fn setup(mut commands: Commands) {
+ commands.spawn(Camera3dBundle::default());
+}
+
+/// Utility function do despawn an entity and all its children
+pub fn despawn_screen<T: Component>(to_despawn: Query<Entity, With<T>>, mut commands: Commands) {
+ for entity in &to_despawn {
+ commands.entity(entity).despawn_recursive();
+ }
+}
A => +43 -0
@@ 0,0 1,43 @@
/*
* This file is part of sdbclient
* Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
*/
use bevy::prelude::*;
use bevy_egui::{egui, EguiContext};
use crate::{util::eguipwd, CfgUserLoginInputs};
use super::MenuState;
pub fn account_login_ui(
mut egui_context: ResMut<EguiContext>,
mut menu_state: ResMut<State<MenuState>>,
mut inputs: ResMut<CfgUserLoginInputs>,
) {
egui::Window::new("Login")
.collapsible(false)
.show(egui_context.ctx_mut(), |ui| {
ui.horizontal(|ui| {
ui.label("Username: ");
ui.text_edit_singleline(&mut inputs.0);
});
ui.horizontal(|ui| {
ui.label("Password: ");
ui.add(eguipwd::password(&mut inputs.1));
});
ui.with_layout(egui::Layout::right_to_left(egui::Align::Min), |ui| {
if ui.button("Cancel").clicked() {
menu_state.set(MenuState::Main).unwrap();
}
if ui.button("Login").clicked() {
info!("todo");
}
})
});
}
A => +99 -0
@@ 0,0 1,99 @@
/*
* This file is part of sdbclient
* Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
*/
use bevy::{
prelude::*,
ui::{JustifyContent, Size, Style, Val},
};
use super::{MenuButtonAction, NORMAL_BUTTON, TEXT_COLOR};
/// Tag component for tagging entities on settings menu screen
#[derive(Component)]
pub struct OnAccountLoggedInScreen;
pub fn account_loggedin_setup(mut commands: Commands, asset_server: Res<AssetServer>) {
let font = asset_server.load("fonts/FiraMono-Regular.ttf");
let button_style = Style {
size: Size::new(Val::Px(200.0), Val::Px(65.0)),
margin: UiRect::all(Val::Px(20.0)),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
};
let button_text_style = TextStyle {
font: font.clone(),
font_size: 40.0,
color: TEXT_COLOR,
};
commands
.spawn((
NodeBundle {
style: Style {
size: Size::new(Val::Percent(100.0), Val::Percent(100.0)),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
flex_direction: FlexDirection::Column,
..default()
},
..default()
},
OnAccountLoggedInScreen,
))
.with_children(|parent| {
parent.spawn(
TextBundle::from_section(
"Account",
TextStyle {
font: font.clone(),
font_size: 60.0,
color: TEXT_COLOR,
},
)
.with_style(Style {
margin: UiRect::all(Val::Px(50.)),
..Default::default()
}),
);
parent
.spawn(NodeBundle {
style: Style {
flex_direction: FlexDirection::Column,
align_items: AlignItems::Center,
..default()
},
background_color: Color::GRAY.into(),
..default()
})
.with_children(|parent| {
for (action, text) in [
(MenuButtonAction::AccountLogin, "Logout"),
(MenuButtonAction::BackToMainMenu, "Back"),
] {
parent
.spawn((
ButtonBundle {
style: button_style.clone(),
background_color: NORMAL_BUTTON.into(),
..default()
},
action,
))
.with_children(|parent| {
parent.spawn(TextBundle::from_section(
text,
button_text_style.clone(),
));
});
}
});
});
}
A => +100 -0
@@ 0,0 1,100 @@
/*
* This file is part of sdbclient
* Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
*/
use bevy::{
prelude::*,
ui::{JustifyContent, Size, Style, Val},
};
use super::{MenuButtonAction, NORMAL_BUTTON, TEXT_COLOR};
/// Tag component for tagging entities on settings menu screen
#[derive(Component)]
pub struct OnAccountLoggedOutScreen;
pub fn account_loggedout_setup(mut commands: Commands, asset_server: Res<AssetServer>) {
let font = asset_server.load("fonts/FiraMono-Regular.ttf");
let button_style = Style {
size: Size::new(Val::Px(200.0), Val::Px(65.0)),
margin: UiRect::all(Val::Px(20.0)),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
};
let button_text_style = TextStyle {
font: font.clone(),
font_size: 40.0,
color: TEXT_COLOR,
};
commands
.spawn((
NodeBundle {
style: Style {
size: Size::new(Val::Percent(100.0), Val::Percent(100.0)),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
flex_direction: FlexDirection::Column,
..default()
},
..default()
},
OnAccountLoggedOutScreen,
))
.with_children(|parent| {
parent.spawn(
TextBundle::from_section(
"Account",
TextStyle {
font: font.clone(),
font_size: 60.0,
color: TEXT_COLOR,
},
)
.with_style(Style {
margin: UiRect::all(Val::Px(50.)),
..Default::default()
}),
);
parent
.spawn(NodeBundle {
style: Style {
flex_direction: FlexDirection::Column,
align_items: AlignItems::Center,
..default()
},
background_color: Color::GRAY.into(),
..default()
})
.with_children(|parent| {
for (action, text) in [
(MenuButtonAction::AccountLogin, "Login"),
(MenuButtonAction::AccountRegister, "Register"),
(MenuButtonAction::BackToMainMenu, "Back"),
] {
parent
.spawn((
ButtonBundle {
style: button_style.clone(),
background_color: NORMAL_BUTTON.into(),
..default()
},
action,
))
.with_children(|parent| {
parent.spawn(TextBundle::from_section(
text,
button_text_style.clone(),
));
});
}
});
});
}
A sdbclient/src/menu/mainmenuscreen.rs => sdbclient/src/menu/mainmenuscreen.rs +90 -0
@@ 0,0 1,90 @@
+/*
+ * This file is part of sdbclient
+ * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
+ *
+ * Licensed under GPL-3.0-only.
+ * See LICENSE for licensing information.
+ */
+
+use bevy::{
+ prelude::*,
+ ui::{JustifyContent, Size, Style, Val},
+};
+
+use super::{MenuButtonAction, NORMAL_BUTTON, TEXT_COLOR};
+
+/// Tag component for tagging entities on menu screen
+#[derive(Component)]
+pub struct OnMainMenuScreen;
+
+pub fn main_menu_setup(mut commands: Commands, asset_server: Res<AssetServer>) {
+ let font = asset_server.load("fonts/FiraMono-Regular.ttf");
+
+ let button_style = Style {
+ size: Size::new(Val::Px(250.), Val::Px(65.)),
+ margin: UiRect::all(Val::Px(20.)),
+ justify_content: JustifyContent::Center,
+ align_items: AlignItems::Center,
+ ..Default::default()
+ };
+
+ let button_text_style = TextStyle {
+ font: font.clone(),
+ font_size: 40.0,
+ color: TEXT_COLOR,
+ };
+
+ commands
+ .spawn((
+ NodeBundle {
+ style: Style {
+ size: Size::new(Val::Percent(100.), Val::Percent(100.)),
+ align_items: AlignItems::Center,
+ justify_content: JustifyContent::Center,
+ flex_direction: FlexDirection::Column,
+ ..Default::default()
+ },
+ ..Default::default()
+ },
+ OnMainMenuScreen,
+ ))
+ .with_children(|parent| {
+ // Game title, currently text
+ // TODO: Change to a fancy image logo
+ parent.spawn(
+ TextBundle::from_section(
+ "Deck Builder",
+ TextStyle {
+ font: font.clone(),
+ font_size: 80.0,
+ color: TEXT_COLOR,
+ },
+ )
+ .with_style(Style {
+ margin: UiRect::all(Val::Px(50.)),
+ ..Default::default()
+ }),
+ );
+
+ // Main menu buttons
+ for (action, text) in [
+ (MenuButtonAction::Play, "Play"),
+ (MenuButtonAction::Account, "Account"),
+ (MenuButtonAction::Settings, "Settings"),
+ (MenuButtonAction::Exit, "Exit"),
+ ] {
+ parent
+ .spawn((
+ ButtonBundle {
+ style: button_style.clone(),
+ background_color: NORMAL_BUTTON.into(),
+ ..Default::default()
+ },
+ action,
+ ))
+ .with_children(|parent| {
+ parent.spawn(TextBundle::from_section(text, button_text_style.clone()));
+ });
+ }
+ });
+}
A => +193 -0
@@ 0,0 1,193 @@
/*
* This file is part of sdbclient
* Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
*/
use bevy::{app::AppExit, prelude::*};
use crate::CfgLoggedIn;
use super::{despawn_screen, GameState};
mod mainmenuscreen;
use mainmenuscreen::*;
mod settingsmenuscreen;
use settingsmenuscreen::*;
mod settingsdisplayscreen;
use settingsdisplayscreen::*;
mod settingsaudioscreen;
use settingsaudioscreen::*;
mod settingsmiscscreen;
use settingsmiscscreen::*;
mod accountscreenloggedout;
use accountscreenloggedout::*;
mod accountscreenloggedin;
use accountscreenloggedin::*;
mod accountloginui;
use accountloginui::*;
const TEXT_COLOR: Color = Color::rgb(0.9, 0.9, 0.9);
pub struct MenuPlugin;
impl Plugin for MenuPlugin {
fn build(&self, app: &mut App) {
app.
// Start with no menu. The menu is loaded when the GameState::MainMenu is entered.
add_state(MenuState::None)
.add_system_set(SystemSet::on_enter(GameState::MainMenu).with_system(menu_setup))
// Systems for main menu screen
.add_system_set(SystemSet::on_enter(MenuState::Main).with_system(main_menu_setup))
.add_system_set(SystemSet::on_exit(MenuState::Main).with_system(despawn_screen::<OnMainMenuScreen>))
// Systems for settings menu screen
.add_system_set(SystemSet::on_enter(MenuState::Settings).with_system(settings_menu_setup))
.add_system_set(SystemSet::on_exit(MenuState::Settings).with_system(despawn_screen::<OnSettingsMenuScreen>))
// Systems for settings display screen
.add_system_set(SystemSet::on_enter(MenuState::SettingsDisplay).with_system(settings_display_setup))
.add_system_set(SystemSet::on_exit(MenuState::SettingsDisplay).with_system(despawn_screen::<OnSettingsDisplayScreen>))
// Systems for settings audio screen
.add_system_set(SystemSet::on_enter(MenuState::SettingsAudio).with_system(settings_audio_setup))
.add_system_set(SystemSet::on_exit(MenuState::SettingsAudio).with_system(despawn_screen::<OnSettingsAudioScreen>))
// Systems for settings misc screen
.add_system_set(SystemSet::on_enter(MenuState::SettingsMisc).with_system(settings_misc_setup))
.add_system_set(SystemSet::on_exit(MenuState::SettingsMisc).with_system(despawn_screen::<OnSettingsMiscScreen>))
// Systems for account loggedout screen
.add_system_set(SystemSet::on_enter(MenuState::AccountLoggedOut).with_system(account_loggedout_setup))
.add_system_set(SystemSet::on_exit(MenuState::AccountLoggedOut).with_system(despawn_screen::<OnAccountLoggedOutScreen>))
// Systems for account loggedin screen
.add_system_set(SystemSet::on_enter(MenuState::AccountLoggedIn).with_system(account_loggedin_setup))
.add_system_set(SystemSet::on_exit(MenuState::AccountLoggedIn).with_system(despawn_screen::<OnAccountLoggedInScreen>))
// Account login and register systems
.add_system_set(SystemSet::on_update(MenuState::AccountLogin).with_system(account_login_ui))
// Common systems
.add_system_set(SystemSet::on_update(GameState::MainMenu).with_system(menu_action).with_system(button_system));
}
}
/// Menu State
#[derive(Clone, Eq, PartialEq, Debug, Hash)]
pub enum MenuState {
None,
Main,
Settings,
SettingsDisplay,
SettingsAudio,
SettingsMisc,
AccountLoggedIn,
AccountLoggedOut,
AccountLogin,
AccountRegister,
}
/// Tag component for tagging entities on account screen
#[derive(Component)]
struct OnAccountScreen;
/// Tag component for tagging entities on login screen
#[derive(Component)]
struct OnLoginScreen;
/// Tag component for tagging entities on register screen
#[derive(Component)]
struct OnRegisterScreen;
const NORMAL_BUTTON: Color = Color::rgb(0.15, 0.15, 0.15);
const HOVERED_BUTTON: Color = Color::rgb(0.25, 0.25, 0.25);
const HOVERED_PRESSED_BUTTON: Color = Color::rgb(0.25, 0.65, 0.25);
const PRESSED_BUTTON: Color = Color::rgb(0.35, 0.75, 0.35);
/// Tag component for tagging currently selected settings tab
#[derive(Component)]
struct SelectedSettingsTab;
/// All button actions
#[derive(Component)]
enum MenuButtonAction {
Play,
Settings,
SettingsDisplay,
SettingsAudio,
SettingsMisc,
Account,
AccountLogin,
AccountRegister,
BackToMainMenu,
BackToSettings,
Exit,
}
fn menu_action(
mut interaction_query: Query<
(&Interaction, &MenuButtonAction),
(Changed<Interaction>, With<Button>),
>,
mut app_exit_events: EventWriter<AppExit>,
mut menu_state: ResMut<State<MenuState>>,
mut game_state: ResMut<State<GameState>>,
mut logged_in: Res<CfgLoggedIn>,
) {
for (interaction, menu_button_action) in &interaction_query {
if *interaction == Interaction::Clicked {
match menu_button_action {
MenuButtonAction::Exit => app_exit_events.send(AppExit),
MenuButtonAction::Play => println!("todo"),
MenuButtonAction::Settings => menu_state.set(MenuState::Settings).unwrap(),
MenuButtonAction::SettingsDisplay => {
menu_state.set(MenuState::SettingsDisplay).unwrap()
}
MenuButtonAction::SettingsAudio => {
menu_state.set(MenuState::SettingsAudio).unwrap()
}
MenuButtonAction::SettingsMisc => menu_state.set(MenuState::SettingsMisc).unwrap(),
MenuButtonAction::Account => {
if logged_in.0 {
menu_state.set(MenuState::AccountLoggedIn).unwrap()
} else {
menu_state.set(MenuState::AccountLoggedOut).unwrap()
}
}
MenuButtonAction::AccountLogin => menu_state.set(MenuState::AccountLogin).unwrap(),
MenuButtonAction::AccountRegister => {
menu_state.set(MenuState::AccountRegister).unwrap()
}
MenuButtonAction::BackToSettings => menu_state.set(MenuState::Settings).unwrap(),
MenuButtonAction::BackToMainMenu => menu_state.set(MenuState::Main).unwrap(),
}
}
}
}
/// System for handling button hovering
fn button_system(
mut interaction_query: Query<
(
&Interaction,
&mut BackgroundColor,
Option<&SelectedSettingsTab>,
),
(Changed<Interaction>, With<Button>),
>,
) {
for (interaction, mut color, selected) in &mut interaction_query {
*color = match (*interaction, selected) {
(Interaction::Clicked, _) | (Interaction::None, Some(_)) => PRESSED_BUTTON.into(),
(Interaction::Hovered, Some(_)) => HOVERED_PRESSED_BUTTON.into(),
(Interaction::Hovered, None) => HOVERED_BUTTON.into(),
(Interaction::None, None) => NORMAL_BUTTON.into(),
}
}
}
fn menu_setup(mut menu_state: ResMut<State<MenuState>>) {
let _ = menu_state.set(MenuState::Main);
}
A => +79 -0
@@ 0,0 1,79 @@
/*
* This file is part of sdbclient
* Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
*/
use bevy::{
prelude::*,
ui::{JustifyContent, Size, Style, Val},
};
use super::{MenuButtonAction, NORMAL_BUTTON, TEXT_COLOR};
/// Tag component for tagging entities on settings volume screen
#[derive(Component)]
pub struct OnSettingsAudioScreen;
pub fn settings_audio_setup(mut commands: Commands, asset_server: Res<AssetServer>) {
let button_style = Style {
size: Size::new(Val::Px(200.0), Val::Px(65.0)),
margin: UiRect::all(Val::Px(20.0)),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
};
let button_text_style = TextStyle {
font: asset_server.load("fonts/FiraMono-Regular.ttf"),
font_size: 40.0,
color: TEXT_COLOR,
};
commands
.spawn((
NodeBundle {
style: Style {
size: Size::new(Val::Percent(100.0), Val::Percent(100.0)),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
..default()
},
OnSettingsAudioScreen,
))
.with_children(|parent| {
parent
.spawn(NodeBundle {
style: Style {
flex_direction: FlexDirection::Column,
align_items: AlignItems::Center,
..default()
},
background_color: Color::GRAY.into(),
..default()
})
.with_children(|parent| {
for (action, text) in [(MenuButtonAction::BackToSettings, "Back")] {
parent
.spawn((
ButtonBundle {
style: button_style.clone(),
background_color: NORMAL_BUTTON.into(),
..default()
},
action,
))
.with_children(|parent| {
parent.spawn(TextBundle::from_section(
text,
button_text_style.clone(),
));
});
}
});
});
}
A => +79 -0
@@ 0,0 1,79 @@
/*
* This file is part of sdbclient
* Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
*/
use bevy::{
prelude::*,
ui::{JustifyContent, Size, Style, Val},
};
use super::{MenuButtonAction, NORMAL_BUTTON, TEXT_COLOR};
/// Tag component for tagging entities on settings display screen
#[derive(Component)]
pub struct OnSettingsDisplayScreen;
pub fn settings_display_setup(mut commands: Commands, asset_server: Res<AssetServer>) {
let button_style = Style {
size: Size::new(Val::Px(200.0), Val::Px(65.0)),
margin: UiRect::all(Val::Px(20.0)),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
};
let button_text_style = TextStyle {
font: asset_server.load("fonts/FiraMono-Regular.ttf"),
font_size: 40.0,
color: TEXT_COLOR,
};
commands
.spawn((
NodeBundle {
style: Style {
size: Size::new(Val::Percent(100.0), Val::Percent(100.0)),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
..default()
},
OnSettingsDisplayScreen,
))
.with_children(|parent| {
parent
.spawn(NodeBundle {
style: Style {
flex_direction: FlexDirection::Column,
align_items: AlignItems::Center,
..default()
},
background_color: Color::GRAY.into(),
..default()
})
.with_children(|parent| {
for (action, text) in [(MenuButtonAction::BackToSettings, "Back")] {
parent
.spawn((
ButtonBundle {
style: button_style.clone(),
background_color: NORMAL_BUTTON.into(),
..default()
},
action,
))
.with_children(|parent| {
parent.spawn(TextBundle::from_section(
text,
button_text_style.clone(),
));
});
}
});
});
}
A => +101 -0
@@ 0,0 1,101 @@
/*
* This file is part of sdbclient
* Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
*/
use bevy::{
prelude::*,
ui::{JustifyContent, Size, Style, Val},
};
use super::{MenuButtonAction, NORMAL_BUTTON, TEXT_COLOR};
/// Tag component for tagging entities on settings menu screen
#[derive(Component)]
pub struct OnSettingsMenuScreen;
pub fn settings_menu_setup(mut commands: Commands, asset_server: Res<AssetServer>) {
let font = asset_server.load("fonts/FiraMono-Regular.ttf");
let button_style = Style {
size: Size::new(Val::Px(200.0), Val::Px(65.0)),
margin: UiRect::all(Val::Px(20.0)),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
};
let button_text_style = TextStyle {
font: font.clone(),
font_size: 40.0,
color: TEXT_COLOR,
};
commands
.spawn((
NodeBundle {
style: Style {
size: Size::new(Val::Percent(100.0), Val::Percent(100.0)),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
flex_direction: FlexDirection::Column,
..default()
},
..default()
},
OnSettingsMenuScreen,
))
.with_children(|parent| {
parent.spawn(
TextBundle::from_section(
"Settings",
TextStyle {
font: font.clone(),
font_size: 60.0,
color: TEXT_COLOR,
},
)
.with_style(Style {
margin: UiRect::all(Val::Px(50.)),
..Default::default()
}),
);
parent
.spawn(NodeBundle {
style: Style {
flex_direction: FlexDirection::Column,
align_items: AlignItems::Center,
..default()
},
background_color: Color::GRAY.into(),
..default()
})
.with_children(|parent| {
for (action, text) in [
(MenuButtonAction::SettingsDisplay, "Display"),
(MenuButtonAction::SettingsAudio, "Audio"),
(MenuButtonAction::SettingsMisc, "Misc"),
(MenuButtonAction::BackToMainMenu, "Back"),
] {
parent
.spawn((
ButtonBundle {
style: button_style.clone(),
background_color: NORMAL_BUTTON.into(),
..default()
},
action,
))
.with_children(|parent| {
parent.spawn(TextBundle::from_section(
text,
button_text_style.clone(),
));
});
}
});
});
}
A => +79 -0
@@ 0,0 1,79 @@
/*
* This file is part of sdbclient
* Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
*/
use bevy::{
prelude::*,
ui::{JustifyContent, Size, Style, Val},
};
use super::{MenuButtonAction, NORMAL_BUTTON, TEXT_COLOR};
/// Tag component for tagging entities on settings misc screen
#[derive(Component)]
pub struct OnSettingsMiscScreen;
pub fn settings_misc_setup(mut commands: Commands, asset_server: Res<AssetServer>) {
let button_style = Style {
size: Size::new(Val::Px(200.0), Val::Px(65.0)),
margin: UiRect::all(Val::Px(20.0)),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
};
let button_text_style = TextStyle {
font: asset_server.load("fonts/FiraMono-Regular.ttf"),
font_size: 40.0,
color: TEXT_COLOR,
};
commands
.spawn((
NodeBundle {
style: Style {
size: Size::new(Val::Percent(100.0), Val::Percent(100.0)),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
..default()
},
OnSettingsMiscScreen,
))
.with_children(|parent| {
parent
.spawn(NodeBundle {
style: Style {
flex_direction: FlexDirection::Column,
align_items: AlignItems::Center,
..default()
},
background_color: Color::GRAY.into(),
..default()
})
.with_children(|parent| {
for (action, text) in [(MenuButtonAction::BackToSettings, "Back")] {
parent
.spawn((
ButtonBundle {
style: button_style.clone(),
background_color: NORMAL_BUTTON.into(),
..default()
},
action,
))
.with_children(|parent| {
parent.spawn(TextBundle::from_section(
text,
button_text_style.clone(),
));
});
}
});
});
}
A sdbclient/src/splash/mod.rs => sdbclient/src/splash/mod.rs +80 -0
@@ 0,0 1,80 @@
+/*
+ * This file is part of sdbclient
+ * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
+ *
+ * Licensed under GPL-3.0-only.
+ * See LICENSE for licensing information.
+ */
+
+use bevy::{
+ prelude::*,
+ ui::{JustifyContent, Size, Style, Val},
+};
+
+use super::{despawn_screen, GameState};
+
+/// This plugin will display a splsh logo on startup
+pub struct SplashPlugin;
+
+impl Plugin for SplashPlugin {
+ fn build(&self, app: &mut App) {
+ app
+ // Load the splash when we enter the Splash state
+ .add_system_set(SystemSet::on_enter(GameState::Splash).with_system(splash_setup))
+ // Run a timer for a few seconds
+ .add_system_set(SystemSet::on_update(GameState::Splash).with_system(splash_timer))
+ // Despawn when we exit the Splash state
+ .add_system_set(
+ SystemSet::on_exit(GameState::Splash).with_system(despawn_screen::<OnSplashScreen>),
+ );
+ }
+}
+
+/// Tag component for tagging entities on the splash screen
+#[derive(Component)]
+struct OnSplashScreen;
+
+/// Timer resource
+#[derive(Resource, Deref, DerefMut)]
+struct SplashTimer(Timer);
+
+fn splash_setup(mut commands: Commands, asset_server: Res<AssetServer>) {
+ let logo = asset_server.load("branding/logo.png");
+
+ commands
+ .spawn((
+ NodeBundle {
+ style: Style {
+ align_items: AlignItems::Center,
+ justify_content: JustifyContent::Center,
+ size: Size::new(Val::Percent(100.), Val::Percent(100.)),
+ ..Default::default()
+ },
+ ..Default::default()
+ },
+ OnSplashScreen,
+ ))
+ .with_children(|parent| {
+ parent.spawn(ImageBundle {
+ style: Style {
+ size: Size::new(Val::Px(1000.), Val::Auto),
+ ..Default::default()
+ },
+ image: UiImage(logo),
+ ..Default::default()
+ });
+ });
+
+ // Insert the timer resource
+ commands.insert_resource(SplashTimer(Timer::from_seconds(2., TimerMode::Once)));
+}
+
+fn splash_timer(
+ mut game_state: ResMut<State<GameState>>,
+ time: Res<Time>,
+ mut timer: ResMut<SplashTimer>,
+) {
+ if timer.tick(time.delta()).finished() {
+ game_state.set(GameState::MainMenu).unwrap()
+ }
+}
A sdbclient/src/util/eguipwd.rs => sdbclient/src/util/eguipwd.rs +38 -0
@@ 0,0 1,38 @@
+/*
+ * This file is part of sdbclient
+ * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
+ *
+ * Licensed under GPL-3.0-only.
+ * See LICENSE for licensing information.
+ */
+
+use bevy_egui::egui;
+
+fn password_ui(ui: &mut egui::Ui, password: &mut String) -> egui::Response {
+ let state_id = ui.id().with("show_plaintext");
+
+ let mut show_plaintext = ui.data().get_temp::<bool>(state_id).unwrap_or(false);
+
+ let result = ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
+ let response = ui
+ .add(egui::SelectableLabel::new(show_plaintext, "👁"))
+ .on_hover_text("Show/hide password");
+
+ if response.clicked() {
+ show_plaintext = !show_plaintext;
+ }
+
+ ui.add_sized(
+ ui.available_size(),
+ egui::TextEdit::singleline(password).password(!show_plaintext),
+ );
+ });
+
+ ui.data().insert_temp(state_id, show_plaintext);
+
+ result.response
+}
+
+pub fn password(password: &mut String) -> impl egui::Widget + '_ {
+ move |ui: &mut egui::Ui| password_ui(ui, password)
+}
A sdbclient/src/util/mod.rs => sdbclient/src/util/mod.rs +9 -0
@@ 0,0 1,9 @@
+/*
+ * This file is part of sdbclient
+ * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
+ *
+ * Licensed under GPL-3.0-only.
+ * See LICENSE for licensing information.
+ */
+
+pub mod eguipwd;