winio_layout/
stack_panel.rs

1use taffy::{
2    NodeId, Style, TaffyTree,
3    prelude::{auto, length, percent},
4};
5use winio_primitive::{Failable, HAlign, Orient, Point, Rect, Size, VAlign};
6
7use crate::{LayoutChild, LayoutError, layout_child, rect_t2e, render};
8
9layout_child! {
10    /// Builder of a child for [`StackPanel`].
11    struct StackPanelChild {
12        /// Whether to fill the remain space of the container.
13        grow: bool = false,
14    }
15}
16
17/// A stacked layout container.
18pub struct StackPanel<'a, E> {
19    children: Vec<StackPanelChild<'a, E>>,
20    orient: Orient,
21    loc: Point,
22    size: Size,
23}
24
25impl<'a, E> StackPanel<'a, E> {
26    /// Create [`StackPanel`] with orientation.
27    pub fn new(orient: Orient) -> Self {
28        Self {
29            children: vec![],
30            orient,
31            loc: Point::zero(),
32            size: Size::zero(),
33        }
34    }
35
36    /// Push a child into the panel.
37    pub fn push<'b>(
38        &'b mut self,
39        widget: &'a mut dyn LayoutChild<Error = E>,
40    ) -> StackPanelChildBuilder<'a, 'b, E> {
41        StackPanelChildBuilder {
42            child: StackPanelChild::new(widget),
43            children: &mut self.children,
44        }
45    }
46
47    fn tree(&self) -> Result<(TaffyTree, NodeId, Vec<NodeId>), LayoutError<E>> {
48        let mut tree: TaffyTree<()> = TaffyTree::new();
49        let mut nodes = vec![];
50        for child in &self.children {
51            let preferred_size = child.child_preferred_size()?;
52            let mut style = Style::default();
53            style.size.width = match child.width {
54                Some(w) => length(w as f32),
55                None => match (self.orient, child.halign, child.grow) {
56                    (Orient::Vertical, HAlign::Stretch, _) => percent(1.0),
57                    (Orient::Horizontal, _, true) => auto(),
58                    _ => length(preferred_size.width as f32),
59                },
60            };
61            style.size.height = match child.height {
62                Some(h) => length(h as f32),
63                None => match (self.orient, child.valign, child.grow) {
64                    (Orient::Horizontal, VAlign::Stretch, _) => percent(1.0),
65                    (Orient::Vertical, _, true) => auto(),
66                    _ => length(preferred_size.height as f32),
67                },
68            };
69            let min_size = child.child_min_size()?;
70            style.min_size = taffy::Size {
71                width: length(min_size.width as f32),
72                height: length(min_size.height as f32),
73            };
74            match self.orient {
75                Orient::Horizontal => {
76                    if matches!(child.valign, VAlign::Top | VAlign::Center) {
77                        style.margin.bottom = auto();
78                    }
79                    if matches!(child.valign, VAlign::Bottom | VAlign::Center) {
80                        style.margin.top = auto();
81                    }
82                }
83                Orient::Vertical => {
84                    if matches!(child.halign, HAlign::Left | HAlign::Center) {
85                        style.margin.right = auto();
86                    }
87                    if matches!(child.halign, HAlign::Right | HAlign::Center) {
88                        style.margin.left = auto();
89                    }
90                }
91            }
92            if child.grow {
93                style.flex_grow = 1.0
94            }
95            let node = tree.new_leaf(style)?;
96            nodes.push(node);
97        }
98        let root = tree.new_with_children(
99            Style {
100                size: taffy::Size::from_percent(1.0, 1.0),
101                flex_direction: match self.orient {
102                    Orient::Horizontal => taffy::FlexDirection::Row,
103                    Orient::Vertical => taffy::FlexDirection::Column,
104                },
105                ..Default::default()
106            },
107            &nodes,
108        )?;
109        Ok((tree, root, nodes))
110    }
111
112    fn render(&mut self) -> Result<(), LayoutError<E>> {
113        let (tree, root, nodes) = self.tree()?;
114        render(tree, root, nodes, self.loc, self.size, &mut self.children)
115    }
116
117    /// Move the location.
118    pub fn set_loc(&mut self, p: Point) -> Result<(), LayoutError<E>> {
119        LayoutChild::set_child_loc(self, p)
120    }
121
122    /// Resize.
123    pub fn set_size(&mut self, s: Size) -> Result<(), LayoutError<E>> {
124        LayoutChild::set_child_size(self, s)
125    }
126
127    /// Set the location and size.
128    pub fn set_rect(&mut self, r: Rect) -> Result<(), LayoutError<E>> {
129        LayoutChild::set_child_rect(self, r)
130    }
131}
132
133impl<E> Failable for StackPanel<'_, E> {
134    type Error = E;
135}
136
137impl<E> LayoutChild for StackPanel<'_, E> {
138    fn set_child_loc(&mut self, p: Point) -> Result<(), LayoutError<Self::Error>> {
139        self.loc = p;
140        self.render()
141    }
142
143    fn set_child_size(&mut self, s: Size) -> Result<(), LayoutError<Self::Error>> {
144        self.size = s;
145        self.render()
146    }
147
148    fn set_child_rect(&mut self, r: Rect) -> Result<(), LayoutError<Self::Error>> {
149        self.loc = r.origin;
150        self.size = r.size;
151        self.render()
152    }
153
154    fn child_preferred_size(&self) -> Result<Size, LayoutError<Self::Error>> {
155        let (mut tree, root, _) = self.tree()?;
156        tree.compute_layout(root, taffy::Size::max_content())?;
157        Ok(rect_t2e(tree.layout(root)?).size)
158    }
159
160    fn child_min_size(&self) -> Result<Size, LayoutError<Self::Error>> {
161        let (mut tree, root, _) = self.tree()?;
162        tree.compute_layout(root, taffy::Size::min_content())?;
163        Ok(rect_t2e(tree.layout(root)?).size)
164    }
165}