gpui_ce_motion/
layout_native.rs1use crate::{LayoutPositionState, Transition};
3
4use gpui::{
5 AnyElement, App, Element, ElementId, GlobalElementId, InspectorElementId, IntoElement,
6 LayoutId, Pixels, Window, point, px,
7};
8
9use std::{cell::Cell, rc::Rc, time::Instant};
10
11#[derive(Clone, Debug, Default)]
16pub struct LayoutGroup {
17 ancestor: Rc<Cell<Option<[f64; 2]>>>,
18}
19
20impl LayoutGroup {
21 pub fn position<E: IntoElement + 'static>(
37 &self,
38 id: impl Into<ElementId>,
39 element: E,
40 ) -> LayoutPositionElement<E> {
41 LayoutPositionElement {
42 id: id.into(),
43 element: Some(element),
44 group: self.clone(),
45 transition: Transition::default(),
46 enabled: true,
47 }
48 }
49}
50
51pub struct LayoutPositionElement<E> {
55 id: ElementId,
56 element: Option<E>,
57 group: LayoutGroup,
58 transition: Transition,
59 enabled: bool,
60}
61
62impl<E> LayoutPositionElement<E> {
63 pub fn transition(mut self, transition: Transition) -> Self {
65 self.transition = transition;
66
67 self
68 }
69
70 pub fn enabled(mut self, enabled: bool) -> Self {
73 self.enabled = enabled;
74
75 self
76 }
77}
78
79struct RetainedPosition {
80 state: LayoutPositionState,
81 epoch: Instant,
82}
83
84struct AncestorScope {
85 ancestor: Rc<Cell<Option<[f64; 2]>>>,
86 previous: Option<[f64; 2]>,
87}
88
89impl Drop for AncestorScope {
90 fn drop(&mut self) {
91 self.ancestor.set(self.previous);
92 }
93}
94
95impl<E: IntoElement + 'static> IntoElement for LayoutPositionElement<E> {
96 type Element = Self;
97
98 fn into_element(self) -> Self::Element {
99 self
100 }
101}
102
103impl<E: IntoElement + 'static> Element for LayoutPositionElement<E> {
104 type RequestLayoutState = AnyElement;
105 type PrepaintState = ();
106
107 fn id(&self) -> Option<ElementId> {
108 Some(self.id.clone())
109 }
110
111 fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
112 None
113 }
114
115 fn request_layout(
116 &mut self,
117 _: Option<&GlobalElementId>,
118 _: Option<&InspectorElementId>,
119 window: &mut Window,
120 cx: &mut App,
121 ) -> (LayoutId, AnyElement) {
122 let mut child = self
123 .element
124 .take()
125 .expect("layout requested once")
126 .into_any_element();
127
128 let layout = child.request_layout(window, cx);
129
130 (layout, child)
131 }
132
133 fn prepaint(
134 &mut self,
135 id: Option<&GlobalElementId>,
136 _: Option<&InspectorElementId>,
137 bounds: gpui::Bounds<Pixels>,
138 child: &mut AnyElement,
139 window: &mut Window,
140 cx: &mut App,
141 ) {
142 let raw = bounds.origin - window.pixel_snap_point(window.element_offset());
144 let origin = [f64::from(raw.x), f64::from(raw.y)];
145 let parent = self.group.ancestor.get().unwrap_or([0.; 2]);
146 let relative = std::array::from_fn(|i| origin[i] - parent[i]);
147 let now = cx.background_executor().now();
148 let reduced = cx.reduce_motion() || !self.enabled;
149
150 let frame = window.with_element_state(
151 id.expect("position wrapper has an ID"),
152 |state: Option<RetainedPosition>, window| {
153 let mut retained = state.unwrap_or_else(|| RetainedPosition {
154 state: LayoutPositionState::default(),
155 epoch: now,
156 });
157
158 let frame = retained.state.sample(
159 relative,
160 self.transition,
161 now.saturating_duration_since(retained.epoch),
162 reduced,
163 );
164
165 if frame.is_animating() {
166 window.request_animation_frame();
167 }
168
169 (frame, retained)
170 },
171 );
172
173 let ancestor = self.group.ancestor.clone();
174
175 let _scope = AncestorScope {
176 previous: ancestor.replace(Some(origin)),
177 ancestor,
178 };
179
180 let [x, y] = frame
181 .offset()
182 .map(|v| v.clamp(f64::from(f32::MIN), f64::from(f32::MAX)) as f32);
183
184 window.with_element_offset(point(px(x), px(y)), |window| {
185 child.prepaint(window, cx);
186 });
187 }
188
189 fn paint(
190 &mut self,
191 _: Option<&GlobalElementId>,
192 _: Option<&InspectorElementId>,
193 _: gpui::Bounds<Pixels>,
194 child: &mut AnyElement,
195 _: &mut (),
196 window: &mut Window,
197 cx: &mut App,
198 ) {
199 child.paint(window, cx);
200 }
201}