1use crate::{LayoutPositionState, Transition};
3
4use gpui::{
5 AnyElement, App, AvailableSpace, Bounds, Display, Element, ElementId, GlobalElementId,
6 InspectorElementId, IntoElement, LayoutId, Pixels, Point, Style, Window, point, px, size,
7};
8
9use std::{cell::Cell, rc::Rc, time::Instant};
10
11#[derive(Clone, Debug, Default)]
17pub struct PopLayout {
18 origin: Rc<Cell<Point<Pixels>>>,
19}
20
21impl PopLayout {
22 pub fn container<E: IntoElement + 'static>(
36 &self,
37 id: impl Into<ElementId>,
38 child: E,
39 ) -> PopElement<E> {
40 PopElement {
41 id: id.into(),
42 child: Some(child),
43 scope: self.clone(),
44 present: None,
45 transition: Transition::default(),
46 }
47 }
48
49 pub fn item<E: IntoElement + 'static>(
55 &self,
56 id: impl Into<ElementId>,
57 present: bool,
58 child: E,
59 ) -> PopElement<E> {
60 PopElement {
61 id: id.into(),
62 child: Some(child),
63 scope: self.clone(),
64 present: Some(present),
65 transition: Transition::default(),
66 }
67 }
68}
69
70pub struct PopElement<E> {
72 id: ElementId,
73 child: Option<E>,
74 scope: PopLayout,
75 present: Option<bool>,
76 transition: Transition,
77}
78
79impl<E> PopElement<E> {
80 pub fn transition(mut self, transition: Transition) -> Self {
83 self.transition = transition;
84
85 self
86 }
87}
88
89struct History {
90 bounds: Option<Bounds<Pixels>>,
91 position: LayoutPositionState,
92 epoch: Instant,
93}
94
95struct OriginScope {
96 origin: Rc<Cell<Point<Pixels>>>,
97 previous: Point<Pixels>,
98}
99
100impl Drop for OriginScope {
101 fn drop(&mut self) {
102 self.origin.set(self.previous);
103 }
104}
105
106pub struct PopRequest {
109 child: AnyElement,
110 bounds: Option<Bounds<Pixels>>,
111}
112
113impl<E: IntoElement + 'static> IntoElement for PopElement<E> {
114 type Element = Self;
115
116 fn into_element(self) -> Self {
117 self
118 }
119}
120
121impl<E: IntoElement + 'static> Element for PopElement<E> {
122 type RequestLayoutState = PopRequest;
123 type PrepaintState = ();
124
125 fn id(&self) -> Option<ElementId> {
126 Some(self.id.clone())
127 }
128
129 fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
130 None
131 }
132
133 fn request_layout(
134 &mut self,
135 id: Option<&GlobalElementId>,
136 _: Option<&InspectorElementId>,
137 w: &mut Window,
138 cx: &mut App,
139 ) -> (LayoutId, PopRequest) {
140 let mut child = self.child.take().expect("one layout").into_any_element();
141 let now = cx.background_executor().now();
142
143 let old_bounds =
144 w.with_element_state(id.expect("pop ID"), |history: Option<History>, _| {
145 let h = history.unwrap_or_else(|| History {
146 bounds: None,
147 position: LayoutPositionState::default(),
148 epoch: now,
149 });
150
151 (h.bounds, h)
152 });
153
154 let layout = if self.present == Some(false) {
155 let style = Style {
157 display: Display::None,
158 ..Default::default()
159 };
160
161 w.request_layout(style, [], cx)
162 } else {
163 child.request_layout(w, cx)
164 };
165
166 (
167 layout,
168 PopRequest {
169 child,
170 bounds: old_bounds,
171 },
172 )
173 }
174
175 fn prepaint(
176 &mut self,
177 id: Option<&GlobalElementId>,
178 _: Option<&InspectorElementId>,
179 bounds: Bounds<Pixels>,
180 request: &mut PopRequest,
181 w: &mut Window,
182 cx: &mut App,
183 ) {
184 if self.present.is_none() {
185 let _scope = OriginScope {
186 previous: self.scope.origin.replace(bounds.origin),
187 origin: self.scope.origin.clone(),
188 };
189
190 request.child.prepaint(w, cx);
191
192 return;
193 }
194
195 let now = cx.background_executor().now();
196
197 if self.present == Some(false) {
198 if let Some(old) = request.bounds {
199 w.with_element_state(id.expect("pop ID"), |history: Option<History>, _| {
202 let mut h = history.expect("history initialized during layout");
203
204 h.position.sample(
205 [f64::from(old.origin.x), f64::from(old.origin.y)],
206 Transition::Instant,
207 now.saturating_duration_since(h.epoch),
208 true,
209 );
210
211 ((), h)
212 });
213 request.child.prepaint_as_root(
214 self.scope.origin.get() + old.origin,
215 size(
216 AvailableSpace::Definite(old.size.width),
217 AvailableSpace::Definite(old.size.height),
218 ),
219 w,
220 cx,
221 );
222 }
223 } else {
224 let origin = bounds.origin - self.scope.origin.get();
225
226 let frame = w.with_element_state(id.expect("pop ID"), |history: Option<History>, w| {
227 let mut h = history.expect("history initialized during layout");
228
229 let frame = h.position.sample(
230 [f64::from(origin.x), f64::from(origin.y)],
231 self.transition,
232 now.saturating_duration_since(h.epoch),
233 cx.reduce_motion(),
234 );
235
236 let [x, y] = frame
237 .position()
238 .map(|v| v.clamp(f32::MIN as f64, f32::MAX as f64) as f32);
239
240 h.bounds = Some(Bounds {
241 origin: point(px(x), px(y)),
242 size: bounds.size,
243 });
244
245 if frame.is_animating() {
246 w.request_animation_frame();
247 }
248
249 (frame, h)
250 });
251
252 let [x, y] = frame
253 .offset()
254 .map(|v| v.clamp(f32::MIN as f64, f32::MAX as f64) as f32);
255
256 w.with_element_offset(point(px(x), px(y)), |w| request.child.prepaint(w, cx));
257 }
258 }
259
260 fn paint(
261 &mut self,
262 _: Option<&GlobalElementId>,
263 _: Option<&InspectorElementId>,
264 _: Bounds<Pixels>,
265 request: &mut PopRequest,
266 _: &mut (),
267 w: &mut Window,
268 cx: &mut App,
269 ) {
270 if self.present != Some(false) || request.bounds.is_some() {
271 request.child.paint(w, cx);
272 }
273 }
274}