From d54a59b3e8cfee593e02270db2d970c1cc1907a5 Mon Sep 17 00:00:00 2001 From: mars Date: Sat, 30 Jul 2022 23:33:55 -0600 Subject: [PATCH] Refactor widget modules --- crates/sao-ui-rs/src/lib.rs | 31 +-- crates/sao-ui-rs/src/main_menu.rs | 284 +++++++++++++++++++++++++ crates/sao-ui-rs/src/widgets/button.rs | 9 +- crates/sao-ui-rs/src/widgets/dialog.rs | 5 +- crates/sao-ui-rs/src/widgets/menu.rs | 6 +- crates/sao-ui-rs/src/widgets/mod.rs | 268 ++--------------------- crates/sao-ui-rs/src/widgets/scroll.rs | 3 +- crates/sao-ui-rs/src/widgets/shell.rs | 6 +- crates/sao-ui-rs/src/widgets/text.rs | 2 +- 9 files changed, 313 insertions(+), 301 deletions(-) create mode 100644 crates/sao-ui-rs/src/main_menu.rs diff --git a/crates/sao-ui-rs/src/lib.rs b/crates/sao-ui-rs/src/lib.rs index 483ef5a..65bd41a 100644 --- a/crates/sao-ui-rs/src/lib.rs +++ b/crates/sao-ui-rs/src/lib.rs @@ -3,12 +3,14 @@ static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; pub mod anim; pub mod draw; +pub mod main_menu; pub mod panel; pub mod widgets; use canary_script::*; use glam::Vec2; use widgets::Widget; +use main_menu::MainMenuPanel; export_abi!(MainMenuPanel); @@ -16,35 +18,6 @@ pub const ICON_FONT: &str = "Iosevka Nerd Font"; pub const DISPLAY_FONT: &str = "Homenaje"; pub const CONTENT_FONT: &str = "Liberation Sans"; -pub struct MainMenuPanel { - panel: Panel, - menu: widgets::MainMenu, -} - -impl BindPanel for MainMenuPanel { - fn bind(panel: Panel) -> Box { - Box::new(Self { - panel, - menu: widgets::MainMenu::default(), - }) - } -} - -impl PanelImpl for MainMenuPanel { - fn update(&mut self, dt: f32) { - self.menu.update(dt); - } - - fn draw(&mut self) { - let ctx = canary_script::draw::DrawContext::new(self.panel); - self.menu.draw(&ctx); - } - - fn on_cursor_event(&mut self, kind: CursorEventKind, at: canary_script::Vec2) { - self.menu.on_cursor_event(kind, at.into()); - } -} - pub struct ConfirmationDialogPanel { panel: Panel, dialog: widgets::dialog::Dialog, diff --git a/crates/sao-ui-rs/src/main_menu.rs b/crates/sao-ui-rs/src/main_menu.rs new file mode 100644 index 0000000..1f99e3d --- /dev/null +++ b/crates/sao-ui-rs/src/main_menu.rs @@ -0,0 +1,284 @@ +use crate::widgets::*; +use button::{RectButton, RoundButton, RoundButtonStyle}; +use canary_script::draw::{DrawContext, Rect}; +use canary_script::{BindPanel, Color, CursorEventKind, Font, Panel, PanelImpl}; +use dialog::{Dialog, DialogInfo, DialogResponse, DialogStyle}; +use glam::Vec2; +use menu::{SlotMenu, SlotMenuEvent, TabMenu}; +use shell::{Offset, OffsetAlignment, Popup, Reveal}; +use text::LabelText; + +pub struct MainMenuPanel { + panel: Panel, + menu: MainMenu, +} + +impl BindPanel for MainMenuPanel { + fn bind(panel: Panel) -> Box { + Box::new(Self { + panel, + menu: MainMenu::default(), + }) + } +} + +impl PanelImpl for MainMenuPanel { + fn update(&mut self, dt: f32) { + Widget::update(&mut self.menu, dt); + } + + fn draw(&mut self) { + let ctx = canary_script::draw::DrawContext::new(self.panel); + Widget::draw(&mut self.menu, &ctx); + } + + fn on_cursor_event(&mut self, kind: CursorEventKind, at: canary_script::Vec2) { + Widget::on_cursor_event(&mut self.menu, kind, at.into()); + } +} + +pub struct MainMenu { + pub menu: Offset>, + pub player_info: Reveal>, + pub inventory: Reveal>, + pub settings: Reveal>, +} + +impl MainMenu { + pub const POSITION_X: f32 = -0.40; + pub const SUBMENU_SPACING: f32 = 0.1; +} + +impl Default for MainMenu { + fn default() -> Self { + let icon_font = Font::new(crate::ICON_FONT); + let icons = ["", "", "", "", "", ""]; + + let button_style = RoundButtonStyle { + radius: 0.05, + spacing: 0.01, + thickness: 0.002, + color: Color::WHITE, + icon_color: Color::BLACK, + }; + + let mut buttons = Vec::new(); + for icon in icons { + let text = LabelText { + font: icon_font, + text: icon.to_string(), + }; + + let button = RoundButton::new(button_style.clone(), Some(text)); + buttons.push(button); + } + + let menu = SlotMenu::new(buttons, 0.18); + let menu = Offset::new(menu, Vec2::new(Self::POSITION_X, 0.0)); + + let submenu_spacing_left = Vec2::new(Self::POSITION_X - Self::SUBMENU_SPACING, 0.0); + let submenu_spacing_right = Vec2::new(Self::POSITION_X + Self::SUBMENU_SPACING, 0.0); + let reveal_slide = -0.02; + let reveal_duration = 0.1; + + let player_info = PlayerInfo::new(); + let player_info = Offset::new_aligned( + player_info, + submenu_spacing_left, + OffsetAlignment::End, + OffsetAlignment::Center, + ); + let player_info = Reveal::new(player_info, -reveal_slide, reveal_duration); + + let inventory = TabMenu::new(); + let inventory = Offset::new(inventory, submenu_spacing_right); + let inventory = Reveal::new(inventory, reveal_slide, reveal_duration); + + let settings = SettingsMenu::new(); + let settings = Offset::new(settings, submenu_spacing_right); + let settings = Reveal::new(settings, reveal_slide, reveal_duration); + + Self { + menu, + player_info, + inventory, + settings, + } + } +} + +impl Container for MainMenu { + fn with_children(&mut self, mut f: impl FnMut(&mut dyn Widget)) { + f(&mut self.menu); + f(&mut self.player_info); + f(&mut self.inventory); + f(&mut self.settings); + } + + fn update(&mut self, _dt: f32) { + match self.menu.get_was_clicked() { + None => {} + Some(5) => self.menu.close(), + Some(button) => self.menu.select(button), + }; + + match self.menu.get_event() { + SlotMenuEvent::SubmenuOpen(0) => { + self.player_info.show(); + self.inventory.show(); + } + SlotMenuEvent::SubmenuClose(0) => { + self.player_info.hide(); + self.inventory.hide(); + } + SlotMenuEvent::SubmenuOpen(4) => self.settings.show(), + SlotMenuEvent::SubmenuClose(4) => self.settings.hide(), + _ => {} + }; + } +} + +pub struct PlayerInfo { + width: f32, + height: f32, + rounding: f32, +} + +impl PlayerInfo { + pub fn new() -> Self { + Self { + width: 0.5, + height: 0.9, + rounding: 0.02, + } + } +} + +impl RectBounds for PlayerInfo { + fn get_bounds(&self) -> Rect { + Rect::from_xy_size(Vec2::ZERO, Vec2::new(self.width, self.height)) + } +} + +impl Widget for PlayerInfo { + fn draw(&mut self, ctx: &DrawContext) { + ctx.draw_rounded_rect(self.get_bounds(), self.rounding, Color::WHITE); + } +} + +pub struct Inventory { + width: f32, + height: f32, +} + +impl Inventory { + pub fn new(available_width: f32) -> (Self, f32) { + let height = 1.28; + + ( + Self { + width: available_width, + height, + }, + height, + ) + } +} + +impl Widget for Inventory { + fn draw(&mut self, ctx: &DrawContext) { + let box_size = 0.06; + let box_margin = 0.02; + let box_stride = box_size + box_margin; + + let grid_width = (self.width / box_stride).floor() as usize; + let grid_height = (self.height / box_stride).floor() as usize; + let grid_offset = Vec2::new(box_margin, box_margin) / 2.0; + + for x in 0..grid_width { + for y in 0..grid_height { + let off = Vec2::new(x as f32, y as f32) * box_stride + grid_offset; + let rect = Rect::from_xy_size(off, Vec2::new(box_size, box_size)); + let color = Color::MAGENTA; + ctx.draw_rect(rect, color); + } + } + } +} + +pub struct SettingsMenu { + menu: SlotMenu, + log_out_dialog: Option>, +} + +impl SettingsMenu { + pub fn new() -> Self { + let icon_font = Font::new(crate::ICON_FONT); + let label_font = Font::new(crate::DISPLAY_FONT); + + let button_texts = [ + ("Graphics", ""), + ("Sound", "墳"), + ("Interface", ""), + ("Network", "ﴽ"), + ("Account", "𣏕"), + ("Log Out", "﫼"), + ]; + + let button_size = Vec2::new(0.4, 0.1); + let button_rect = Rect::from_xy_size(Vec2::new(0.0, -button_size.y / 2.0), button_size); + + let mut buttons = Vec::new(); + for (label, icon) in button_texts.iter() { + let label = LabelText { + font: label_font, + text: label.to_string(), + }; + + let icon = LabelText { + font: icon_font, + text: icon.to_string(), + }; + + let style = Default::default(); + + let button = RectButton::new(style, button_rect, Some(label), Some(icon)); + buttons.push(button); + } + + let menu = SlotMenu::new(buttons, 0.12); + + Self { + menu, + log_out_dialog: None, + } + } +} + +impl Container for SettingsMenu { + fn with_children(&mut self, mut f: impl FnMut(&mut dyn Widget)) { + f(&mut self.menu); + f(&mut self.log_out_dialog); + } + + fn update(&mut self, _dt: f32) { + if let Some(button) = self.menu.get_was_clicked() { + self.menu.select(button); + } + + match self.menu.get_event() { + SlotMenuEvent::SubmenuOpen(5) => { + let style = DialogStyle::default(); + let info = DialogInfo { + title: "ha jk", + content: "lmao u wish", + responses: &[DialogResponse::Yes, DialogResponse::No], + }; + let dialog = Dialog::new(style, &info); + let dialog = Popup::new(dialog, Vec2::ZERO); + let _ = self.log_out_dialog.insert(dialog); + } + _ => {} + }; + } +} diff --git a/crates/sao-ui-rs/src/widgets/button.rs b/crates/sao-ui-rs/src/widgets/button.rs index b38329e..f5aa77f 100644 --- a/crates/sao-ui-rs/src/widgets/button.rs +++ b/crates/sao-ui-rs/src/widgets/button.rs @@ -1,10 +1,5 @@ -use super::*; - -use text::Icon; - -pub trait Button { - fn was_clicked(&self) -> bool; -} +use super::prelude::*; +use text::{LabelText, Label, Icon, HorizontalAlignment}; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum ButtonState { diff --git a/crates/sao-ui-rs/src/widgets/dialog.rs b/crates/sao-ui-rs/src/widgets/dialog.rs index e98ddec..7b7d162 100644 --- a/crates/sao-ui-rs/src/widgets/dialog.rs +++ b/crates/sao-ui-rs/src/widgets/dialog.rs @@ -1,4 +1,7 @@ -use super::*; +use super::prelude::*; +use shell::Offset; +use button::{RoundButton, RoundButtonStyle}; +use text::{HorizontalAlignment, Label, LabelText}; #[derive(Copy, Clone, Debug)] pub enum DialogResponse { diff --git a/crates/sao-ui-rs/src/widgets/menu.rs b/crates/sao-ui-rs/src/widgets/menu.rs index 2c8e4f7..15a9846 100644 --- a/crates/sao-ui-rs/src/widgets/menu.rs +++ b/crates/sao-ui-rs/src/widgets/menu.rs @@ -1,4 +1,8 @@ -use super::*; +use super::prelude::*; +use crate::main_menu::Inventory; +use button::{RectButton, RectButtonStyle}; +use scroll::{ScrollBar, ScrollView}; +use shell::Offset; #[derive(Eq, PartialEq)] pub enum SlotMenuState { diff --git a/crates/sao-ui-rs/src/widgets/mod.rs b/crates/sao-ui-rs/src/widgets/mod.rs index 2d1bdcb..0876f97 100644 --- a/crates/sao-ui-rs/src/widgets/mod.rs +++ b/crates/sao-ui-rs/src/widgets/mod.rs @@ -1,8 +1,5 @@ -use crate::anim::Animation; -use crate::{Color, CursorEventKind, Vec2}; -use canary_script::draw::{CornerFlags, DrawContext, Rect}; -use canary_script::{Font, TextLayout}; -use keyframe::functions::*; +use crate::{CursorEventKind, Vec2}; +use canary_script::draw::{DrawContext, Rect}; pub mod button; pub mod dialog; @@ -12,13 +9,6 @@ pub mod scroll; pub mod shell; pub mod text; -use button::{Button, RectButton, RectButtonStyle, RoundButton, RoundButtonStyle}; -use dialog::{Dialog, DialogInfo, DialogResponse, DialogStyle}; -use menu::{SlotMenu, SlotMenuEvent, TabMenu}; -use scroll::{ScrollBar, ScrollView}; -use shell::{Offset, OffsetAlignment, Popup, Reveal}; -use text::{HorizontalAlignment, Label, LabelText}; - #[allow(unused)] pub trait Widget { fn update(&mut self, dt: f32) {} @@ -50,6 +40,10 @@ pub trait RectBounds { fn get_bounds(&self) -> Rect; } +pub trait Button { + fn was_clicked(&self) -> bool; +} + #[allow(unused)] pub trait Container { fn with_children(&mut self, f: impl FnMut(&mut dyn Widget)); @@ -75,248 +69,10 @@ impl Widget for T { } } -pub struct MainMenu { - pub menu: Offset>, - pub player_info: Reveal>, - pub inventory: Reveal>, - pub settings: Reveal>, -} - -impl MainMenu { - pub const POSITION_X: f32 = -0.40; - pub const SUBMENU_SPACING: f32 = 0.1; -} - -impl Default for MainMenu { - fn default() -> Self { - let icon_font = Font::new(crate::ICON_FONT); - let icons = ["", "", "", "", "", ""]; - - let button_style = RoundButtonStyle { - radius: 0.05, - spacing: 0.01, - thickness: 0.002, - color: Color::WHITE, - icon_color: Color::BLACK, - }; - - let mut buttons = Vec::new(); - for icon in icons { - let text = LabelText { - font: icon_font, - text: icon.to_string(), - }; - - let button = RoundButton::new(button_style.clone(), Some(text)); - buttons.push(button); - } - - let menu = SlotMenu::new(buttons, 0.18); - let menu = Offset::new(menu, Vec2::new(Self::POSITION_X, 0.0)); - - let submenu_spacing_left = Vec2::new(Self::POSITION_X - Self::SUBMENU_SPACING, 0.0); - let submenu_spacing_right = Vec2::new(Self::POSITION_X + Self::SUBMENU_SPACING, 0.0); - let reveal_slide = -0.02; - let reveal_duration = 0.1; - - let player_info = PlayerInfo::new(); - let player_info = Offset::new_aligned( - player_info, - submenu_spacing_left, - OffsetAlignment::End, - OffsetAlignment::Center, - ); - let player_info = Reveal::new(player_info, -reveal_slide, reveal_duration); - - let inventory = TabMenu::new(); - let inventory = Offset::new(inventory, submenu_spacing_right); - let inventory = Reveal::new(inventory, reveal_slide, reveal_duration); - - let settings = SettingsMenu::new(); - let settings = Offset::new(settings, submenu_spacing_right); - let settings = Reveal::new(settings, reveal_slide, reveal_duration); - - Self { - menu, - player_info, - inventory, - settings, - } - } -} - -impl Container for MainMenu { - fn with_children(&mut self, mut f: impl FnMut(&mut dyn Widget)) { - f(&mut self.menu); - f(&mut self.player_info); - f(&mut self.inventory); - f(&mut self.settings); - } - - fn update(&mut self, _dt: f32) { - match self.menu.get_was_clicked() { - None => {} - Some(5) => self.menu.close(), - Some(button) => self.menu.select(button), - }; - - match self.menu.get_event() { - SlotMenuEvent::SubmenuOpen(0) => { - self.player_info.show(); - self.inventory.show(); - } - SlotMenuEvent::SubmenuClose(0) => { - self.player_info.hide(); - self.inventory.hide(); - } - SlotMenuEvent::SubmenuOpen(4) => self.settings.show(), - SlotMenuEvent::SubmenuClose(4) => self.settings.hide(), - _ => {} - }; - } -} - -pub struct PlayerInfo { - width: f32, - height: f32, - rounding: f32, -} - -impl PlayerInfo { - pub fn new() -> Self { - Self { - width: 0.5, - height: 0.9, - rounding: 0.02, - } - } -} - -impl RectBounds for PlayerInfo { - fn get_bounds(&self) -> Rect { - Rect::from_xy_size(Vec2::ZERO, Vec2::new(self.width, self.height)) - } -} - -impl Widget for PlayerInfo { - fn draw(&mut self, ctx: &DrawContext) { - ctx.draw_rounded_rect(self.get_bounds(), self.rounding, Color::WHITE); - } -} - -pub struct Inventory { - width: f32, - height: f32, -} - -impl Inventory { - pub fn new(available_width: f32) -> (Self, f32) { - let height = 1.28; - - ( - Self { - width: available_width, - height, - }, - height, - ) - } -} - -impl Widget for Inventory { - fn draw(&mut self, ctx: &DrawContext) { - let box_size = 0.06; - let box_margin = 0.02; - let box_stride = box_size + box_margin; - - let grid_width = (self.width / box_stride).floor() as usize; - let grid_height = (self.height / box_stride).floor() as usize; - let grid_offset = Vec2::new(box_margin, box_margin) / 2.0; - - for x in 0..grid_width { - for y in 0..grid_height { - let off = Vec2::new(x as f32, y as f32) * box_stride + grid_offset; - let rect = Rect::from_xy_size(off, Vec2::new(box_size, box_size)); - let color = Color::MAGENTA; - ctx.draw_rect(rect, color); - } - } - } -} - -pub struct SettingsMenu { - menu: SlotMenu, - log_out_dialog: Option>, -} - -impl SettingsMenu { - pub fn new() -> Self { - let icon_font = Font::new(crate::ICON_FONT); - let label_font = Font::new(crate::DISPLAY_FONT); - - let button_texts = [ - ("Graphics", ""), - ("Sound", "墳"), - ("Interface", ""), - ("Network", "ﴽ"), - ("Account", "𣏕"), - ("Log Out", "﫼"), - ]; - - let button_size = Vec2::new(0.4, 0.1); - let button_rect = Rect::from_xy_size(Vec2::new(0.0, -button_size.y / 2.0), button_size); - - let mut buttons = Vec::new(); - for (label, icon) in button_texts.iter() { - let label = LabelText { - font: label_font, - text: label.to_string(), - }; - - let icon = LabelText { - font: icon_font, - text: icon.to_string(), - }; - - let style = Default::default(); - - let button = RectButton::new(style, button_rect, Some(label), Some(icon)); - buttons.push(button); - } - - let menu = SlotMenu::new(buttons, 0.12); - - Self { - menu, - log_out_dialog: None, - } - } -} - -impl Container for SettingsMenu { - fn with_children(&mut self, mut f: impl FnMut(&mut dyn Widget)) { - f(&mut self.menu); - f(&mut self.log_out_dialog); - } - - fn update(&mut self, _dt: f32) { - if let Some(button) = self.menu.get_was_clicked() { - self.menu.select(button); - } - - match self.menu.get_event() { - SlotMenuEvent::SubmenuOpen(5) => { - let style = DialogStyle::default(); - let info = DialogInfo { - title: "ha jk", - content: "lmao u wish", - responses: &[DialogResponse::Yes, DialogResponse::No], - }; - let dialog = Dialog::new(style, &info); - let dialog = Popup::new(dialog, Vec2::ZERO); - let _ = self.log_out_dialog.insert(dialog); - } - _ => {} - }; - } +pub mod prelude { + pub use super::*; + pub use canary_script::{CursorEventKind, Color, Font, TextLayout}; + pub use canary_script::draw::{CornerFlags, Rect, DrawContext}; + pub use crate::anim::Animation; + pub use keyframe::functions::*; } diff --git a/crates/sao-ui-rs/src/widgets/scroll.rs b/crates/sao-ui-rs/src/widgets/scroll.rs index 8c5a3df..56cdd6e 100644 --- a/crates/sao-ui-rs/src/widgets/scroll.rs +++ b/crates/sao-ui-rs/src/widgets/scroll.rs @@ -1,4 +1,5 @@ -use super::*; +use super::prelude::*; +use shell::Offset; pub struct ScrollBarStyle { pub margin: Vec2, diff --git a/crates/sao-ui-rs/src/widgets/shell.rs b/crates/sao-ui-rs/src/widgets/shell.rs index db8e5ad..d31ac9f 100644 --- a/crates/sao-ui-rs/src/widgets/shell.rs +++ b/crates/sao-ui-rs/src/widgets/shell.rs @@ -1,8 +1,4 @@ -use super::{RectBounds, Widget}; -use crate::anim::Animation; -use crate::{CursorEventKind, Vec2}; -use canary_script::draw::DrawContext; -use keyframe::functions::*; +use super::prelude::*; use std::ops::{Deref, DerefMut}; macro_rules! impl_shell_inner { diff --git a/crates/sao-ui-rs/src/widgets/text.rs b/crates/sao-ui-rs/src/widgets/text.rs index afe4db2..6bb6a25 100644 --- a/crates/sao-ui-rs/src/widgets/text.rs +++ b/crates/sao-ui-rs/src/widgets/text.rs @@ -1,4 +1,4 @@ -use super::*; +use super::prelude::*; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum HorizontalAlignment {