fix tooltips staying after parent is gone

This commit is contained in:
galister 2026-07-13 22:03:26 +09:00
parent acaabead52
commit c74c51f7cd
12 changed files with 88 additions and 25 deletions

View File

@ -206,7 +206,10 @@ impl<S: 'static> GuiPanel<S> {
setup_custom_label::<S>(&mut self.layout, &self.parser_state, elem, app);
} else if let Ok(button) = self
.parser_state
.fetch_component_from_widget_id_as::<ComponentButton>(&self.layout.state, elem.widget_id)
.fetch_component_from_widget_id_as::<ComponentButton>(
&self.layout.state,
elem.widget_id,
)
{
setup_custom_button::<S>(
&mut self.layout,

View File

@ -372,8 +372,8 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
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::<ComponentButton>(&layout.state, attribs.widget_id)
let Ok(button) = parser
.fetch_component_from_widget_id_as::<ComponentButton>(&layout.state, attribs.widget_id)
else {
return;
};

View File

@ -341,7 +341,7 @@ fn find_children<S>(
.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::<WidgetSprite>(child) {
sprites.push(ChildWidget {
id: child,

View File

@ -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::<ComponentButton>(&layout.state, attribs.widget_id)
else {
let Ok(button) = parser.fetch_component_from_widget_id_as::<ComponentButton>(
&layout.state,
attribs.widget_id,
) else {
return;
};

View File

@ -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::<ComponentButton>(&layout.state, attribs.widget_id)
let Ok(button) = parser
.fetch_component_from_widget_id_as::<ComponentButton>(&layout.state, attribs.widget_id)
else {
return;
};

View File

@ -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::<WidgetSprite>(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 {

View File

@ -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) {

View File

@ -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<WidgetID>,
}
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)]

View File

@ -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 {

View File

@ -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 {

View File

@ -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<WidgetID>) {
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<taffy::NodeId>) {
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();
}

View File

@ -97,7 +97,11 @@ pub trait Fetchable {
fn fetch_component_as<T: 'static>(&self, id: &str) -> anyhow::Result<Rc<T>>;
/// Fetch a component by widget ID and downcast it to a concrete component type `T` (see `components/mod.rs`)
fn fetch_component_from_widget_id_as<T: 'static>(&self, state: &LayoutState, widget_id: WidgetID) -> anyhow::Result<Rc<T>>;
fn fetch_component_from_widget_id_as<T: 'static>(
&self,
state: &LayoutState,
widget_id: WidgetID,
) -> anyhow::Result<Rc<T>>;
/// Return a widget by its string ID
fn get_widget_id(&self, id: &str) -> anyhow::Result<WidgetID>;
@ -168,7 +172,11 @@ impl Fetchable for ParserData {
state.fetch_component_by_widget_id(widget_id)
}
fn fetch_component_from_widget_id_as<T: 'static>(&self, state: &LayoutState, widget_id: WidgetID) -> anyhow::Result<Rc<T>> {
fn fetch_component_from_widget_id_as<T: 'static>(
&self,
state: &LayoutState,
widget_id: WidgetID,
) -> anyhow::Result<Rc<T>> {
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<T: 'static>(&self, state: &LayoutState, widget_id: WidgetID) -> anyhow::Result<Rc<T>> {
fn fetch_component_from_widget_id_as<T: 'static>(
&self,
state: &LayoutState,
widget_id: WidgetID,
) -> anyhow::Result<Rc<T>> {
self.data.fetch_component_from_widget_id_as(state, widget_id)
}