feat(ribbon): wrap compact panels onto extra rows when still too narrow

DensitySwap now takes a third, narrowest variant: the compact panels
flex-wrapped (via WrapFlow) across as many rows as the width needs. The
tool area therefore degrades full -> compact -> wrapped as the window
shrinks. build_tool_row becomes build_tool_groups, returning fixed-height
panel elements so they lay out identically in a row or a wrap flow.

The tool bar is no longer a fixed TOOL_BAR_H; DensitySwap reports its
measured height into a cell and the ribbon dropdown overlays anchor at
tab_bar_height() + tool_bar_height() so they track the taller bar.

Second stage of the adaptive tool area (shrink -> wrap -> collapse).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-05 16:51:26 +03:00
1 parent 65dfd4e462
commit ff36a1bdac
2 files changed
+79 -55

No files matched your search

+15 -3
View File
@@ -621,6 +621,7 @@ impl<'a> From<WrapFlow<'a>> for Element<'a, Message> {
pub struct DensitySwap<'a> {
variants: Vec<Element<'a, Message>>,
chosen: Cell<usize>,
height_out: Option<Arc<AtomicU32>>,
}
impl<'a> DensitySwap<'a> {
@@ -628,8 +629,16 @@ impl<'a> DensitySwap<'a> {
Self {
variants,
chosen: Cell::new(0),
height_out: None,
}
}
/// Report the chosen variant's height (bits of an `f32`) so callers can
/// anchor overlays below a possibly-taller (wrapped) tool area.
pub fn report_height(mut self, out: Arc<AtomicU32>) -> Self {
self.height_out = Some(out);
self
}
}
impl<'a> Widget<Message, Theme, Renderer> for DensitySwap<'a> {
@@ -643,7 +652,7 @@ impl<'a> Widget<Message, Theme, Renderer> for DensitySwap<'a> {
}
fn size(&self) -> Size<Length> {
Size::new(Length::Shrink, Length::Fill)
Size::new(Length::Shrink, Length::Shrink)
}
fn layout(
@@ -654,10 +663,10 @@ impl<'a> Widget<Message, Theme, Renderer> for DensitySwap<'a> {
) -> layout::Node {
let max_w = limits.max().width;
let natural =
layout::Limits::new(Size::ZERO, Size::new(f32::INFINITY, limits.max().height));
layout::Limits::new(Size::ZERO, Size::new(f32::INFINITY, f32::INFINITY));
// Widest-first: keep the first variant whose natural width fits; else the
// last (narrowest).
// last (which is expected to wrap to fit any width).
let mut pick = self.variants.len().saturating_sub(1);
for (i, v) in self.variants.iter_mut().enumerate() {
let n = v.as_widget_mut().layout(&mut tree.children[i], renderer, &natural);
@@ -673,6 +682,9 @@ impl<'a> Widget<Message, Theme, Renderer> for DensitySwap<'a> {
.as_widget_mut()
.layout(&mut tree.children[pick], renderer, limits);
let sz = node.size();
if let Some(out) = &self.height_out {
out.store(sz.height.to_bits(), Ordering::Relaxed);
}
layout::Node::with_children(sz, vec![node])
}