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}