From 165070da51c26820d6fd228e4f77e47647134cac Mon Sep 17 00:00:00 2001 From: galister <22305755+galister@users.noreply.github.com> Date: Wed, 7 Jan 2026 13:59:14 +0900 Subject: [PATCH] refactor context_menu to only require parser_state on tick --- uidev/src/testbed/testbed_generic.rs | 52 ++++++++++++------------- wgui/src/parser/mod.rs | 21 +++++----- wgui/src/windowing/context_menu.rs | 57 +++++++++++++++++----------- 3 files changed, 68 insertions(+), 62 deletions(-) diff --git a/uidev/src/testbed/testbed_generic.rs b/uidev/src/testbed/testbed_generic.rs index acb8faf..fd05bc1 100644 --- a/uidev/src/testbed/testbed_generic.rs +++ b/uidev/src/testbed/testbed_generic.rs @@ -8,9 +8,9 @@ use glam::Vec2; use wgui::{ assets::AssetPath, components::{ - Component, button::{ButtonClickCallback, ComponentButton}, checkbox::ComponentCheckbox, + Component, }, drawing::Color, event::StyleSetRequest, @@ -35,15 +35,13 @@ pub enum TestbedTask { } struct Data { - #[allow(dead_code)] - state: ParserState, - popup_window: WguiWindow, context_menu: context_menu::ContextMenu, } pub struct TestbedGeneric { pub layout: Layout, + pub parser_state: ParserState, tasks: Tasks, globals: WguiGlobals, @@ -122,15 +120,15 @@ impl TestbedGeneric { dev_mode: false, }; - let (layout, state) = wgui::parser::new_layout_from_assets( + let (layout, parser_state) = wgui::parser::new_layout_from_assets( &TestbedGeneric::doc_params(&globals, extra), &LayoutParams { resize_to_parent: true, }, )?; - let cb_visible = state.fetch_component_as::("cb_visible")?; - let div_visibility = state.fetch_widget(&layout.state, "div_visibility")?; + let cb_visible = parser_state.fetch_component_as::("cb_visible")?; + let div_visibility = parser_state.fetch_widget(&layout.state, "div_visibility")?; cb_visible.on_toggle(Box::new(move |common, evt| { common.alterables.set_style( @@ -144,20 +142,21 @@ impl TestbedGeneric { Ok(()) })); - let label_cur_option = state.fetch_widget(&layout.state, "label_current_option")?; + let label_cur_option = parser_state.fetch_widget(&layout.state, "label_current_option")?; - let button_context_menu = state.fetch_component_as::("button_context_menu")?; - let button_click_me = state.fetch_component_as::("button_click_me")?; + let button_context_menu = + parser_state.fetch_component_as::("button_context_menu")?; + let button_click_me = parser_state.fetch_component_as::("button_click_me")?; let button = button_click_me.clone(); button_click_me.on_click(Box::new(move |common, _e| { button.set_text(common, Translation::from_raw_text("congrats!")); Ok(()) })); - let button_popup = state.fetch_component_as::("button_popup")?; - let button_red = state.fetch_component_as::("button_red")?; - let button_aqua = state.fetch_component_as::("button_aqua")?; - let button_yellow = state.fetch_component_as::("button_yellow")?; + let button_popup = parser_state.fetch_component_as::("button_popup")?; + let button_red = parser_state.fetch_component_as::("button_red")?; + let button_aqua = parser_state.fetch_component_as::("button_aqua")?; + let button_yellow = parser_state.fetch_component_as::("button_yellow")?; handle_button_click(button_red, label_cur_option.widget.clone(), "Clicked red"); handle_button_click(button_aqua, label_cur_option.widget.clone(), "Clicked aqua"); @@ -167,7 +166,7 @@ impl TestbedGeneric { "Clicked yellow", ); - let cb_first = state.fetch_component_as::("cb_first")?; + let cb_first = parser_state.fetch_component_as::("cb_first")?; let label = label_cur_option.widget.clone(); cb_first.on_toggle(Box::new(move |common, e| { let mut widget = label.get_as::().unwrap(); @@ -178,10 +177,10 @@ impl TestbedGeneric { let testbed = Self { layout, + parser_state, tasks: Default::default(), globals: globals.clone(), data: Rc::new(RefCell::new(Data { - state, popup_window: WguiWindow::default(), context_menu: context_menu::ContextMenu::default(), })), @@ -214,7 +213,7 @@ impl TestbedGeneric { ) -> anyhow::Result<()> { match task { TestbedTask::ShowPopup => self.show_popup(params, data)?, - TestbedTask::ShowContextMenu(position) => self.show_context_menu(params, data, *position)?, + TestbedTask::ShowContextMenu(position) => self.show_context_menu(params, data, *position), } Ok(()) @@ -255,18 +254,15 @@ impl TestbedGeneric { _params: &mut TestbedUpdateParams, data: &mut Data, position: Vec2, - ) -> anyhow::Result<()> { - data.state.instantiate_context_menu( - Some(Rc::new(move |custom_attribs| { + ) { + data.context_menu.open(context_menu::OpenParams { + on_custom_attribs: Some(Rc::new(move |custom_attribs| { log::info!("custom attribs {:?}", custom_attribs.pairs); })), - "my_context_menu", - Default::default(), - &mut data.context_menu, + template_name: "my_context_menu".into(), + template_params: Default::default(), position, - )?; - - Ok(()) + }); } } @@ -286,7 +282,9 @@ impl Testbed for TestbedGeneric { self.process_task(&task, &mut params, &mut data)?; } - let res = data.context_menu.tick(&mut self.layout)?; + let res = data + .context_menu + .tick(&mut self.layout, &mut self.parser_state)?; if let Some(action_name) = res.action_name { log::info!("got action: {}", action_name); } diff --git a/wgui/src/parser/mod.rs b/wgui/src/parser/mod.rs index 3a7d95d..7c7b2e3 100644 --- a/wgui/src/parser/mod.rs +++ b/wgui/src/parser/mod.rs @@ -260,14 +260,12 @@ impl ParserState { Ok(()) } - pub fn instantiate_context_menu( + pub(crate) fn context_menu_create_blueprint( &mut self, - on_custom_attribs: Option, template_name: &str, - template_params: HashMap, Rc>, - context_menu: &mut context_menu::ContextMenu, + template_params: &HashMap, Rc>, position: Vec2, - ) -> anyhow::Result<()> { + ) -> anyhow::Result { let Some(template) = self.data.templates.get(template_name) else { anyhow::bail!("no template named \"{template_name}\" found"); }; @@ -319,13 +317,12 @@ impl ParserState { } } - context_menu.open(context_menu::OpenParams { - cells, - on_custom_attribs, - position, - }); - - Ok(()) + Ok( + context_menu::Blueprint { + cells, + position, + } + ) } } diff --git a/wgui/src/windowing/context_menu.rs b/wgui/src/windowing/context_menu.rs index 238a594..756df1a 100644 --- a/wgui/src/windowing/context_menu.rs +++ b/wgui/src/windowing/context_menu.rs @@ -1,7 +1,4 @@ -use std::{ - collections::{HashMap, HashSet}, - rc::Rc, -}; +use std::{collections::HashMap, rc::Rc}; use glam::Vec2; @@ -11,7 +8,7 @@ use crate::{ globals::WguiGlobals, i18n::Translation, layout::Layout, - parser::{self, Fetchable}, + parser::{self, Fetchable, ParserState}, task::Tasks, windowing::window::{WguiWindow, WguiWindowParams, WguiWindowParamsExtra}, }; @@ -22,10 +19,16 @@ pub struct Cell { pub attribs: Vec, } -pub struct OpenParams { +pub(crate) struct Blueprint { pub position: Vec2, pub cells: Vec, +} + +pub struct OpenParams { pub on_custom_attribs: Option, + pub template_name: Rc, + pub template_params: HashMap, Rc>, + pub position: Vec2, } #[derive(Clone)] @@ -40,11 +43,17 @@ pub struct ContextMenu { tasks: Tasks, } -fn doc_params<'a>(globals: &WguiGlobals) -> parser::ParseDocumentParams<'a> { +fn doc_params<'a>( + globals: &WguiGlobals, + on_custom_attribs: Option, +) -> parser::ParseDocumentParams<'a> { parser::ParseDocumentParams { globals: globals.clone(), path: AssetPath::WguiInternal("wgui/context_menu.xml"), - extra: Default::default(), + extra: parser::ParseDocumentExtra { + on_custom_attribs, + ..Default::default() + }, } } @@ -62,7 +71,15 @@ impl ContextMenu { self.window.close(); } - fn open_process(&mut self, params: &mut OpenParams, layout: &mut Layout) -> anyhow::Result<()> { + fn open_process( + &mut self, + params: &mut OpenParams, + layout: &mut Layout, + parser_state: &mut ParserState, + ) -> anyhow::Result<()> { + let blueprint = + parser_state.context_menu_create_blueprint(¶ms.template_name, ¶ms.template_params, params.position)?; + let globals = layout.state.globals.clone(); self.window.open(&mut WguiWindowParams { @@ -77,15 +94,16 @@ impl ContextMenu { })?; let content = self.window.get_content(); + let doc_params = doc_params(&globals, params.on_custom_attribs.clone()); - let mut state = parser::parse_from_assets(&doc_params(&globals), layout, content.id)?; + let mut state = parser::parse_from_assets(&doc_params, layout, content.id)?; let id_buttons = state.get_widget_id("buttons")?; - for (idx, cell) in params.cells.iter().enumerate() { + for (idx, cell) in blueprint.cells.iter().enumerate() { let mut par = HashMap::new(); par.insert(Rc::from("text"), cell.title.generate(&mut globals.i18n())); - let data_cell = state.parse_template(&doc_params(&globals), "Cell", layout, id_buttons, par)?; + let data_cell = state.parse_template(&doc_params, "Cell", layout, id_buttons, par)?; let button = data_cell.fetch_component_as::("button")?; let button_id = button.base().get_id(); @@ -102,23 +120,16 @@ impl ContextMenu { }); } - if idx < params.cells.len() - 1 { - state.parse_template( - &doc_params(&globals), - "Separator", - layout, - id_buttons, - Default::default(), - )?; + if idx < blueprint.cells.len() - 1 { + state.parse_template(&doc_params, "Separator", layout, id_buttons, Default::default())?; } } - Ok(()) } - pub fn tick(&mut self, layout: &mut Layout) -> anyhow::Result { + pub fn tick(&mut self, layout: &mut Layout, parser_state: &mut ParserState) -> anyhow::Result { if let Some(mut p) = self.pending_open.take() { - self.open_process(&mut p, layout)?; + self.open_process(&mut p, layout, parser_state)?; } let mut result = TickResult::default();