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::devtools::Probed;
11use crate::theme::{theme, Size};
12
13/// A responsive-feeling fixed-column grid. `SimpleGrid::new(3).spacing(Size::Md)`.
14#[derive(IntoElement)]
15pub struct SimpleGrid {
16  children: Vec<AnyElement>,
17  cols: usize,
18  spacing: Size,
19}
20
21impl SimpleGrid {
22  pub fn new(cols: usize) -> Self {
23    SimpleGrid {
24      children: Vec::new(),
25      cols: cols.max(1),
26      spacing: Size::Md,
27    }
28  }
29
30  pub fn spacing(mut self, spacing: Size) -> Self {
31    self.spacing = spacing;
32    self
33  }
34}
35
36impl ParentElement for SimpleGrid {
37  fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
38    self.children.extend(elements);
39  }
40}
41
42/// One equal-weight grid cell.
43fn cell() -> gpui::Div {
44  div().flex_grow().flex_shrink().flex_basis(relative(0.0))
45}
46
47impl RenderOnce for SimpleGrid {
48  fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
49    let gap = theme(cx).spacing(self.spacing);
50    let cols = self.cols;
51
52    let mut column = div().flex().flex_col().gap(px(gap));
53    let mut iter = self.children.into_iter();
54    loop {
55      let mut row_cells: Vec<AnyElement> = Vec::with_capacity(cols);
56      for _ in 0..cols {
57        match iter.next() {
58          Some(child) => row_cells.push(cell().child(child).into_any_element()),
59          None => break,
60        }
61      }
62      if row_cells.is_empty() {
63        break;
64      }
65      // Pad the final short row so columns line up.
66      while row_cells.len() < cols {
67        row_cells.push(cell().into_any_element());
68      }
69      column = column.child(div().flex().flex_row().gap(px(gap)).children(row_cells));
70    }
71    column.probe("SimpleGrid")
72  }
73}