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