From 4efdeceffdca853d7533a966f2ca98d598eec571 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Tue, 21 Jul 2026 14:46:02 +0300 Subject: [PATCH] =?UTF-8?q?fix(ribbon):=20tooltips=20show=20again=20?= =?UTF-8?q?=E2=80=94=20forward=20child=20overlays?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ribbon tooltips died the day the panels moved into CollapsePanels (v0.7.6): its overlay() started with `self.open?`, so with no flyout open — the normal state — it returned None and swallowed every child overlay, and iced tooltips ARE overlays (#411). Buttons still hovered and clicked, which is why only the tips were missing. With no flyout the widget now collects the shown variants' overlays into a Group, borrows split per panel slot. Co-Authored-By: Claude Fable 5 --- src/ui/ribbon/collapse.rs | 70 ++++++++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/src/ui/ribbon/collapse.rs b/src/ui/ribbon/collapse.rs index 129678a1e..6f753c342 100644 --- a/src/ui/ribbon/collapse.rs +++ b/src/ui/ribbon/collapse.rs @@ -494,23 +494,63 @@ impl<'a> Widget for CollapsePanels<'a> { translation: Vector, ) -> Option> { let levels = self.levels_snapshot(self.panels.len()); - let open_id = self.open.clone()?; - let p = self.panels.iter().position(|pan| pan.id == open_id)?; - // Only a button-form panel (collapsed or tight) shows a flyout. - let lvl = levels.get(p).copied().unwrap_or(FULL); - if lvl != COLLAPSED && lvl != TIGHT { - return None; + + // An open flyout owns the overlay slot. + if let Some(open_id) = self.open.clone() { + if let Some(p) = self.panels.iter().position(|pan| pan.id == open_id) { + // Only a button-form panel (collapsed or tight) shows a flyout. + let lvl = levels.get(p).copied().unwrap_or(FULL); + if (lvl == COLLAPSED || lvl == TIGHT) && layout.children().nth(p).is_some() { + let child_layout = layout.children().nth(p).unwrap(); + let b = child_layout.bounds(); + let anchor = + Point::new(b.x + translation.x, b.y + b.height + translation.y); + return Some(overlay::Element::new(Box::new(FlyoutOverlay { + flyout: &mut self.panels[p].flyout, + tree: &mut tree.children[SLOTS * p + 4], + anchor, + }))); + } + } } - let child_layout = layout.children().nth(p)?; - let b = child_layout.bounds(); - let anchor = Point::new(b.x + translation.x, b.y + b.height + translation.y); - - Some(overlay::Element::new(Box::new(FlyoutOverlay { - flyout: &mut self.panels[p].flyout, - tree: &mut tree.children[SLOTS * p + 4], - anchor, - }))) + // No flyout: forward the SHOWN children's overlays. Every ribbon + // tooltip is an iced overlay produced inside these children, so + // returning None here left all of them permanently dead (#411 — a + // regression from the day this widget replaced the plain row). + // Split borrows so each shown child and its tree slot are disjoint. + let mut overlays = Vec::new(); + let mut tree_rest = tree.children.as_mut_slice(); + for ((i, panel), child_layout) in + self.panels.iter_mut().enumerate().zip(layout.children()) + { + if tree_rest.len() < SLOTS { + break; + } + let (chunk, rest) = tree_rest.split_at_mut(SLOTS); + tree_rest = rest; + let level = levels.get(i).copied().unwrap_or(FULL); + let child = match level { + FULL => &mut panel.full, + COMPACT => &mut panel.compact, + COLLAPSED => &mut panel.button, + _ => &mut panel.tight, + }; + if let Some(o) = child.as_widget_mut().overlay( + &mut chunk[level as usize], + child_layout, + _renderer, + _viewport, + translation, + ) { + overlays.push(o); + } + } + if overlays.is_empty() { + None + } else { + Some(overlay::Group::with_children(overlays).overlay()) + } } }