Skip to main content

ui/docking/
element.rs

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    /// Runs once on release. Apply the split/join synchronously and return its
21    /// resulting pane: the new pane for an edge split, the existing one for a
22    /// join. `None` rejects it and the item returns home.
23    pub fn on_drop(
24        mut self,
25        callback: impl Fn(&super::Drop<P, I>, &mut Window, &mut App) -> Option<P> + 'static,
26    ) -> Self {
27        self.dropped = Some(Rc::new(callback));
28        self
29    }
30}
31
32impl<P: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> RenderOnce for Surface<P, I> {
33    fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement {
34        self.dock.0.borrow_mut().dropped = self.dropped;
35        let moved = self.dock.clone();
36        let dropped = self.dock.clone();
37        SurfaceElement {
38            dock: self.dock,
39            child: div()
40                .id(self.id)
41                .size_full()
42                .on_drag_move(move |event: &DragMoveEvent<Carry<I>>, _, cx| {
43                    let carry = event.drag(cx);
44                    let (item, gesture) = (carry.item().clone(), carry.gesture.clone());
45                    moved.update(item, gesture, event.event.position, cx);
46                })
47                .on_drop(move |carry: &Carry<I>, window, cx| {
48                    dropped.release(carry, window.mouse_position(), window, cx);
49                })
50                .child(self.child)
51                .into_any_element(),
52        }
53    }
54}
55
56struct SurfaceElement<P, I> {
57    dock: Dock<P, I>,
58    child: AnyElement,
59}
60
61impl<P: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> IntoElement
62    for SurfaceElement<P, I>
63{
64    type Element = Self;
65    fn into_element(self) -> Self {
66        self
67    }
68}
69
70impl<P: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> Element
71    for SurfaceElement<P, I>
72{
73    type RequestLayoutState = ();
74    type PrepaintState = Option<AnyElement>;
75    fn id(&self) -> Option<ElementId> {
76        None
77    }
78    fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
79        None
80    }
81    fn request_layout(
82        &mut self,
83        _: Option<&GlobalElementId>,
84        _: Option<&InspectorElementId>,
85        window: &mut Window,
86        cx: &mut App,
87    ) -> (LayoutId, ()) {
88        (self.child.request_layout(window, cx), ())
89    }
90    fn prepaint(
91        &mut self,
92        _: Option<&GlobalElementId>,
93        _: Option<&InspectorElementId>,
94        bounds: Bounds<Pixels>,
95        _: &mut (),
96        window: &mut Window,
97        cx: &mut App,
98    ) -> Option<AnyElement> {
99        self.dock.0.borrow_mut().targets.clear();
100        if !cx.has_active_drag() {
101            self.dock.clear(cx);
102        }
103        self.child.prepaint(window, cx);
104        let mut state = self.dock.0.borrow_mut();
105        state.aim(cx);
106        let theme = theme::Theme::of(cx);
107        let mut overlay = div()
108            .absolute()
109            .w(bounds.size.width)
110            .h(bounds.size.height)
111            .overflow_hidden();
112        let mut active = false;
113        if let Some(preview) = &state.preview {
114            let rect = preview.bounds(cx);
115            let offset = rect.origin - bounds.origin;
116            let target = div()
117                .debug_selector(|| "dock-preview".into())
118                .absolute()
119                .left(offset.x)
120                .top(offset.y)
121                .w(rect.size.width)
122                .h(rect.size.height)
123                .rounded(px(6.));
124            // The surface's flat fallback is opaque and would hide the pane.
125            overlay = overlay.child(if crate::surface::lensed(theme) {
126                target.surface(theme, theme.drop_preview).into_any_element()
127            } else {
128                target.bg(theme.drop_target).into_any_element()
129            });
130            active |= preview.progress(cx) < 1.;
131        }
132        let ghost = state.settling.as_ref().and_then(|settle| {
133            let from = settle.ghost?;
134            state
135                .targets
136                .iter()
137                .find(|target| target.id == settle.pane)
138                .map(|target| {
139                    let tween = Tween {
140                        from,
141                        to: Bounds::new(target.bounds.origin, from.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.hosted())
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
214/// A measured target; style its child to set its layout size.
215pub 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}