Skip to main content

guise/flex/
stack.rs

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