1use std::rc::Rc;
2
3use gpui::{
4 AnyElement, App, Axis, Bounds, DispatchPhase, Element, ElementId, GlobalElementId,
5 InspectorElementId, IntoElement, LayoutId, MouseButton, MouseDownEvent, MouseMoveEvent,
6 MouseUpEvent, Pixels, Point, Refineable, RenderOnce, StyleRefinement, Styled, Window, div,
7 fill, point, prelude::*, px, size,
8};
9
10use motion::Painter;
11
12use crate::scroll::Scroller;
13
14use super::{
15 Domain, Drop, Feedback, Outside,
16 state::{Accepts, Commit, Config, Lands, OnDrop, OnOutside},
17};
18
19#[derive(IntoElement)]
22pub struct Region<R: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> {
23 domain: Domain<R, I>,
24 element_id: ElementId,
25 id: R,
26 axis: Axis,
27 child: AnyElement,
28 accepts: Option<Box<Accepts<I>>>,
29 lands: Option<Box<Lands<I>>>,
30 feedback: Feedback,
31 axis_locked: bool,
32 scroll: Option<Scroller>,
33 dropped: Option<Rc<OnDrop<R, I>>>,
34 outside: Option<Rc<OnOutside<I>>>,
35 style: StyleRefinement,
36}
37
38impl<R: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> Region<R, I> {
39 pub(super) fn new(
40 domain: Domain<R, I>,
41 element_id: ElementId,
42 id: R,
43 axis: Axis,
44 child: AnyElement,
45 ) -> Self {
46 Self {
47 domain,
48 element_id,
49 id,
50 axis,
51 child,
52 accepts: None,
53 lands: None,
54 feedback: Feedback::default(),
55 axis_locked: false,
56 scroll: None,
57 dropped: None,
58 outside: None,
59 style: StyleRefinement::default(),
60 }
61 }
62
63 pub fn accepts(mut self, accepts: impl Fn(&I) -> bool + 'static) -> Self {
65 self.accepts = Some(Box::new(accepts));
66 self
67 }
68
69 pub fn lands(mut self, lands: impl Fn(&I, Option<&I>, Option<&I>) -> bool + 'static) -> Self {
74 self.lands = Some(Box::new(lands));
75 self
76 }
77
78 pub fn feedback(mut self, feedback: Feedback) -> Self {
79 self.feedback = feedback;
80 self
81 }
82
83 pub fn axis_locked(mut self) -> Self {
86 self.axis_locked = true;
87 self
88 }
89
90 pub fn track_scroll(mut self, handle: impl Into<Scroller>) -> Self {
94 self.scroll = Some(handle.into());
95 self
96 }
97
98 pub fn on_drop(
101 mut self,
102 dropped: impl Fn(&Drop<R, I>, &mut Window, &mut App) + 'static,
103 ) -> Self {
104 self.dropped = Some(Rc::new(dropped));
105 self
106 }
107
108 pub fn on_drop_outside(
111 mut self,
112 outside: impl Fn(&Outside<I>, &mut Window, &mut App) + 'static,
113 ) -> Self {
114 self.outside = Some(Rc::new(outside));
115 self
116 }
117}
118
119impl<R: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> Styled for Region<R, I> {
120 fn style(&mut self) -> &mut StyleRefinement {
121 &mut self.style
122 }
123}
124
125impl<R: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> RenderOnce for Region<R, I> {
126 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
127 let focus = window
128 .use_keyed_state(self.element_id.clone(), cx, |_, cx| cx.focus_handle())
129 .read(cx)
130 .clone();
131 let ended = {
132 let state = self.domain.0.borrow();
133 state.live.is_some() && (state.stale || !cx.has_active_drag())
134 };
135 if ended {
136 self.domain.cancel(window, cx);
137 }
138 let config = Rc::new(Config {
139 accepts: self.accepts,
140 lands: self.lands,
141 feedback: self.feedback,
142 axis_locked: self.axis_locked,
143 scroll: self.scroll,
144 dropped: self.dropped,
145 outside: self.outside,
146 focus: focus.clone(),
147 });
148 let escaped = self.domain.clone();
149 let mut frame =
150 div()
151 .id(self.element_id)
152 .track_focus(&focus)
153 .on_key_down(move |event, window, cx| {
154 if event.keystroke.key == "escape" && escaped.0.borrow().live.is_some() {
155 escaped.cancel(window, cx);
156 cx.stop_propagation();
157 }
158 });
159 frame.style().refine(&self.style);
160 RegionElement {
161 domain: self.domain,
162 id: self.id,
163 axis: self.axis,
164 config,
165 child: frame.child(self.child).into_any_element(),
166 }
167 }
168}
169
170struct RegionElement<R, I> {
171 domain: Domain<R, I>,
172 id: R,
173 axis: Axis,
174 config: Rc<Config<R, I>>,
175 child: AnyElement,
176}
177
178impl<R: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> IntoElement
179 for RegionElement<R, I>
180{
181 type Element = Self;
182 fn into_element(self) -> Self {
183 self
184 }
185}
186
187impl<R: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> Element
188 for RegionElement<R, I>
189{
190 type RequestLayoutState = ();
191 type PrepaintState = ();
192 fn id(&self) -> Option<ElementId> {
193 None
194 }
195 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
196 None
197 }
198 fn request_layout(
199 &mut self,
200 _: Option<&GlobalElementId>,
201 _: Option<&InspectorElementId>,
202 window: &mut Window,
203 cx: &mut App,
204 ) -> (LayoutId, ()) {
205 (self.child.request_layout(window, cx), ())
206 }
207 fn prepaint(
208 &mut self,
209 _: Option<&GlobalElementId>,
210 _: Option<&InspectorElementId>,
211 bounds: Bounds<Pixels>,
212 _: &mut (),
213 window: &mut Window,
214 cx: &mut App,
215 ) {
216 let visible = bounds.intersect(&window.content_mask().bounds);
217 self.domain.0.borrow_mut().drift(cx);
218 let previous = self.domain.0.borrow_mut().enter(
219 self.id.clone(),
220 self.axis,
221 bounds,
222 visible,
223 self.config.clone(),
224 Painter::from(window.current_view()),
225 );
226 self.child.prepaint(window, cx);
227 let mut state = self.domain.0.borrow_mut();
228 state.leave(previous);
229 }
230 fn paint(
231 &mut self,
232 _: Option<&GlobalElementId>,
233 _: Option<&InspectorElementId>,
234 _: Bounds<Pixels>,
235 _: &mut (),
236 _: &mut (),
237 window: &mut Window,
238 cx: &mut App,
239 ) {
240 self.child.paint(window, cx);
241 if self.config.feedback == Feedback::Indicator
242 && let Some((axis, at, visible)) = self.domain.0.borrow().indicator(&self.id)
243 {
244 let line = match axis {
245 Axis::Horizontal => Bounds::new(
246 point(at - px(1.), visible.origin.y),
247 size(px(2.), visible.size.height),
248 ),
249 Axis::Vertical => Bounds::new(
250 point(visible.origin.x, at - px(1.)),
251 size(visible.size.width, px(2.)),
252 ),
253 };
254 window.paint_quad(fill(line, theme::Theme::of(cx).accent));
255 }
256 let moving = self.domain.clone();
257 window.on_mouse_event(move |event: &MouseMoveEvent, phase, _, cx| {
258 if phase != DispatchPhase::Capture {
259 return;
260 }
261 let mut state = moving.0.borrow_mut();
262 if state.aim(event.position) {
263 state.notify(cx);
264 }
265 });
266 let released = self.domain.clone();
267 window.on_mouse_event(move |event: &MouseUpEvent, phase, window, cx| {
268 if phase != DispatchPhase::Capture || event.button != MouseButton::Left {
269 return;
270 }
271 let commit = released.0.borrow_mut().release(event.position, window, cx);
272 match commit {
273 Some(Commit::Drop(dropped, event)) => dropped(&event, window, cx),
274 Some(Commit::Outside(outside, event)) => outside(&event, window, cx),
275 None => {}
276 }
277 });
278 }
279}
280
281pub struct Handle<R, I> {
283 domain: Domain<R, I>,
284 item: I,
285 child: Option<AnyElement>,
286}
287
288impl<R, I> Handle<R, I> {
289 pub(super) fn new(domain: Domain<R, I>, item: I, child: AnyElement) -> Self {
290 Self {
291 domain,
292 item,
293 child: Some(child),
294 }
295 }
296}
297
298impl<R: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> IntoElement for Handle<R, I> {
299 type Element = Self;
300 fn into_element(self) -> Self {
301 self
302 }
303}
304
305#[doc(hidden)]
306pub struct Painted {
307 origin: Point<Pixels>,
308 hidden: bool,
309}
310
311impl<R: Clone + PartialEq + 'static, I: Clone + PartialEq + 'static> Element for Handle<R, I> {
312 type RequestLayoutState = ();
313 type PrepaintState = Painted;
314 fn id(&self) -> Option<ElementId> {
315 None
316 }
317 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
318 None
319 }
320 fn request_layout(
321 &mut self,
322 _: Option<&GlobalElementId>,
323 _: Option<&InspectorElementId>,
324 window: &mut Window,
325 cx: &mut App,
326 ) -> (LayoutId, ()) {
327 (self.child.as_mut().unwrap().request_layout(window, cx), ())
328 }
329 fn prepaint(
330 &mut self,
331 _: Option<&GlobalElementId>,
332 _: Option<&InspectorElementId>,
333 bounds: Bounds<Pixels>,
334 _: &mut (),
335 window: &mut Window,
336 cx: &mut App,
337 ) -> Painted {
338 let pointer = window.mouse_position();
339 let placement = self
340 .domain
341 .0
342 .borrow_mut()
343 .place(&self.item, bounds, pointer, cx);
344 window.with_element_offset(placement.offset, |window| {
345 if placement.floating {
346 let child = self.child.take().unwrap();
347 let offset = window.element_offset();
348 window.defer_draw(child, offset, 1, None);
349 } else {
350 self.child.as_mut().unwrap().prepaint(window, cx);
351 }
352 });
353 Painted {
354 origin: bounds.origin + placement.offset,
355 hidden: placement.hidden,
356 }
357 }
358 fn paint(
359 &mut self,
360 _: Option<&GlobalElementId>,
361 _: Option<&InspectorElementId>,
362 bounds: Bounds<Pixels>,
363 _: &mut (),
364 painted: &mut Painted,
365 window: &mut Window,
366 cx: &mut App,
367 ) {
368 let origin = painted.origin;
369 let pressable = Bounds::new(origin, bounds.size);
370 window.on_mouse_event(move |event: &MouseDownEvent, phase, _, cx| {
373 if phase == DispatchPhase::Bubble
374 && event.button == MouseButton::Left
375 && pressable.contains(&event.position)
376 {
377 cx.stop_propagation();
378 }
379 });
380 if let Some(child) = self.child.as_mut()
381 && !painted.hidden
382 {
383 child.paint(window, cx);
384 }
385 let domain = self.domain.clone();
386 let item = self.item.clone();
387 window.on_mouse_event(move |event: &MouseDownEvent, phase, _, _| {
388 if phase == DispatchPhase::Capture
389 && event.button == MouseButton::Left
390 && pressable.contains(&event.position)
391 {
392 domain.0.borrow_mut().press = Some((item.clone(), event.position - origin));
393 }
394 });
395 }
396}