Skip to main content

guise/layout/
grid.rs

1//! `SimpleGrid` — equal-width columns that wrap into rows.
2//!
3//! gpui's flexbox has no CSS-grid track system, so this lays children out as a
4//! column of flex rows, each holding up to `cols` equal-weight cells. The final
5//! row is padded with empty cells so columns stay aligned.
6
7use gpui::prelude::*;
8use gpui::{div, px, relative, AnyElement, App, IntoElement, Window};
9
10use crate::theme::{theme, Size};
11
12/// A responsive-feeling fixed-column grid. `SimpleGrid::new(3).spacing(Size::Md)`.
13#[derive(IntoElement)]
14pub struct SimpleGrid {
15    children: Vec<AnyElement>,
16    cols: usize,
17    spacing: Size,
18}
19
20impl SimpleGrid {
21    pub fn new(cols: usize) -> Self {
22        SimpleGrid {
23            children: Vec::new(),
24            cols: cols.max(1),
25            spacing: Size::Md,
26        }
27    }
28
29    pub fn spacing(mut self, spacing: Size) -> Self {
30        self.spacing = spacing;
31        self
32    }
33}
34
35impl ParentElement for SimpleGrid {
36    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
37        self.children.extend(elements);
38    }
39}
40
41/// One equal-weight grid cell.
42fn cell() -> gpui::Div {
43    div().flex_grow().flex_shrink().flex_basis(relative(0.0))
44}
45
46impl RenderOnce for SimpleGrid {
47    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
48        let gap = theme(cx).spacing(self.spacing);
49        let cols = self.cols;
50
51        let mut column = div().flex().flex_col().gap(px(gap));
52        let mut iter = self.children.into_iter();
53        loop {
54            let mut row_cells: Vec<AnyElement> = Vec::with_capacity(cols);
55            for _ in 0..cols {
56                match iter.next() {
57                    Some(child) => row_cells.push(cell().child(child).into_any_element()),
58                    None => break,
59                }
60            }
61            if row_cells.is_empty() {
62                break;
63            }
64            // Pad the final short row so columns line up.
65            while row_cells.len() < cols {
66                row_cells.push(cell().into_any_element());
67            }
68            column = column.child(div().flex().flex_row().gap(px(gap)).children(row_cells));
69        }
70        column
71    }
72}