ribir_widgets/layout/stack.rs
1use ribir_core::prelude::*;
2
3/// A widget that overlap children align with left top.
4#[derive(MultiChild, Query, Declare)]
5pub struct Stack {
6 #[declare(default)]
7 fit: StackFit,
8}
9
10/// How to size the non-positioned children of a [Stack]. (same as flutter)
11#[derive(Default)]
12pub enum StackFit {
13 /// The constraints passed to the stack from its parent are loosened.
14 ///
15 /// For example, if the stack has constraints that force it to 350x600, then
16 /// this would allow the non-positioned children of the stack to have any
17 /// width from zero to 350 and any height from zero to 600.
18 ///
19 /// See also:
20 ///
21 /// * [Center], which loosens the constraints passed to its child and then
22 /// centers the child in itself.
23 /// * [BoxConstraints.loosen], which implements the loosening of box
24 /// constraints.
25 #[default]
26 Loose,
27
28 /// The constraints passed to the stack from its parent are tightened to the
29 /// biggest size allowed.
30 ///
31 /// For example, if the stack has loose constraints with a width in the range
32 /// 10 to 100 and a height in the range 0 to 600, then the non-positioned
33 /// children of the stack would all be sized as 100 pixels wide and 600 high.
34 Expand,
35
36 /// The constraints passed to the stack from its parent are passed unmodified
37 /// to the non-positioned children.
38 ///
39 /// For example, if a [Stack] is an [Expanded] child of a [Row], the
40 /// horizontal constraints will be tight and the vertical constraints will be
41 /// loose.
42 Passthrough,
43}
44
45impl Render for Stack {
46 fn perform_layout(&self, clamp: BoxClamp, ctx: &mut LayoutCtx) -> Size {
47 let clamp = match self.fit {
48 StackFit::Loose => clamp.loose(),
49 StackFit::Expand => BoxClamp { min: clamp.max, max: clamp.max },
50 StackFit::Passthrough => clamp,
51 };
52
53 let mut size = ZERO_SIZE;
54 let mut layouter = ctx.first_child_layouter();
55 while let Some(mut l) = layouter {
56 let child_size = l.perform_widget_layout(clamp);
57 size = size.max(child_size);
58 layouter = l.into_next_sibling();
59 }
60 size
61 }
62
63 fn paint(&self, _: &mut PaintingCtx) {
64 // nothing to paint.
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use ribir_core::test_helper::*;
71 use ribir_dev_helper::*;
72
73 use super::*;
74 use crate::prelude::*;
75 const FIVE: Size = Size::new(5., 5.);
76 fn smoke() -> impl WidgetBuilder {
77 let one = Size::new(1., 1.);
78 let five = Size::new(5., 5.);
79 fn_widget! {
80 @Stack {
81 @SizedBox { size: one}
82 @SizedBox { size: five}
83 }
84 }
85 }
86
87 widget_layout_test!(smoke, size == FIVE,);
88}