Skip to main content

guise/flex/
mod.rs

1//! Flexbox layout primitives, for people who think in `Row`/`Column`/
2//! `Container`/`Expanded` (à la Flutter).
3//!
4//! These map a Flutter-like box model onto gpui's flexbox. Several names
5//! (`Row`, `Column`, `Stack`, `Center`, ...) overlap with guise's own `layout`
6//! module (`Stack`/`Center`), so this module is **not** glob-exported at the
7//! crate root. Import it explicitly:
8//!
9//! ```ignore
10//! use guise::flex::*;
11//!
12//! Column::new()
13//!     .cross_axis_alignment(CrossAxisAlignment::Stretch)
14//!     .child(Row::new().child(Expanded::new(header)).child(actions))
15//!     .child(SizedBox::height(12.0))
16//!     .child(Expanded::new(body))
17//! ```
18
19mod container;
20mod flexible;
21mod rowcolumn;
22mod stack;
23mod wrap;
24
25pub use container::{Align, Center, Container, Padding};
26pub use flexible::{Expanded, Flexible, SizedBox, Spacer};
27pub use rowcolumn::{Column, Row};
28pub use stack::{Positioned, Stack};
29pub use wrap::Wrap;
30
31use gpui::prelude::*;
32use gpui::{px, Div};
33
34use crate::style::FlexExt;
35
36/// Distribution of children along the main axis.
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum MainAxisAlignment {
39  Start,
40  End,
41  Center,
42  SpaceBetween,
43  SpaceAround,
44  SpaceEvenly,
45}
46
47/// Alignment of children across the cross axis.
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub enum CrossAxisAlignment {
50  Start,
51  End,
52  Center,
53  Stretch,
54  Baseline,
55}
56
57/// Whether a Row/Column shrinks to its children or fills the main axis.
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub enum MainAxisSize {
60  Min,
61  Max,
62}
63
64/// A 2-D alignment within a box (`Container`/`Align`/`Center`).
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub enum Alignment {
67  TopLeft,
68  TopCenter,
69  TopRight,
70  CenterLeft,
71  Center,
72  CenterRight,
73  BottomLeft,
74  BottomCenter,
75  BottomRight,
76}
77
78/// Padding/margin offsets, like Flutter's `EdgeInsets`.
79#[derive(Debug, Clone, Copy, Default, PartialEq)]
80pub struct EdgeInsets {
81  pub top: f32,
82  pub right: f32,
83  pub bottom: f32,
84  pub left: f32,
85}
86
87impl EdgeInsets {
88  pub const fn all(value: f32) -> Self {
89    EdgeInsets {
90      top: value,
91      right: value,
92      bottom: value,
93      left: value,
94    }
95  }
96
97  pub const fn symmetric(horizontal: f32, vertical: f32) -> Self {
98    EdgeInsets {
99      top: vertical,
100      right: horizontal,
101      bottom: vertical,
102      left: horizontal,
103    }
104  }
105
106  pub const fn only(top: f32, right: f32, bottom: f32, left: f32) -> Self {
107    EdgeInsets {
108      top,
109      right,
110      bottom,
111      left,
112    }
113  }
114
115  pub const fn horizontal(value: f32) -> Self {
116    EdgeInsets::symmetric(value, 0.0)
117  }
118
119  pub const fn vertical(value: f32) -> Self {
120    EdgeInsets::symmetric(0.0, value)
121  }
122}
123
124pub(crate) fn apply_main(div: Div, main: MainAxisAlignment) -> Div {
125  match main {
126    MainAxisAlignment::Start => div.justify_start(),
127    MainAxisAlignment::End => div.justify_end(),
128    MainAxisAlignment::Center => div.justify_center(),
129    MainAxisAlignment::SpaceBetween => div.justify_between(),
130    MainAxisAlignment::SpaceAround => div.justify_around(),
131    MainAxisAlignment::SpaceEvenly => div.justify_evenly(),
132  }
133}
134
135pub(crate) fn apply_cross(div: Div, cross: CrossAxisAlignment) -> Div {
136  match cross {
137    CrossAxisAlignment::Start => div.items_start(),
138    CrossAxisAlignment::End => div.items_end(),
139    CrossAxisAlignment::Center => div.items_center(),
140    CrossAxisAlignment::Stretch => div.items_stretch(),
141    CrossAxisAlignment::Baseline => div.items_baseline(),
142  }
143}
144
145pub(crate) fn apply_alignment(div: Div, alignment: Alignment) -> Div {
146  use Alignment::*;
147  let div = match alignment {
148    TopLeft | CenterLeft | BottomLeft => div.justify_start(),
149    TopCenter | Center | BottomCenter => div.justify_center(),
150    TopRight | CenterRight | BottomRight => div.justify_end(),
151  };
152  match alignment {
153    TopLeft | TopCenter | TopRight => div.items_start(),
154    CenterLeft | Center | CenterRight => div.items_center(),
155    BottomLeft | BottomCenter | BottomRight => div.items_end(),
156  }
157}
158
159pub(crate) fn apply_padding(div: Div, e: EdgeInsets) -> Div {
160  div
161    .pt(px(e.top))
162    .pr(px(e.right))
163    .pb(px(e.bottom))
164    .pl(px(e.left))
165}
166
167pub(crate) fn apply_margin(div: Div, e: EdgeInsets) -> Div {
168  div
169    .mt(px(e.top))
170    .mr(px(e.right))
171    .mb(px(e.bottom))
172    .ml(px(e.left))
173}
174
175#[cfg(test)]
176mod tests {
177  use super::EdgeInsets;
178
179  #[test]
180  fn edge_insets_constructors() {
181    assert_eq!(EdgeInsets::all(8.0), EdgeInsets::only(8.0, 8.0, 8.0, 8.0));
182    assert_eq!(
183      EdgeInsets::symmetric(10.0, 4.0),
184      EdgeInsets::only(4.0, 10.0, 4.0, 10.0)
185    );
186    assert_eq!(
187      EdgeInsets::horizontal(6.0),
188      EdgeInsets::only(0.0, 6.0, 0.0, 6.0)
189    );
190    assert_eq!(
191      EdgeInsets::vertical(6.0),
192      EdgeInsets::only(6.0, 0.0, 6.0, 0.0)
193    );
194  }
195}