Skip to main content

guise/flex/
stack.rs

1//! `Stack` and `Positioned` — Flutter's overlap layout.
2
3use gpui::prelude::*;
4use gpui::{div, px, AnyElement, App, IntoElement, Window};
5
6/// Overlays children on top of one another. Flutter's `Stack`.
7///
8/// The first child sits in normal flow and defines the size; wrap later
9/// children in [`Positioned`] to place them as overlays.
10#[derive(IntoElement)]
11pub struct Stack {
12    children: Vec<AnyElement>,
13}
14
15impl Stack {
16    pub fn new() -> Self {
17        Stack {
18            children: Vec::new(),
19        }
20    }
21}
22
23impl Default for Stack {
24    fn default() -> Self {
25        Stack::new()
26    }
27}
28
29impl ParentElement for Stack {
30    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
31        self.children.extend(elements);
32    }
33}
34
35impl RenderOnce for Stack {
36    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
37        div().relative().children(self.children)
38    }
39}
40
41/// Positions a child within a [`Stack`] by edge offsets. Flutter's `Positioned`.
42#[derive(IntoElement)]
43pub struct Positioned {
44    child: AnyElement,
45    top: Option<f32>,
46    right: Option<f32>,
47    bottom: Option<f32>,
48    left: Option<f32>,
49    width: Option<f32>,
50    height: Option<f32>,
51}
52
53impl Positioned {
54    pub fn new(child: impl IntoElement) -> Self {
55        Positioned {
56            child: child.into_any_element(),
57            top: None,
58            right: None,
59            bottom: None,
60            left: None,
61            width: None,
62            height: None,
63        }
64    }
65
66    /// Pins to all four edges (fills the stack).
67    pub fn fill(child: impl IntoElement) -> Self {
68        Positioned::new(child)
69            .top(0.0)
70            .right(0.0)
71            .bottom(0.0)
72            .left(0.0)
73    }
74
75    pub fn top(mut self, value: f32) -> Self {
76        self.top = Some(value);
77        self
78    }
79
80    pub fn right(mut self, value: f32) -> Self {
81        self.right = Some(value);
82        self
83    }
84
85    pub fn bottom(mut self, value: f32) -> Self {
86        self.bottom = Some(value);
87        self
88    }
89
90    pub fn left(mut self, value: f32) -> Self {
91        self.left = Some(value);
92        self
93    }
94
95    pub fn width(mut self, value: f32) -> Self {
96        self.width = Some(value);
97        self
98    }
99
100    pub fn height(mut self, value: f32) -> Self {
101        self.height = Some(value);
102        self
103    }
104}
105
106impl RenderOnce for Positioned {
107    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
108        let mut el = div().absolute();
109        if let Some(v) = self.top {
110            el = el.top(px(v));
111        }
112        if let Some(v) = self.right {
113            el = el.right(px(v));
114        }
115        if let Some(v) = self.bottom {
116            el = el.bottom(px(v));
117        }
118        if let Some(v) = self.left {
119            el = el.left(px(v));
120        }
121        if let Some(v) = self.width {
122            el = el.w(px(v));
123        }
124        if let Some(v) = self.height {
125            el = el.h(px(v));
126        }
127        el.child(self.child)
128    }
129}