diff --git a/crates/egui/src/containers/popup.rs b/crates/egui/src/containers/popup.rs index 105f4a5b32e..ab01aa6ab30 100644 --- a/crates/egui/src/containers/popup.rs +++ b/crates/egui/src/containers/popup.rs @@ -124,7 +124,12 @@ fn show_tooltip_at_dyn<'c, R>( } // if there are multiple tooltips open they should use the same common_id for the `tooltip_size` caching to work. - let mut state = ctx.frame_state(|fs| { + let mut state = ctx.frame_state_mut(|fs| { + // Remember that this is the widget showing the tooltip: + fs.tooltip_state + .per_layer_tooltip_widget + .insert(parent_layer, widget_id); + fs.tooltip_state .widget_tooltips .get(&widget_id) diff --git a/crates/egui/src/frame_state.rs b/crates/egui/src/frame_state.rs index 4e1aa390017..4f434a60ebc 100644 --- a/crates/egui/src/frame_state.rs +++ b/crates/egui/src/frame_state.rs @@ -1,13 +1,27 @@ use crate::{id::IdSet, *}; +/// Reset at the start of each frame. #[derive(Clone, Debug, Default)] pub struct TooltipFrameState { + /// If a tooltip has been shown this frame, where was it? + /// This is used to prevent multiple tooltips to cover each other. pub widget_tooltips: IdMap, + + /// For each layer, which widget is showing a tooltip (if any)? + /// + /// Only one widget per layer may show a tooltip. + /// But if a tooltip contains a tooltip, you can show a tooltip on top of a tooltip. + pub per_layer_tooltip_widget: ahash::HashMap, } impl TooltipFrameState { pub fn clear(&mut self) { - self.widget_tooltips.clear(); + let Self { + widget_tooltips, + per_layer_tooltip_widget, + } = self; + widget_tooltips.clear(); + per_layer_tooltip_widget.clear(); } } @@ -51,9 +65,6 @@ pub struct FrameState { /// How much space is used by panels. pub used_by_panels: Rect, - /// If a tooltip has been shown this frame, where was it? - /// This is used to prevent multiple tooltips to cover each other. - /// Reset at the start of each frame. pub tooltip_state: TooltipFrameState, /// The current scroll area should scroll to this range (horizontal, vertical). diff --git a/crates/egui/src/response.rs b/crates/egui/src/response.rs index e2d898dae1a..f0f10b9d4c1 100644 --- a/crates/egui/src/response.rs +++ b/crates/egui/src/response.rs @@ -645,6 +645,22 @@ impl Response { } } + let is_other_tooltip_open = self.ctx.prev_frame_state(|fs| { + if let Some(already_open_tooltip) = fs + .tooltip_state + .per_layer_tooltip_widget + .get(&self.layer_id) + { + already_open_tooltip != &self.id + } else { + false + } + }); + if is_other_tooltip_open { + // We only allow one tooltip per layer. First one wins. It is up to that tooltip to close itself. + return false; + } + // Fast early-outs: if self.enabled { if !self.hovered || !self.ctx.input(|i| i.pointer.has_pointer()) {