// Copyright (c) 2022 Marceline Cramer // SPDX-License-Identifier: AGPL-3.0-or-later use crate::widgets::prelude::*; use crate::{DrawContext, Rect}; use button::{RectButton, RoundButton, RoundButtonStyle}; use dialog::{Dialog, DialogInfo, DialogResponse, DialogStyle}; use menu::{SlotMenu, SlotMenuEvent, TabMenu}; use palette::Palette; use shell::{Offset, OffsetAlignment, Popup, Reveal}; use text::LabelText; pub struct MainMenuPanel { panel: Panel, menu: MainMenu, } impl PanelImpl for MainMenuPanel { fn update(&mut self, dt: f32) { Widget::update(&mut self.menu, dt); } fn draw(&mut self) { let ctx = DrawContext::new(self.panel); Widget::draw(&mut self.menu, &ctx); } fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) { Widget::on_cursor_event(&mut self.menu, kind, at.into()); } fn on_resize(&mut self, _size: Vec2) {} fn on_message(&mut self, msg: Message) {} } impl MainMenuPanel { pub fn bind(panel: Panel, msg: Message) -> Box { Box::new(Self { panel, menu: MainMenu::default(), }) } } pub struct MainMenu { pub menu: Offset>, pub player_info: Reveal>, pub inventory: Reveal>, pub palette: Reveal>, pub settings: Reveal>, } impl MainMenu { pub const ANCHOR: Vec2 = Vec2::new(100.0, 100.0); pub const SUBMENU_SPACING: f32 = 15.0; } impl Default for MainMenu { fn default() -> Self { let icon_font = Font::new(crate::ICON_FONT); let icons = ["", "", "", "", "", ""]; let button_style = RoundButtonStyle { radius: 7.5, spacing: 1.5, thickness: 0.4, body_color: THEME.palette.surface, ring_color: THEME.palette.surface, icon_color: THEME.palette.text, }; 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, 30.0); let menu = Offset::new(menu, Self::ANCHOR); let submenu_spacing = Vec2::new(Self::SUBMENU_SPACING, 0.0); let submenu_spacing_left = Self::ANCHOR - submenu_spacing; let submenu_spacing_right = Self::ANCHOR + submenu_spacing; let reveal_slide = -5.0; 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 palette = Palette::new(Default::default()); let palette = Offset::new_aligned( palette, submenu_spacing_left, OffsetAlignment::End, OffsetAlignment::Center, ); let palette = Reveal::new(palette, -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, palette, 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.palette); 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.palette.show(); self.settings.show(); } SlotMenuEvent::SubmenuClose(4) => { self.palette.hide(); self.settings.hide(); } _ => {} }; } } pub struct PlayerInfo { width: f32, height: f32, rounding: f32, color: Color, } impl PlayerInfo { pub fn new() -> Self { Self { width: 70.0, height: 120.0, rounding: 5.0, color: THEME.palette.surface, } } } 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, self.color); } } pub struct Inventory { width: f32, height: f32, } impl Inventory { pub fn new(available_width: f32) -> (Self, f32) { let height = 1024.0; ( Self { width: available_width, height, }, height, ) } } impl Widget for Inventory { fn draw(&mut self, ctx: &DrawContext) { let box_size = 12.0; let box_margin = 4.0; 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(90.0, 20.0); 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, 25.0); 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".to_string(), content: "lmao u wish".to_string(), responses: [DialogResponse::Yes, DialogResponse::No].to_vec(), }; let dialog = Dialog::new(style, &info); let dialog = Popup::new(dialog, Vec2::ZERO); let _ = self.log_out_dialog.insert(dialog); } _ => {} }; } }