Skip to main content

guise/layout/
container.rs

1//! `Container` — a max-width centered column on the `xs..xl` size scale.
2//!
3//! Not to be confused with [`crate::flex::Container`], the Flutter-style
4//! pixel box (which is why `flex` is not glob-exported).
5//!
6//! ```ignore
7//! use guise::prelude::*;
8//!
9//! Container::new()
10//!     .size(Size::Sm)
11//!     .padding(Size::Md)
12//!     .child(Title::new("Article").order(2))
13//!     .child(Text::new("Readable line lengths on any window width."))
14//! ```
15
16use gpui::prelude::*;
17use gpui::{div, px, AnyElement, App, IntoElement, Window};
18
19use crate::devtools::Probed;
20use crate::theme::{theme, Size};
21
22/// Max content width (px) for each [`Size`].
23fn max_width(size: Size) -> f32 {
24  match size {
25    Size::Xs => 540.0,
26    Size::Sm => 720.0,
27    Size::Md => 960.0,
28    Size::Lg => 1140.0,
29    Size::Xl => 1320.0,
30  }
31}
32
33/// A centered column with a capped width.
34#[derive(IntoElement)]
35pub struct Container {
36  size: Size,
37  padding: Size,
38  children: Vec<AnyElement>,
39}
40
41impl Container {
42  pub fn new() -> Self {
43    Container {
44      size: Size::Md,
45      padding: Size::Md,
46      children: Vec::new(),
47    }
48  }
49
50  /// Max content width: `Xs..Xl` map to 540 / 720 / 960 / 1140 / 1320 px.
51  pub fn size(mut self, size: Size) -> Self {
52    self.size = size;
53    self
54  }
55
56  /// Horizontal padding inside the capped column (theme spacing scale).
57  pub fn padding(mut self, padding: Size) -> Self {
58    self.padding = padding;
59    self
60  }
61}
62
63impl Default for Container {
64  fn default() -> Self {
65    Container::new()
66  }
67}
68
69impl ParentElement for Container {
70  fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
71    self.children.extend(elements);
72  }
73}
74
75impl RenderOnce for Container {
76  fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
77    let pad = theme(cx).spacing(self.padding);
78    div()
79      .w_full()
80      .flex()
81      .flex_col()
82      .items_center()
83      .child(
84        div()
85          .w_full()
86          .max_w(px(max_width(self.size)))
87          .px(px(pad))
88          .flex()
89          .flex_col()
90          .children(self.children),
91      )
92      .probe("Container")
93  }
94}
95
96#[cfg(test)]
97mod tests {
98  use super::*;
99
100  #[test]
101  fn widths_follow_the_size_scale() {
102    assert_eq!(max_width(Size::Xs), 540.0);
103    assert_eq!(max_width(Size::Sm), 720.0);
104    assert_eq!(max_width(Size::Md), 960.0);
105    assert_eq!(max_width(Size::Lg), 1140.0);
106    assert_eq!(max_width(Size::Xl), 1320.0);
107  }
108}