fix(ribbon): tooltips show again — forward child overlays

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 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-07-21 14:46:02 +03:00
commit 4efdeceffd

View file

@ -494,23 +494,63 @@ impl<'a> Widget<Message, Theme, Renderer> for CollapsePanels<'a> {
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
let levels = self.levels_snapshot(self.panels.len()); 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)?; // An open flyout owns the overlay slot.
// Only a button-form panel (collapsed or tight) shows a flyout. if let Some(open_id) = self.open.clone() {
let lvl = levels.get(p).copied().unwrap_or(FULL); if let Some(p) = self.panels.iter().position(|pan| pan.id == open_id) {
if lvl != COLLAPSED && lvl != TIGHT { // Only a button-form panel (collapsed or tight) shows a flyout.
return None; 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)?; // No flyout: forward the SHOWN children's overlays. Every ribbon
let b = child_layout.bounds(); // tooltip is an iced overlay produced inside these children, so
let anchor = Point::new(b.x + translation.x, b.y + b.height + translation.y); // returning None here left all of them permanently dead (#411 — a
// regression from the day this widget replaced the plain row).
Some(overlay::Element::new(Box::new(FlyoutOverlay { // Split borrows so each shown child and its tree slot are disjoint.
flyout: &mut self.panels[p].flyout, let mut overlays = Vec::new();
tree: &mut tree.children[SLOTS * p + 4], let mut tree_rest = tree.children.as_mut_slice();
anchor, 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())
}
} }
} }