use crate::{ assets::AssetPath, components::{ComponentTrait, button::ComponentButton}, globals::WguiGlobals, i18n::Translation, layout::Layout, parser::{self, Fetchable, ParserState, TemplateParams}, task::Tasks, windowing::window::{WguiWindow, WguiWindowParams, WguiWindowParamsExtra}, }; use glam::Vec2; use std::rc::Rc; #[derive(Clone)] pub struct Cell { pub title: Translation, pub tooltip: Option, pub action_name: Option>, pub attribs: Vec, } pub enum Blueprint { Cells(Vec), Template { template_name: Rc, template_params: TemplateParams, }, } pub struct OpenParams { pub on_custom_attribs: Option, pub position: Vec2, pub blueprint: Blueprint, } #[derive(Clone)] enum Task { ActionClicked(Option>), } #[derive(Default)] pub struct ContextMenu { window: WguiWindow, pending_open: Option, tasks: Tasks, } fn doc_params<'a>(globals: &WguiGlobals) -> parser::ParseDocumentParams<'a> { parser::ParseDocumentParams { globals: globals.clone(), path: AssetPath::WguiInternal("wgui/context_menu.xml"), extra: Default::default(), } } #[derive(Default)] pub enum TickResult { /// Nothing happened #[default] None, /// The context menu was opened. Opened, /// User has selected an action. Action(Rc), /// The context menu was closed without an action. Closed, } impl ContextMenu { pub fn open(&mut self, params: OpenParams) { self.pending_open = Some(params); } pub fn close(&self) { self.window.close(); } fn open_process( &mut self, params: OpenParams, layout: &mut Layout, parser_state: &mut ParserState, ) -> anyhow::Result<()> { let cells = match params.blueprint { Blueprint::Template { template_name, template_params, } => parser_state.context_menu_parse_cells(&template_name, &template_params)?, Blueprint::Cells(cells) => cells, }; let globals = layout.state.globals.clone(); self.window.open(&mut WguiWindowParams { layout, position: params.position, extra: WguiWindowParamsExtra { with_decorations: false, close_if_clicked_outside: true, ..Default::default() }, })?; let content = self.window.get_content(); let doc_params = doc_params(&globals); let mut inner_parser = parser::parse_from_assets(&doc_params, layout, content.id)?; let id_buttons = inner_parser.get_widget_id("buttons")?; for (idx, cell) in cells.iter().enumerate() { let mut par = TemplateParams::new(); par.insert_rc("text", cell.title.generate(&mut globals.i18n())); if let Some(tooltip) = cell.tooltip.as_ref() { par.insert_rc("tooltip_str", tooltip.generate(&mut globals.i18n())); } let mut data_cell = inner_parser.parse_template_only(&doc_params, "Cell", layout, id_buttons, par)?; let button = data_cell.fetch_component_as::("button")?; let button_id = button.base().get_id(); parser_state.data.take_results_from(&mut data_cell); self .tasks .handle_button(&button, Task::ActionClicked(cell.action_name.clone())); if let Some(c) = params.on_custom_attribs.as_ref() { (*c)(parser::CustomAttribsInfo { pairs: &cell.attribs, parent_id: id_buttons, widget_id: button_id, widgets: &layout.state.widgets, }); } if idx < cells.len() - 1 { inner_parser.realize_template(&doc_params, "Separator", layout, id_buttons, Default::default())?; } } Ok(()) } pub fn tick(&mut self, layout: &mut Layout, parser_state: &mut ParserState) -> anyhow::Result { if let Some(p) = self.pending_open.take() { self.open_process(p, layout, parser_state)?; let _ = self.tasks.drain(); return Ok(TickResult::Opened); } let mut result = TickResult::default(); for task in self.tasks.drain() { match task { Task::ActionClicked(Some(action_name)) => { result = TickResult::Action(action_name); self.close(); } Task::ActionClicked(None) => { result = TickResult::Closed; self.close(); } } } Ok(result) } }