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}