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 menu; pub mod scroll; pub mod shell; pub mod text; use button::{Button, RectButton, RectButtonStyle, RoundButton, RoundButtonStyle}; use menu::{SlotMenu, SlotMenuEvent, TabMenu}; use scroll::{ScrollBar, ScrollView}; use shell::{Offset, 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 FixedWidth { fn get_width(&self) -> f32; } pub struct MainMenu { pub menu: Offset>, 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("Iosevka Nerd 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_right = Vec2::new(Self::SUBMENU_SPACING + Self::POSITION_X, 0.0); let reveal_slide = -0.02; let reveal_duration = 0.1; 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, inventory, settings, } } } impl Widget for MainMenu { 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.inventory.show(), SlotMenuEvent::SubmenuClose(0) => self.inventory.hide(), SlotMenuEvent::SubmenuOpen(4) => self.settings.show(), SlotMenuEvent::SubmenuClose(4) => self.settings.hide(), _ => {} }; self.menu.update(dt); self.inventory.update(dt); self.settings.update(dt); } fn draw(&mut self, ctx: &DrawContext) { self.menu.draw(&ctx); self.inventory.draw(&ctx); self.settings.draw(&ctx); } fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) { self.menu.on_cursor_event(kind, at); self.inventory.on_cursor_event(kind, at); self.settings.on_cursor_event(kind, at); } } 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 { pub menu: SlotMenu, } impl SettingsMenu { pub fn new() -> Self { let icon_font = Font::new("Iosevka Nerd Font"); let label_font = Font::new("Leon Sans"); let button_texts = [ ("Graphics", ""), ("Sound", "墳"), ("Interface", ""), ("Network", "ﴽ"), ("Account", "𣏕"), ]; 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 } } } impl Widget for SettingsMenu { fn update(&mut self, dt: f32) { if let Some(button) = self.menu.get_was_clicked() { self.menu.select(button); } match self.menu.get_event() { _ => {} }; self.menu.update(dt); } fn draw(&mut self, ctx: &DrawContext) { self.menu.draw(ctx); } fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) { self.menu.on_cursor_event(kind, at); } }