winio_layout/
grid.rs

1use std::{fmt::Display, num::ParseFloatError, str::FromStr};
2
3use taffy::{
4    GridTemplateComponent, NodeId, Style, TaffyTree, TrackSizingFunction,
5    prelude::{auto, fr, length, line, span},
6};
7use winio_primitive::Failable;
8
9use super::{layout_child, rect_t2e, render};
10use crate::{HAlign, LayoutChild, LayoutError, Point, Rect, Size, VAlign};
11
12/// Error can be returned when parsing [`GridLength`].
13#[derive(Debug)]
14#[non_exhaustive]
15pub enum ParseGridLengthError {
16    /// Invalid length value.
17    InvalidLength(ParseFloatError),
18}
19
20impl Display for ParseGridLengthError {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        match self {
23            Self::InvalidLength(e) => write!(f, "invalid length value: {e}"),
24        }
25    }
26}
27
28impl std::error::Error for ParseGridLengthError {}
29
30/// The width or height of a grid cell.
31#[derive(Debug, Clone, Copy, PartialEq)]
32pub enum GridLength {
33    /// The length is determined automatically.
34    Auto,
35    /// Represents a relative ratio.
36    Stretch(f64),
37    /// Fixed length.
38    Length(f64),
39}
40
41impl FromStr for GridLength {
42    type Err = ParseGridLengthError;
43
44    fn from_str(s: &str) -> Result<Self, Self::Err> {
45        if s.eq_ignore_ascii_case("auto") {
46            Ok(Self::Auto)
47        } else if let Some(s) = s.strip_suffix('*') {
48            s.parse::<f64>()
49                .map(Self::Stretch)
50                .map_err(ParseGridLengthError::InvalidLength)
51        } else {
52            s.parse::<f64>()
53                .map(Self::Length)
54                .map_err(ParseGridLengthError::InvalidLength)
55        }
56    }
57}
58
59impl From<GridLength> for TrackSizingFunction {
60    fn from(value: GridLength) -> Self {
61        match value {
62            GridLength::Auto => auto(),
63            GridLength::Length(v) => length(v as f32),
64            GridLength::Stretch(v) => fr(v as f32),
65        }
66    }
67}
68
69impl From<&GridLength> for TrackSizingFunction {
70    fn from(value: &GridLength) -> Self {
71        TrackSizingFunction::from(*value)
72    }
73}
74
75layout_child! {
76    /// Builder of a child for [`Grid`].
77    struct GridChild {
78        /// The column index in the grid.
79        column: usize = 0,
80        /// The row index in the grid.
81        row: usize = 0,
82        /// The column span in the grid.
83        column_span: usize = 1,
84        /// The row span in the grid.
85        row_span: usize = 1,
86    }
87}
88
89/// A grid layout container.
90pub struct Grid<'a, E> {
91    children: Vec<GridChild<'a, E>>,
92    columns: Vec<GridLength>,
93    rows: Vec<GridLength>,
94    loc: Point,
95    size: Size,
96}
97
98impl<'a, E> Grid<'a, E> {
99    /// Create [`Grid`].
100    pub fn new(columns: Vec<GridLength>, rows: Vec<GridLength>) -> Self {
101        Self {
102            children: vec![],
103            columns,
104            rows,
105            loc: Point::zero(),
106            size: Size::zero(),
107        }
108    }
109
110    /// Create [`Grid`] with string-representative of grid lengths.
111    pub fn from_str(
112        columns: impl AsRef<str>,
113        rows: impl AsRef<str>,
114    ) -> Result<Self, ParseGridLengthError> {
115        Ok(Self::new(
116            Self::parse_grid_lengths(columns.as_ref())?,
117            Self::parse_grid_lengths(rows.as_ref())?,
118        ))
119    }
120
121    fn parse_grid_lengths(s: &str) -> Result<Vec<GridLength>, ParseGridLengthError> {
122        let mut lengths = vec![];
123        for s in s.split(',') {
124            let s = s.trim();
125            lengths.push(s.parse()?);
126        }
127        Ok(lengths)
128    }
129
130    /// Push a child into the panel.
131    pub fn push<'b>(
132        &'b mut self,
133        widget: &'a mut dyn LayoutChild<Error = E>,
134    ) -> GridChildBuilder<'a, 'b, E> {
135        GridChildBuilder {
136            child: GridChild::new(widget),
137            children: &mut self.children,
138        }
139    }
140
141    fn tree(&self) -> Result<(TaffyTree, NodeId, Vec<NodeId>), LayoutError<E>> {
142        let mut tree: TaffyTree<()> = TaffyTree::new();
143        let mut nodes = vec![];
144        for child in &self.children {
145            let preferred_size = child.child_preferred_size()?;
146            let mut style = Style::default();
147            style.size.width = match child.width {
148                Some(w) => length(w as f32),
149                None => match child.halign {
150                    HAlign::Stretch => auto(),
151                    _ => length(preferred_size.width as f32),
152                },
153            };
154            style.size.height = match child.height {
155                Some(h) => length(h as f32),
156                None => match child.valign {
157                    VAlign::Stretch => auto(),
158                    _ => length(preferred_size.height as f32),
159                },
160            };
161            let min_size = child.child_min_size()?;
162            style.min_size = taffy::Size {
163                width: length(min_size.width as f32),
164                height: length(min_size.height as f32),
165            };
166            if matches!(child.valign, VAlign::Top | VAlign::Center) {
167                style.margin.bottom = auto();
168            }
169            if matches!(child.valign, VAlign::Bottom | VAlign::Center) {
170                style.margin.top = auto();
171            }
172            if matches!(child.halign, HAlign::Left | HAlign::Center) {
173                style.margin.right = auto();
174            }
175            if matches!(child.halign, HAlign::Right | HAlign::Center) {
176                style.margin.left = auto();
177            }
178
179            style.grid_column.start = line(child.column as i16 + 1);
180            style.grid_row.start = line(child.row as i16 + 1);
181
182            let cspan = child.column_span;
183            if cspan > 1 {
184                style.grid_column.end = span(cspan as u16);
185            }
186
187            let rspan = child.row_span;
188            if rspan > 1 {
189                style.grid_row.end = span(rspan as u16);
190            }
191
192            let node = tree.new_leaf(style)?;
193            nodes.push(node);
194        }
195        let root = tree.new_with_children(
196            Style {
197                display: taffy::Display::Grid,
198                size: taffy::Size::from_percent(1.0, 1.0),
199                grid_template_columns: self
200                    .columns
201                    .iter()
202                    .map(TrackSizingFunction::from)
203                    .map(GridTemplateComponent::Single)
204                    .collect(),
205                grid_template_rows: self
206                    .rows
207                    .iter()
208                    .map(TrackSizingFunction::from)
209                    .map(GridTemplateComponent::Single)
210                    .collect(),
211                ..Default::default()
212            },
213            &nodes,
214        )?;
215        Ok((tree, root, nodes))
216    }
217
218    fn render(&mut self) -> Result<(), LayoutError<E>> {
219        let (tree, root, nodes) = self.tree()?;
220        render(tree, root, nodes, self.loc, self.size, &mut self.children)
221    }
222
223    /// Move the location.
224    pub fn set_loc(&mut self, p: Point) -> Result<(), LayoutError<E>> {
225        LayoutChild::set_child_loc(self, p)
226    }
227
228    /// Resize.
229    pub fn set_size(&mut self, s: Size) -> Result<(), LayoutError<E>> {
230        LayoutChild::set_child_size(self, s)
231    }
232
233    /// Set the location and size.
234    pub fn set_rect(&mut self, r: Rect) -> Result<(), LayoutError<E>> {
235        LayoutChild::set_child_rect(self, r)
236    }
237}
238
239impl<E> Failable for Grid<'_, E> {
240    type Error = E;
241}
242
243impl<E> LayoutChild for Grid<'_, E> {
244    fn set_child_loc(&mut self, p: Point) -> Result<(), LayoutError<Self::Error>> {
245        self.loc = p;
246        self.render()
247    }
248
249    fn set_child_size(&mut self, s: Size) -> Result<(), LayoutError<Self::Error>> {
250        self.size = s;
251        self.render()
252    }
253
254    fn set_child_rect(&mut self, r: Rect) -> Result<(), LayoutError<Self::Error>> {
255        self.loc = r.origin;
256        self.size = r.size;
257        self.render()
258    }
259
260    fn child_preferred_size(&self) -> Result<Size, LayoutError<Self::Error>> {
261        let (mut tree, root, _) = self.tree()?;
262        tree.compute_layout(root, taffy::Size::max_content())?;
263        Ok(rect_t2e(tree.layout(root)?).size)
264    }
265
266    fn child_min_size(&self) -> Result<Size, LayoutError<Self::Error>> {
267        let (mut tree, root, _) = self.tree()?;
268        tree.compute_layout(root, taffy::Size::min_content())?;
269        Ok(rect_t2e(tree.layout(root)?).size)
270    }
271}