use crate::anim::Animation; use crate::{Color, CursorEventKind, Vec2}; use canary_script::draw::{CornerFlags, DrawContext, Rect}; use canary_script::{Font, TextLayout}; use keyframe::functions::*; pub mod button; pub mod dialog; pub mod flex; pub mod menu; 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) {} fn draw(&mut self, ctx: &DrawContext) {} fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {} } impl Widget for Option { fn update(&mut self, dt: f32) { if let Some(inner) = self.as_mut() { inner.update(dt); } } fn draw(&mut self, ctx: &DrawContext) { if let Some(inner) = self.as_mut() { inner.draw(ctx); } } fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) { if let Some(inner) = self.as_mut() { inner.on_cursor_event(kind, at); } } } pub trait RectBounds { fn get_bounds(&self) -> Rect; } #[allow(unused)] pub trait Container { fn with_children(&mut self, f: impl FnMut(&mut dyn Widget)); fn update(&mut self, dt: f32) {} fn draw(&mut self, ctx: &DrawContext) {} fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {} } impl Widget for T { fn update(&mut self, dt: f32) { ::update(self, dt); self.with_children(|w| w.update(dt)); } fn draw(&mut self, ctx: &DrawContext) { ::draw(self, ctx); self.with_children(|w| w.draw(ctx)); } fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) { ::on_cursor_event(self, kind, at); self.with_children(|w| w.on_cursor_event(kind, at)); } } 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); } _ => {} }; } }