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 { top, right, bottom, left }
108    }
109
110    pub const fn horizontal(value: f32) -> Self {
111        EdgeInsets::symmetric(value, 0.0)
112    }
113
114    pub const fn vertical(value: f32) -> Self {
115        EdgeInsets::symmetric(0.0, value)
116    }
117}
118
119pub(crate) fn apply_main(div: Div, main: MainAxisAlignment) -> Div {
120    match main {
121        MainAxisAlignment::Start => div.justify_start(),
122        MainAxisAlignment::End => div.justify_end(),
123        MainAxisAlignment::Center => div.justify_center(),
124        MainAxisAlignment::SpaceBetween => div.justify_between(),
125        MainAxisAlignment::SpaceAround => div.justify_around(),
126        MainAxisAlignment::SpaceEvenly => div.justify_evenly(),
127    }
128}
129
130pub(crate) fn apply_cross(div: Div, cross: CrossAxisAlignment) -> Div {
131    match cross {
132        CrossAxisAlignment::Start => div.items_start(),
133        CrossAxisAlignment::End => div.items_end(),
134        CrossAxisAlignment::Center => div.items_center(),
135        CrossAxisAlignment::Stretch => div.items_stretch(),
136        CrossAxisAlignment::Baseline => div.items_baseline(),
137    }
138}
139
140pub(crate) fn apply_alignment(div: Div, alignment: Alignment) -> Div {
141    use Alignment::*;
142    let div = match alignment {
143        TopLeft | CenterLeft | BottomLeft => div.justify_start(),
144        TopCenter | Center | BottomCenter => div.justify_center(),
145        TopRight | CenterRight | BottomRight => div.justify_end(),
146    };
147    match alignment {
148        TopLeft | TopCenter | TopRight => div.items_start(),
149        CenterLeft | Center | CenterRight => div.items_center(),
150        BottomLeft | BottomCenter | BottomRight => div.items_end(),
151    }
152}
153
154pub(crate) fn apply_padding(div: Div, e: EdgeInsets) -> Div {
155    div.pt(px(e.top))
156        .pr(px(e.right))
157        .pb(px(e.bottom))
158        .pl(px(e.left))
159}
160
161pub(crate) fn apply_margin(div: Div, e: EdgeInsets) -> Div {
162    div.mt(px(e.top))
163        .mr(px(e.right))
164        .mb(px(e.bottom))
165        .ml(px(e.left))
166}
167
168#[cfg(test)]
169mod tests {
170    use super::EdgeInsets;
171
172    #[test]
173    fn edge_insets_constructors() {
174        assert_eq!(EdgeInsets::all(8.0), EdgeInsets::only(8.0, 8.0, 8.0, 8.0));
175        assert_eq!(
176            EdgeInsets::symmetric(10.0, 4.0),
177            EdgeInsets::only(4.0, 10.0, 4.0, 10.0)
178        );
179        assert_eq!(EdgeInsets::horizontal(6.0), EdgeInsets::only(0.0, 6.0, 0.0, 6.0));
180        assert_eq!(EdgeInsets::vertical(6.0), EdgeInsets::only(6.0, 0.0, 6.0, 0.0));
181    }
182}