layout/stack: opt-in Stack::fit_content() so a wrapping container can adopt the stack's intrinsic size
`Stack::preferred_size` unconditionally reported `(max_width, max_h)` — every stack claimed the full width its parent offered, regardless of what its children actually needed. That is the right default for a FrameLayout-style overlay (the existing callers all rely on it) but it makes the natural "pin a stack to a fixed-size child (e.g. a spacer of `card_w × card_h`) and centre other children on top of it" pattern impossible: a container wrapping such a stack always inherited the full parent width and ignored the spacer's footprint. The new `Stack::fit_content()` builder mirrors the `Column::fit_content` flag — when set, `preferred_size` returns the max of children's intrinsic widths and heights instead of claiming the parent's `max_width`, with the same "skip filler widgets that themselves claim `max_width`" exclusion list (`Spacer` with no `fixed_width`, `Separator`, `Scroll`, `ProgressBar`, `Slider`, `TextEdit` without `fixed_width`) so the flag is not defeated by a flexible child slipping into the stack. Default behaviour is unchanged.
This commit is contained in:
@@ -54,6 +54,7 @@ pub struct Stack<Msg: Clone>
|
||||
/// Children with their alignment, margin, and extra `(x, y)` translation
|
||||
/// applied after alignment. Drawn in order — last child is on top.
|
||||
pub children: Vec<( Element<Msg>, HAlign, VAlign, f32, f32, f32 )>,
|
||||
pub fit_content: bool,
|
||||
}
|
||||
|
||||
impl<Msg: Clone> Stack<Msg>
|
||||
@@ -61,7 +62,13 @@ impl<Msg: Clone> Stack<Msg>
|
||||
/// Create an empty stack.
|
||||
pub fn new() -> Self
|
||||
{
|
||||
Self { children: Vec::new() }
|
||||
Self { children: Vec::new(), fit_content: false }
|
||||
}
|
||||
|
||||
pub fn fit_content( mut self ) -> Self
|
||||
{
|
||||
self.fit_content = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Append a child that fills the entire Stack rect (Android FrameLayout default).
|
||||
@@ -111,9 +118,31 @@ impl<Msg: Clone> Stack<Msg>
|
||||
self
|
||||
}
|
||||
|
||||
/// Return the preferred `(width, height)` — the maximum height among children.
|
||||
pub fn preferred_size( &self, max_width: f32, canvas: &Canvas ) -> (f32, f32)
|
||||
{
|
||||
if self.fit_content
|
||||
{
|
||||
let content_w = self.children.iter()
|
||||
.map( |( c, _, _, _, _, _ )| match c
|
||||
{
|
||||
Element::Spacer( s ) => s.fixed_width.unwrap_or( 0.0 ),
|
||||
Element::Separator( _ ) => 0.0,
|
||||
Element::Scroll( _ ) => 0.0,
|
||||
Element::ProgressBar( _ ) => 0.0,
|
||||
Element::Slider( _ ) => 0.0,
|
||||
Element::TextEdit( t ) => if t.fixed_width.is_some()
|
||||
{
|
||||
t.preferred_size( max_width, canvas ).0
|
||||
} else { 0.0 },
|
||||
other => other.preferred_size( max_width, canvas ).0,
|
||||
} )
|
||||
.fold( 0.0_f32, f32::max );
|
||||
let max_h = self.children.iter()
|
||||
.map( |( c, _, _, _, _, _ )| c.preferred_size( max_width, canvas ).1 )
|
||||
.fold( 0.0_f32, f32::max );
|
||||
return ( content_w.min( max_width ), max_h );
|
||||
}
|
||||
|
||||
let max_h = self.children.iter()
|
||||
.map( |( c, _, _, _, _, _ )| c.preferred_size( max_width, canvas ).1 )
|
||||
.fold( 0.0_f32, f32::max );
|
||||
@@ -163,6 +192,7 @@ impl<Msg: Clone> Stack<Msg>
|
||||
.map( |( child, ha, va, margin, ox, oy )|
|
||||
( child.map_arc( f ), ha, va, margin, ox, oy ) )
|
||||
.collect(),
|
||||
fit_content: self.fit_content,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user