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