refactor ui logic and impl
This commit is contained in:
@@ -24,6 +24,9 @@ pub(crate) enum NavigationSystems {
|
|||||||
#[derive(Resource, Default)]
|
#[derive(Resource, Default)]
|
||||||
pub(crate) struct NavigationInputState {
|
pub(crate) struct NavigationInputState {
|
||||||
/// Set by the initializer and consumed by the directional-navigation system in the same frame.
|
/// Set by the initializer and consumed by the directional-navigation system in the same frame.
|
||||||
|
///
|
||||||
|
/// This is so that the action that creates a focus can be ignored and used only to set
|
||||||
|
/// the focus to something.
|
||||||
pub(crate) focus_initialized: bool,
|
pub(crate) focus_initialized: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -12,7 +12,7 @@ use bevy::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
use crate::main_menu::InteractionColors;
|
use crate::ui_logic::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);
|
||||||
@@ -41,7 +41,7 @@ pub struct LobbyUiState {
|
|||||||
impl Default for LobbyUiState {
|
impl Default for LobbyUiState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
privacy: LobbyPrivacy::Public,
|
privacy: LobbyPrivacy::InviteOnly,
|
||||||
port: 5599,
|
port: 5599,
|
||||||
password: String::new(),
|
password: String::new(),
|
||||||
session_active: false,
|
session_active: false,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ mod lobby;
|
|||||||
mod main_menu;
|
mod main_menu;
|
||||||
mod networking;
|
mod networking;
|
||||||
mod save;
|
mod save;
|
||||||
|
mod ui_logic;
|
||||||
|
|
||||||
// TODO: pull from cargo.toml or something?
|
// TODO: pull from cargo.toml or something?
|
||||||
pub const CURRENT_GAME_VERSION: &str = "v0.0.1";
|
pub const CURRENT_GAME_VERSION: &str = "v0.0.1";
|
||||||
|
|||||||
+5
-108
@@ -1,6 +1,7 @@
|
|||||||
|
use crate::AppState;
|
||||||
use crate::focus_navigation::{NavigationInputState, NavigationSystems};
|
use crate::focus_navigation::{NavigationInputState, NavigationSystems};
|
||||||
use crate::save::{ActiveGameSave, create_game_save, delete_game_save, get_saves};
|
use crate::save::{ActiveGameSave, create_game_save, delete_game_save, get_saves};
|
||||||
use crate::{AppState, WINDOW_TARGET_HEIGHT, WINDOW_TARGET_WIDTH};
|
use crate::ui_logic::{InteractionColors, Selected, scale_ui, style_ui_elements};
|
||||||
use bevy::color::palettes::tailwind::SLATE_900;
|
use bevy::color::palettes::tailwind::SLATE_900;
|
||||||
use bevy::input::gamepad::GamepadButton;
|
use bevy::input::gamepad::GamepadButton;
|
||||||
use bevy::input_focus::{
|
use bevy::input_focus::{
|
||||||
@@ -9,7 +10,6 @@ use bevy::input_focus::{
|
|||||||
};
|
};
|
||||||
use bevy::picking::hover::Hovered;
|
use bevy::picking::hover::Hovered;
|
||||||
use bevy::text::{EditableText, EditableTextFilter, TextCursorStyle};
|
use bevy::text::{EditableText, EditableTextFilter, TextCursorStyle};
|
||||||
use bevy::ui::Pressed;
|
|
||||||
use bevy::ui::auto_directional_navigation::AutoDirectionalNavigation;
|
use bevy::ui::auto_directional_navigation::AutoDirectionalNavigation;
|
||||||
use bevy::ui_widgets::{Button, TextInput};
|
use bevy::ui_widgets::{Button, TextInput};
|
||||||
use bevy::{prelude::*, ui_widgets::Activate};
|
use bevy::{prelude::*, ui_widgets::Activate};
|
||||||
@@ -40,55 +40,6 @@ const N_LAYERS: usize = 5;
|
|||||||
#[derive(Component, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug)]
|
#[derive(Component, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug)]
|
||||||
struct GameNameTextField;
|
struct GameNameTextField;
|
||||||
|
|
||||||
/// A UI object selected by activation, distinct from keyboard/pointer focus.
|
|
||||||
#[derive(Component, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug)]
|
|
||||||
struct Selected;
|
|
||||||
|
|
||||||
/// Defines colors that a UI object should turn when interactions occur.
|
|
||||||
#[derive(Component, Clone, PartialEq, Default, Debug)]
|
|
||||||
pub struct InteractionColors {
|
|
||||||
pub default: Color,
|
|
||||||
pub hovered: Color,
|
|
||||||
pub pressed: Color,
|
|
||||||
pub selected: Color,
|
|
||||||
pub selected_hovered: Color,
|
|
||||||
pub selected_pressed: Color,
|
|
||||||
pub default_outline: Color,
|
|
||||||
pub focused_outline: Color,
|
|
||||||
}
|
|
||||||
impl InteractionColors {
|
|
||||||
pub const DEFAULT: InteractionColors = InteractionColors {
|
|
||||||
default: Color::srgb(0.25, 0.63, 0.84),
|
|
||||||
hovered: Color::srgb(0.31, 0.77, 0.93),
|
|
||||||
pressed: Color::srgb(0.16, 0.55, 0.89),
|
|
||||||
selected: Color::srgb(0.93, 0.66, 0.06),
|
|
||||||
selected_hovered: Color::srgb(1., 0.74, 0.16),
|
|
||||||
selected_pressed: Color::srgb(0.86, 0.56, 0.),
|
|
||||||
default_outline: Color::BLACK,
|
|
||||||
focused_outline: Color::WHITE,
|
|
||||||
};
|
|
||||||
pub const GOOD: InteractionColors = InteractionColors {
|
|
||||||
default: Color::srgb(0.25, 0.75, 0.25),
|
|
||||||
hovered: Color::srgb(0.35, 0.85, 0.35),
|
|
||||||
pressed: Color::srgb(0.15, 0.65, 0.15),
|
|
||||||
selected: Color::srgb(0.35, 0.8, 0.35),
|
|
||||||
selected_hovered: Color::srgb(0.45, 0.9, 0.45),
|
|
||||||
selected_pressed: Color::srgb(0.25, 0.7, 0.25),
|
|
||||||
default_outline: Color::BLACK,
|
|
||||||
focused_outline: Color::WHITE,
|
|
||||||
};
|
|
||||||
pub const DANGER: InteractionColors = InteractionColors {
|
|
||||||
default: Color::srgb(0.75, 0.25, 0.25),
|
|
||||||
hovered: Color::srgb(0.85, 0.35, 0.35),
|
|
||||||
pressed: Color::srgb(0.65, 0.15, 0.15),
|
|
||||||
selected: Color::srgb(0.8, 0.3, 0.3),
|
|
||||||
selected_hovered: Color::srgb(0.9, 0.4, 0.4),
|
|
||||||
selected_pressed: Color::srgb(0.7, 0.2, 0.2),
|
|
||||||
default_outline: Color::BLACK,
|
|
||||||
focused_outline: Color::WHITE,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The root of the menu
|
/// The root of the menu
|
||||||
#[derive(Component, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug)]
|
#[derive(Component, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug)]
|
||||||
struct MenuRoot;
|
struct MenuRoot;
|
||||||
@@ -551,6 +502,9 @@ fn button_activate_observer(
|
|||||||
// push next layer
|
// push next layer
|
||||||
menu.push(next_layer.0.clone());
|
menu.push(next_layer.0.clone());
|
||||||
|
|
||||||
|
// TODO: This custom "selected" logic, where there is one selected button
|
||||||
|
// on the list of options is a lot like a radio button. Probably should replace
|
||||||
|
// this custom impl with a modified radio button impl
|
||||||
for (entity, selected) in q_selected.iter_many(layer_entries).matched() {
|
for (entity, selected) in q_selected.iter_many(layer_entries).matched() {
|
||||||
if entity == event.entity {
|
if entity == event.entity {
|
||||||
if !selected {
|
if !selected {
|
||||||
@@ -563,63 +517,6 @@ fn button_activate_observer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Styles the UI elements according to their [`InteractionColors`].
|
|
||||||
fn style_ui_elements(
|
|
||||||
input_focus: Res<InputFocus>,
|
|
||||||
mut q_elements: Query<
|
|
||||||
(
|
|
||||||
Entity,
|
|
||||||
Option<&Hovered>,
|
|
||||||
Has<Pressed>,
|
|
||||||
Option<&mut BackgroundColor>,
|
|
||||||
Option<&mut BorderColor>,
|
|
||||||
&InteractionColors,
|
|
||||||
Has<Selected>,
|
|
||||||
),
|
|
||||||
Or<(With<BackgroundColor>, With<BorderColor>)>,
|
|
||||||
>,
|
|
||||||
) {
|
|
||||||
let focused_entity = input_focus.get();
|
|
||||||
|
|
||||||
for (entity, hovered, pressed, bg, border, colors, selected) in &mut q_elements {
|
|
||||||
let is_focused = focused_entity == Some(entity);
|
|
||||||
// if the entity is focused, its similar to it being hovered.
|
|
||||||
let hovered = hovered.is_some_and(|h| h.get()) || is_focused;
|
|
||||||
|
|
||||||
let new_color = match (selected, pressed, hovered) {
|
|
||||||
(true, true, _) => colors.selected_pressed,
|
|
||||||
(true, false, true) => colors.selected_hovered,
|
|
||||||
(true, false, false) => colors.selected,
|
|
||||||
(false, true, _) => colors.pressed,
|
|
||||||
(false, false, true) => colors.hovered,
|
|
||||||
(false, false, false) => colors.default,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(mut border) = border {
|
|
||||||
if is_focused {
|
|
||||||
if border.top != colors.focused_outline {
|
|
||||||
border.set_all(colors.focused_outline);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if border.top != colors.default_outline {
|
|
||||||
border.set_all(colors.default_outline);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(mut bg) = bg {
|
|
||||||
if bg.0 != new_color {
|
|
||||||
bg.0 = new_color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scale_ui(mut ui_scale: ResMut<UiScale>, window: Single<&Window>) {
|
|
||||||
let size = window.size();
|
|
||||||
let scale_factor = f32::min(size.x / WINDOW_TARGET_WIDTH, size.y / WINDOW_TARGET_HEIGHT);
|
|
||||||
ui_scale.0 = scale_factor;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: generify the inputing of the nav inputs that are in here
|
// TODO: generify the inputing of the nav inputs that are in here
|
||||||
fn focus_rightmost_layer_on_navigation(
|
fn focus_rightmost_layer_on_navigation(
|
||||||
keyboard: Res<ButtonInput<KeyCode>>,
|
keyboard: Res<ButtonInput<KeyCode>>,
|
||||||
|
|||||||
+109
@@ -0,0 +1,109 @@
|
|||||||
|
use crate::{WINDOW_TARGET_HEIGHT, WINDOW_TARGET_WIDTH};
|
||||||
|
use bevy::{input_focus::InputFocus, picking::hover::Hovered, prelude::*, ui::Pressed};
|
||||||
|
|
||||||
|
/// A UI object selected by activation, distinct from keyboard/pointer focus.
|
||||||
|
#[derive(Component, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug)]
|
||||||
|
pub struct Selected;
|
||||||
|
|
||||||
|
/// Defines colors that a UI object should turn when interactions occur.
|
||||||
|
#[derive(Component, Clone, PartialEq, Default, Debug)]
|
||||||
|
pub struct InteractionColors {
|
||||||
|
pub default: Color,
|
||||||
|
pub hovered: Color,
|
||||||
|
pub pressed: Color,
|
||||||
|
pub selected: Color,
|
||||||
|
pub selected_hovered: Color,
|
||||||
|
pub selected_pressed: Color,
|
||||||
|
pub default_outline: Color,
|
||||||
|
pub focused_outline: Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InteractionColors {
|
||||||
|
pub const DEFAULT: InteractionColors = InteractionColors {
|
||||||
|
default: Color::srgb(0.25, 0.63, 0.84),
|
||||||
|
hovered: Color::srgb(0.31, 0.77, 0.93),
|
||||||
|
pressed: Color::srgb(0.16, 0.55, 0.89),
|
||||||
|
selected: Color::srgb(0.93, 0.66, 0.06),
|
||||||
|
selected_hovered: Color::srgb(1., 0.74, 0.16),
|
||||||
|
selected_pressed: Color::srgb(0.86, 0.56, 0.),
|
||||||
|
default_outline: Color::BLACK,
|
||||||
|
focused_outline: Color::WHITE,
|
||||||
|
};
|
||||||
|
pub const GOOD: InteractionColors = InteractionColors {
|
||||||
|
default: Color::srgb(0.25, 0.75, 0.25),
|
||||||
|
hovered: Color::srgb(0.35, 0.85, 0.35),
|
||||||
|
pressed: Color::srgb(0.15, 0.65, 0.15),
|
||||||
|
selected: Color::srgb(0.35, 0.8, 0.35),
|
||||||
|
selected_hovered: Color::srgb(0.45, 0.9, 0.45),
|
||||||
|
selected_pressed: Color::srgb(0.25, 0.7, 0.25),
|
||||||
|
default_outline: Color::BLACK,
|
||||||
|
focused_outline: Color::WHITE,
|
||||||
|
};
|
||||||
|
pub const DANGER: InteractionColors = InteractionColors {
|
||||||
|
default: Color::srgb(0.75, 0.25, 0.25),
|
||||||
|
hovered: Color::srgb(0.85, 0.35, 0.35),
|
||||||
|
pressed: Color::srgb(0.65, 0.15, 0.15),
|
||||||
|
selected: Color::srgb(0.8, 0.3, 0.3),
|
||||||
|
selected_hovered: Color::srgb(0.9, 0.4, 0.4),
|
||||||
|
selected_pressed: Color::srgb(0.7, 0.2, 0.2),
|
||||||
|
default_outline: Color::BLACK,
|
||||||
|
focused_outline: Color::WHITE,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles the UI elements according to their [`InteractionColors`].
|
||||||
|
pub fn style_ui_elements(
|
||||||
|
input_focus: Res<InputFocus>,
|
||||||
|
mut q_elements: Query<
|
||||||
|
(
|
||||||
|
Entity,
|
||||||
|
Option<&Hovered>,
|
||||||
|
Has<Pressed>,
|
||||||
|
Option<&mut BackgroundColor>,
|
||||||
|
Option<&mut BorderColor>,
|
||||||
|
&InteractionColors,
|
||||||
|
Has<Selected>,
|
||||||
|
),
|
||||||
|
Or<(With<BackgroundColor>, With<BorderColor>)>,
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
let focused_entity = input_focus.get();
|
||||||
|
|
||||||
|
for (entity, hovered, pressed, bg, border, colors, selected) in &mut q_elements {
|
||||||
|
let is_focused = focused_entity == Some(entity);
|
||||||
|
// if the entity is focused, its similar to it being hovered.
|
||||||
|
let hovered = hovered.is_some_and(|h| h.get()) || is_focused;
|
||||||
|
|
||||||
|
let new_color = match (selected, pressed, hovered) {
|
||||||
|
(true, true, _) => colors.selected_pressed,
|
||||||
|
(true, false, true) => colors.selected_hovered,
|
||||||
|
(true, false, false) => colors.selected,
|
||||||
|
(false, true, _) => colors.pressed,
|
||||||
|
(false, false, true) => colors.hovered,
|
||||||
|
(false, false, false) => colors.default,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(mut border) = border {
|
||||||
|
if is_focused {
|
||||||
|
if border.top != colors.focused_outline {
|
||||||
|
border.set_all(colors.focused_outline);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if border.top != colors.default_outline {
|
||||||
|
border.set_all(colors.default_outline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(mut bg) = bg {
|
||||||
|
if bg.0 != new_color {
|
||||||
|
bg.0 = new_color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scale_ui(mut ui_scale: ResMut<UiScale>, window: Single<&Window>) {
|
||||||
|
let size = window.size();
|
||||||
|
let scale_factor = f32::min(size.x / WINDOW_TARGET_WIDTH, size.y / WINDOW_TARGET_HEIGHT);
|
||||||
|
ui_scale.0 = scale_factor;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user