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