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.pt(px(e.top))
161        .pr(px(e.right))
162        .pb(px(e.bottom))
163        .pl(px(e.left))
164}
165
166pub(crate) fn apply_margin(div: Div, e: EdgeInsets) -> Div {
167    div.mt(px(e.top))
168        .mr(px(e.right))
169        .mb(px(e.bottom))
170        .ml(px(e.left))
171}
172
173#[cfg(test)]
174mod tests {
175    use super::EdgeInsets;
176
177    #[test]
178    fn edge_insets_constructors() {
179        assert_eq!(EdgeInsets::all(8.0), EdgeInsets::only(8.0, 8.0, 8.0, 8.0));
180        assert_eq!(
181            EdgeInsets::symmetric(10.0, 4.0),
182            EdgeInsets::only(4.0, 10.0, 4.0, 10.0)
183        );
184        assert_eq!(
185            EdgeInsets::horizontal(6.0),
186            EdgeInsets::only(0.0, 6.0, 0.0, 6.0)
187        );
188        assert_eq!(
189            EdgeInsets::vertical(6.0),
190            EdgeInsets::only(6.0, 0.0, 6.0, 0.0)
191        );
192    }
193}