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