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