diff --git a/wayvr/src/gui/panel/mod.rs b/wayvr/src/gui/panel/mod.rs index dc8d3a3b..88296dc6 100644 --- a/wayvr/src/gui/panel/mod.rs +++ b/wayvr/src/gui/panel/mod.rs @@ -206,7 +206,10 @@ impl GuiPanel { setup_custom_label::(&mut self.layout, &self.parser_state, elem, app); } else if let Ok(button) = self .parser_state - .fetch_component_from_widget_id_as::(&self.layout.state, elem.widget_id) + .fetch_component_from_widget_id_as::( + &self.layout.state, + elem.widget_id, + ) { setup_custom_button::( &mut self.layout, diff --git a/wayvr/src/overlays/edit/mod.rs b/wayvr/src/overlays/edit/mod.rs index 0d8ccd0c..7600fc95 100644 --- a/wayvr/src/overlays/edit/mod.rs +++ b/wayvr/src/overlays/edit/mod.rs @@ -372,8 +372,8 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result { let anim_mult = app.wgui_theme.animation_mult; let on_custom_attrib: OnCustomAttribFunc = Box::new(move |layout, parser, attribs, _app| { - let Ok(button) = - parser.fetch_component_from_widget_id_as::(&layout.state, attribs.widget_id) + let Ok(button) = parser + .fetch_component_from_widget_id_as::(&layout.state, attribs.widget_id) else { return; }; diff --git a/wayvr/src/overlays/keyboard/builder.rs b/wayvr/src/overlays/keyboard/builder.rs index 4436933b..269353da 100644 --- a/wayvr/src/overlays/keyboard/builder.rs +++ b/wayvr/src/overlays/keyboard/builder.rs @@ -341,7 +341,7 @@ fn find_children( .layout .collect_children_ids_recursive(widget_id, &mut children); - for (child, _) in children { + for child in children { if let Some(widget) = panel.layout.state.widgets.get_as::(child) { sprites.push(ChildWidget { id: child, diff --git a/wayvr/src/overlays/wayvr.rs b/wayvr/src/overlays/wayvr.rs index 92634c24..d53ef83c 100644 --- a/wayvr/src/overlays/wayvr.rs +++ b/wayvr/src/overlays/wayvr.rs @@ -204,9 +204,10 @@ impl WvrWindowBackend { let on_custom_attrib: OnCustomAttribFunc = Box::new(move |layout, parser, attribs, _app| { - let Ok(button) = - parser.fetch_component_from_widget_id_as::(&layout.state, attribs.widget_id) - else { + let Ok(button) = parser.fetch_component_from_widget_id_as::( + &layout.state, + attribs.widget_id, + ) else { return; }; diff --git a/wayvr/src/overlays/whisper.rs b/wayvr/src/overlays/whisper.rs index fec62f40..bfc9373d 100644 --- a/wayvr/src/overlays/whisper.rs +++ b/wayvr/src/overlays/whisper.rs @@ -95,8 +95,8 @@ pub fn create_whisper( let xml = "gui/whisper.xml"; let on_custom_attrib: OnCustomAttribFunc = Box::new(move |layout, parser, attribs, _app| { - let Ok(button) = - parser.fetch_component_from_widget_id_as::(&layout.state, attribs.widget_id) + let Ok(button) = parser + .fetch_component_from_widget_id_as::(&layout.state, attribs.widget_id) else { return; }; diff --git a/wgui/src/components/button.rs b/wgui/src/components/button.rs index fc728eb2..9d64ac86 100644 --- a/wgui/src/components/button.rs +++ b/wgui/src/components/button.rs @@ -143,7 +143,7 @@ impl ComponentTrait for ComponentButton { .layout .collect_children_ids_recursive(self.data.id_rect, &mut children); - for (child, _) in children { + for child in children { if let Some(mut widget) = data.layout.state.widgets.get_as::(child) { if !state.id_sprite.is_null() && state.id_sprite != child { log::error!("Button with more than one sprite!"); @@ -184,6 +184,13 @@ impl ComponentTrait for ComponentButton { } } } + + fn destroy(&self, data: &mut components::DestroyData) { + if let Some(comp) = self.state.borrow_mut().active_tooltip.take() { + comp.destroy(data); + data.destroy_widgets.push(comp.base().id); + } + } } fn get_color2(color: &WguiColor, gradient_intensity: f32) -> WguiColor { diff --git a/wgui/src/components/checkbox.rs b/wgui/src/components/checkbox.rs index f85d3496..6d95a038 100644 --- a/wgui/src/components/checkbox.rs +++ b/wgui/src/components/checkbox.rs @@ -11,7 +11,7 @@ use crate::{ animation::{Animation, AnimationEasing}, color::{WguiColor, WguiColorName}, components::{ - Component, ComponentBase, ComponentTrait, RefreshData, + Component, ComponentBase, ComponentTrait, DestroyData, RefreshData, radio_group::ComponentRadioGroup, tooltip::{self, ComponentTooltip, TooltipTrait}, }, @@ -111,6 +111,13 @@ impl ComponentTrait for ComponentCheckbox { fn refresh(&self, _data: &mut RefreshData) { // nothing to do } + + fn destroy(&self, data: &mut DestroyData) { + if let Some(comp) = self.state.borrow_mut().active_tooltip.take() { + comp.destroy(data); + data.destroy_widgets.push(comp.base().id); + } + } } fn set_box_checked(widgets: &layout::WidgetMap, data: &Data, checked: bool, hovered: bool) { diff --git a/wgui/src/components/mod.rs b/wgui/src/components/mod.rs index bc00f366..cbe7a03a 100644 --- a/wgui/src/components/mod.rs +++ b/wgui/src/components/mod.rs @@ -25,6 +25,11 @@ pub struct RefreshData<'a> { pub layout: &'a mut Layout, } +pub struct DestroyData<'a> { + pub layout: &'a mut Layout, + pub destroy_widgets: &'a mut Vec, +} + pub struct FocusChangeData<'a> { pub common: &'a mut CallbackDataCommon<'a>, pub focused: bool, @@ -49,6 +54,7 @@ pub trait ComponentTrait: AnyTrait { fn base_mut(&mut self) -> &mut ComponentBase; fn refresh(&self, data: &mut RefreshData); fn on_focus_change(&self, _data: &mut FocusChangeData) {} + fn destroy(&self, _data: &mut DestroyData) {} } #[derive(Clone)] diff --git a/wgui/src/components/slider.rs b/wgui/src/components/slider.rs index 4ed30a38..e3f61848 100644 --- a/wgui/src/components/slider.rs +++ b/wgui/src/components/slider.rs @@ -7,7 +7,7 @@ use crate::{ animation::{Animation, AnimationCallback, AnimationEasing}, color::{WguiColor, WguiColorName}, components::{ - Component, ComponentBase, ComponentTrait, RefreshData, + Component, ComponentBase, ComponentTrait, DestroyData, RefreshData, tooltip::{self, ComponentTooltip, TooltipTrait}, }, event::{ @@ -160,6 +160,13 @@ impl ComponentTrait for ComponentSlider { fn base_mut(&mut self) -> &mut ComponentBase { &mut self.base } + + fn destroy(&self, data: &mut DestroyData) { + if let Some(comp) = self.state.borrow_mut().active_tooltip.take() { + comp.destroy(data); + data.destroy_widgets.push(comp.base().id); + } + } } impl ComponentSlider { diff --git a/wgui/src/components/tabs.rs b/wgui/src/components/tabs.rs index da26cfb0..05561ab7 100644 --- a/wgui/src/components/tabs.rs +++ b/wgui/src/components/tabs.rs @@ -2,7 +2,7 @@ use crate::{ assets::AssetPathRc, color::WguiColor, components::{ - Component, ComponentBase, ComponentTrait, RefreshData, + Component, ComponentBase, ComponentTrait, DestroyData, RefreshData, button::{self, ComponentButton}, }, event::CallbackDataCommon, @@ -71,6 +71,13 @@ impl ComponentTrait for ComponentTabs { fn refresh(&self, _data: &mut RefreshData) { // nothing to do } + + fn destroy(&self, data: &mut DestroyData) { + for e in self.state.borrow_mut().mounted_entries.drain(..) { + e.button.destroy(data); + data.destroy_widgets.push(e.button.base().id); + } + } } impl State { diff --git a/wgui/src/layout.rs b/wgui/src/layout.rs index 23a57f0b..e408bded 100644 --- a/wgui/src/layout.rs +++ b/wgui/src/layout.rs @@ -7,7 +7,7 @@ use std::{ use crate::{ animation::Animations, - components::{self, Component, ComponentWeak, FocusChangeData, RefreshData}, + components::{self, Component, ComponentWeak, DestroyData, FocusChangeData, RefreshData}, drawing::{ self, ANSI_BOLD_CODE, ANSI_RESET_CODE, Boundary, PushScissorStackResult, push_scissor_stack, push_transform_stack, }, @@ -283,21 +283,35 @@ impl Layout { }) } - pub fn collect_children_ids_recursive(&self, widget_id: WidgetID, out: &mut Vec<(WidgetID, taffy::NodeId)>) { + pub fn collect_children_ids_recursive(&self, widget_id: WidgetID, out: &mut Vec) { let Some(node_id) = self.state.nodes.get(widget_id) else { return; }; for child_id in self.state.tree.child_ids(*node_id) { let child_widget_id = self.state.tree.get_node_context(child_id).unwrap(); - out.push((*child_widget_id, child_id)); + out.push(*child_widget_id); self.collect_children_ids_recursive(*child_widget_id, out); } } - fn remove_widget_single(&mut self, widget_id: WidgetID, node_id: Option) { + fn remove_widget_single(&mut self, widget_id: WidgetID) { self.state.widgets.remove_single(widget_id); - self.state.nodes.remove(widget_id); + let node_id = self.state.nodes.remove(widget_id); + + if let Some(component_weak) = self.state.components_by_widget_id.remove(widget_id) + && let Some(component) = component_weak.upgrade() + { + let mut destroy_widgets = vec![]; + component.destroy(&mut DestroyData { + layout: self, + destroy_widgets: &mut destroy_widgets, + }); + for wid in destroy_widgets { + self.remove_widget_single(wid); + } + } + if let Some(node_id) = node_id { self.registered_components_to_refresh.remove(&node_id); let _ = self.state.tree.remove(node_id); @@ -313,16 +327,15 @@ impl Layout { self.mark_redraw(); } - for (widget_id, node_id) in ids { - self.remove_widget_single(widget_id, Some(node_id)); + for widget_id in ids { + self.remove_widget_single(widget_id); } } // remove widget and its children, recursively pub fn remove_widget(&mut self, widget_id: WidgetID) { self.remove_children(widget_id); - let node_id = self.state.nodes.get(widget_id); - self.remove_widget_single(widget_id, node_id.copied()); + self.remove_widget_single(widget_id); self.mark_redraw(); } diff --git a/wgui/src/parser/mod.rs b/wgui/src/parser/mod.rs index 026e52cc..42067933 100644 --- a/wgui/src/parser/mod.rs +++ b/wgui/src/parser/mod.rs @@ -97,7 +97,11 @@ pub trait Fetchable { fn fetch_component_as(&self, id: &str) -> anyhow::Result>; /// Fetch a component by widget ID and down‑cast it to a concrete component type `T` (see `components/mod.rs`) - fn fetch_component_from_widget_id_as(&self, state: &LayoutState, widget_id: WidgetID) -> anyhow::Result>; + fn fetch_component_from_widget_id_as( + &self, + state: &LayoutState, + widget_id: WidgetID, + ) -> anyhow::Result>; /// Return a widget by its string ID fn get_widget_id(&self, id: &str) -> anyhow::Result; @@ -168,7 +172,11 @@ impl Fetchable for ParserData { state.fetch_component_by_widget_id(widget_id) } - fn fetch_component_from_widget_id_as(&self, state: &LayoutState, widget_id: WidgetID) -> anyhow::Result> { + fn fetch_component_from_widget_id_as( + &self, + state: &LayoutState, + widget_id: WidgetID, + ) -> anyhow::Result> { state.fetch_component_from_widget_id_as(widget_id) } @@ -381,7 +389,11 @@ impl Fetchable for ParserState { self.data.fetch_component_by_widget_id(state, widget_id) } - fn fetch_component_from_widget_id_as(&self, state: &LayoutState, widget_id: WidgetID) -> anyhow::Result> { + fn fetch_component_from_widget_id_as( + &self, + state: &LayoutState, + widget_id: WidgetID, + ) -> anyhow::Result> { self.data.fetch_component_from_widget_id_as(state, widget_id) }