1use super::{Dock, OnDrop, Target, Tween};
2use crate::drag::Carry;
3use crate::surface::Surfaced;
4use gpui::{
5 AnyElement, App, AvailableSpace, Bounds, ContentMask, DragMoveEvent, Element, ElementId,
6 GlobalElementId, InspectorElementId, IntoElement, LayoutId, Pixels, RenderOnce, Window, div,
7 prelude::*, px,
8};
9use std::rc::Rc;
10
11#[derive(IntoElement)]
12pub struct Surface<P: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> {
13 pub(super) dock: Dock<P, I>,
14 pub(super) id: ElementId,
15 pub(super) child: AnyElement,
16 pub(super) dropped: Option<Rc<OnDrop<P, I>>>,
17}
18
19impl<P: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> Surface<P, I> {
20 pub fn on_drop(
22 mut self,
23 callback: impl Fn(&super::Drop<P, I>, &mut Window, &mut App) -> Option<P> + 'static,
24 ) -> Self {
25 self.dropped = Some(Rc::new(callback));
26 self
27 }
28}
29
30impl<P: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> RenderOnce for Surface<P, I> {
31 fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement {
32 self.dock.0.borrow_mut().dropped = self.dropped;
33 let moved = self.dock.clone();
34 let dropped = self.dock.clone();
35 SurfaceElement {
36 dock: self.dock,
37 child: div()
38 .id(self.id)
39 .size_full()
40 .on_drag_move(move |event: &DragMoveEvent<Carry<I>>, _, cx| {
41 let carry = event.drag(cx);
42 let (item, gesture) = (carry.item().clone(), carry.gesture.clone());
43 moved.update(item, gesture, event.event.position, cx);
44 })
45 .on_drop(move |carry: &Carry<I>, window, cx| {
46 dropped.release(carry, window.mouse_position(), window, cx);
47 })
48 .child(self.child)
49 .into_any_element(),
50 }
51 }
52}
53
54struct SurfaceElement<P, I> {
55 dock: Dock<P, I>,
56 child: AnyElement,
57}
58
59impl<P: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> IntoElement
60 for SurfaceElement<P, I>
61{
62 type Element = Self;
63 fn into_element(self) -> Self {
64 self
65 }
66}
67
68impl<P: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> Element
69 for SurfaceElement<P, I>
70{
71 type RequestLayoutState = ();
72 type PrepaintState = Option<AnyElement>;
73 fn id(&self) -> Option<ElementId> {
74 None
75 }
76 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
77 None
78 }
79 fn request_layout(
80 &mut self,
81 _: Option<&GlobalElementId>,
82 _: Option<&InspectorElementId>,
83 window: &mut Window,
84 cx: &mut App,
85 ) -> (LayoutId, ()) {
86 (self.child.request_layout(window, cx), ())
87 }
88 fn prepaint(
89 &mut self,
90 _: Option<&GlobalElementId>,
91 _: Option<&InspectorElementId>,
92 bounds: Bounds<Pixels>,
93 _: &mut (),
94 window: &mut Window,
95 cx: &mut App,
96 ) -> Option<AnyElement> {
97 self.dock.0.borrow_mut().targets.clear();
98 if !cx.has_active_drag() {
99 self.dock.clear(cx);
100 }
101 self.child.prepaint(window, cx);
102 let mut state = self.dock.0.borrow_mut();
103 state.aim(cx);
104 let theme = theme::Theme::of(cx);
105 let mut overlay = div()
106 .absolute()
107 .w(bounds.size.width)
108 .h(bounds.size.height)
109 .overflow_hidden();
110 let mut active = false;
111 if let Some(preview) = &state.preview {
112 let rect = preview.bounds(cx);
113 let offset = rect.origin - bounds.origin;
114 let target = div()
115 .debug_selector(|| "dock-preview".into())
116 .absolute()
117 .left(offset.x)
118 .top(offset.y)
119 .w(rect.size.width)
120 .h(rect.size.height)
121 .rounded(px(6.));
122 overlay = overlay.child(if crate::surface::lensed(theme) {
124 target.surface(theme, theme.drop_preview).into_any_element()
125 } else {
126 target.bg(theme.drop_target).into_any_element()
127 });
128 active |= preview.progress(cx) < 1.;
129 }
130 let ghost = state.settling.as_ref().and_then(|settle| {
131 let from = settle.ghost?;
132 state
133 .targets
134 .iter()
135 .find(|target| target.id == settle.pane)
136 .map(|target| {
137 let tween = Tween {
138 from,
139 to: Bounds::new(target.bounds.origin, from.size),
140 since: settle.since,
141 };
142 (settle.item.clone(), tween.bounds(cx), tween.progress(cx))
143 })
144 });
145 let carried = state
146 .carried
147 .as_ref()
148 .filter(|carried| carried.hosted())
149 .map(|carried| {
150 let rect = carried.ghost_bounds();
151 carried.gesture.host(rect.origin);
152 (carried.item.clone(), rect, 0.)
153 });
154 let render_ghost = state.ghost.clone();
155 if let Some((_, _, progress)) = &ghost {
156 if *progress >= 1. {
157 state.settling = None;
158 } else {
159 active = true;
160 }
161 } else {
162 state.settling = None;
163 }
164 if active {
165 state.painter.lease(120., motion::TAB_SLIDE.total(), cx);
166 }
167 drop(state);
168 if let Some((item, rect, progress)) = carried.or(ghost)
169 && progress < 1.
170 {
171 let offset = rect.origin - bounds.origin;
172 let selector = if progress == 0. && self.dock.0.borrow().carried.is_some() {
173 "dock-carry-ghost"
174 } else {
175 "dock-settle-ghost"
176 };
177 overlay = overlay.child(
178 div()
179 .debug_selector(move || selector.into())
180 .absolute()
181 .left(offset.x)
182 .top(offset.y)
183 .w(rect.size.width)
184 .h(rect.size.height)
185 .overflow_hidden()
186 .opacity(1. - progress)
187 .child(render_ghost(&item, window, cx)),
188 );
189 }
190 let mut overlay = overlay.into_any_element();
191 overlay.layout_as_root(bounds.size.map(AvailableSpace::Definite), window, cx);
192 overlay.prepaint_at(bounds.origin, window, cx);
193 Some(overlay)
194 }
195 fn paint(
196 &mut self,
197 _: Option<&GlobalElementId>,
198 _: Option<&InspectorElementId>,
199 _: Bounds<Pixels>,
200 _: &mut (),
201 overlay: &mut Option<AnyElement>,
202 window: &mut Window,
203 cx: &mut App,
204 ) {
205 self.child.paint(window, cx);
206 if let Some(overlay) = overlay {
207 overlay.paint(window, cx);
208 }
209 }
210}
211
212pub struct Pane<P, I> {
214 pub(super) dock: Dock<P, I>,
215 pub(super) id: P,
216 pub(super) bar_height: Pixels,
217 pub(super) child: AnyElement,
218}
219
220impl<P: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> IntoElement for Pane<P, I> {
221 type Element = Self;
222 fn into_element(self) -> Self {
223 self
224 }
225}
226impl<P: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> Element for Pane<P, I> {
227 type RequestLayoutState = ();
228 type PrepaintState = Bounds<Pixels>;
229 fn id(&self) -> Option<ElementId> {
230 None
231 }
232 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
233 None
234 }
235 fn request_layout(
236 &mut self,
237 _: Option<&GlobalElementId>,
238 _: Option<&InspectorElementId>,
239 window: &mut Window,
240 cx: &mut App,
241 ) -> (LayoutId, ()) {
242 (self.child.request_layout(window, cx), ())
243 }
244 fn prepaint(
245 &mut self,
246 _: Option<&GlobalElementId>,
247 _: Option<&InspectorElementId>,
248 bounds: Bounds<Pixels>,
249 _: &mut (),
250 window: &mut Window,
251 cx: &mut App,
252 ) -> Bounds<Pixels> {
253 let mut state = self.dock.0.borrow_mut();
254 state.targets.push(Target {
255 id: self.id.clone(),
256 bounds,
257 visible: bounds.intersect(&window.content_mask().bounds),
258 bar_height: self.bar_height,
259 });
260 let rect = state
261 .settling
262 .as_ref()
263 .filter(|settle| settle.pane == self.id)
264 .map(|settle| {
265 Tween {
266 from: settle.preview,
267 to: bounds,
268 since: settle.since,
269 }
270 .bounds(cx)
271 })
272 .unwrap_or(bounds);
273 drop(state);
274 window.with_content_mask(
275 Some(ContentMask {
276 bounds: rect,
277 ..Default::default()
278 }),
279 |window| {
280 window.with_element_offset(rect.origin - bounds.origin, |window| {
281 self.child.prepaint(window, cx)
282 });
283 },
284 );
285 rect
286 }
287 fn paint(
288 &mut self,
289 _: Option<&GlobalElementId>,
290 _: Option<&InspectorElementId>,
291 _: Bounds<Pixels>,
292 _: &mut (),
293 rect: &mut Bounds<Pixels>,
294 window: &mut Window,
295 cx: &mut App,
296 ) {
297 window.with_content_mask(
298 Some(ContentMask {
299 bounds: *rect,
300 ..Default::default()
301 }),
302 |window| self.child.paint(window, cx),
303 );
304 }
305}