1use crate::{
2 point, px, view, Action, AnyBox, AnyDrag, AppContext, BorrowWindow, Bounds, DispatchContext,
3 DispatchPhase, Element, ElementId, FocusHandle, KeyMatch, Keystroke, Modifiers, Overflow,
4 Pixels, Point, SharedString, Size, Style, StyleRefinement, ViewContext,
5};
6use collections::HashMap;
7use derive_more::{Deref, DerefMut};
8use parking_lot::Mutex;
9use refineable::Refineable;
10use smallvec::SmallVec;
11use std::{
12 any::{Any, TypeId},
13 fmt::Debug,
14 marker::PhantomData,
15 ops::Deref,
16 path::PathBuf,
17 sync::Arc,
18};
19
20const DRAG_THRESHOLD: f64 = 2.;
21
22pub trait StatelessInteractive: Element {
23 fn stateless_interaction(&mut self) -> &mut StatelessInteraction<Self::ViewState>;
24
25 fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
26 where
27 Self: Sized,
28 {
29 self.stateless_interaction().hover_style = f(StyleRefinement::default());
30 self
31 }
32
33 fn group_hover(
34 mut self,
35 group_name: impl Into<SharedString>,
36 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
37 ) -> Self
38 where
39 Self: Sized,
40 {
41 self.stateless_interaction().group_hover_style = Some(GroupStyle {
42 group: group_name.into(),
43 style: f(StyleRefinement::default()),
44 });
45 self
46 }
47
48 fn on_mouse_down(
49 mut self,
50 button: MouseButton,
51 handler: impl Fn(&mut Self::ViewState, &MouseDownEvent, &mut ViewContext<Self::ViewState>)
52 + Send
53 + Sync
54 + 'static,
55 ) -> Self
56 where
57 Self: Sized,
58 {
59 self.stateless_interaction()
60 .mouse_down_listeners
61 .push(Arc::new(move |view, event, bounds, phase, cx| {
62 if phase == DispatchPhase::Bubble
63 && event.button == button
64 && bounds.contains_point(&event.position)
65 {
66 handler(view, event, cx)
67 }
68 }));
69 self
70 }
71
72 fn on_mouse_up(
73 mut self,
74 button: MouseButton,
75 handler: impl Fn(&mut Self::ViewState, &MouseUpEvent, &mut ViewContext<Self::ViewState>)
76 + Send
77 + Sync
78 + 'static,
79 ) -> Self
80 where
81 Self: Sized,
82 {
83 self.stateless_interaction()
84 .mouse_up_listeners
85 .push(Arc::new(move |view, event, bounds, phase, cx| {
86 if phase == DispatchPhase::Bubble
87 && event.button == button
88 && bounds.contains_point(&event.position)
89 {
90 handler(view, event, cx)
91 }
92 }));
93 self
94 }
95
96 fn on_mouse_down_out(
97 mut self,
98 button: MouseButton,
99 handler: impl Fn(&mut Self::ViewState, &MouseDownEvent, &mut ViewContext<Self::ViewState>)
100 + Send
101 + Sync
102 + 'static,
103 ) -> Self
104 where
105 Self: Sized,
106 {
107 self.stateless_interaction()
108 .mouse_down_listeners
109 .push(Arc::new(move |view, event, bounds, phase, cx| {
110 if phase == DispatchPhase::Capture
111 && event.button == button
112 && !bounds.contains_point(&event.position)
113 {
114 handler(view, event, cx)
115 }
116 }));
117 self
118 }
119
120 fn on_mouse_up_out(
121 mut self,
122 button: MouseButton,
123 handler: impl Fn(&mut Self::ViewState, &MouseUpEvent, &mut ViewContext<Self::ViewState>)
124 + Send
125 + Sync
126 + 'static,
127 ) -> Self
128 where
129 Self: Sized,
130 {
131 self.stateless_interaction()
132 .mouse_up_listeners
133 .push(Arc::new(move |view, event, bounds, phase, cx| {
134 if phase == DispatchPhase::Capture
135 && event.button == button
136 && !bounds.contains_point(&event.position)
137 {
138 handler(view, event, cx);
139 }
140 }));
141 self
142 }
143
144 fn on_mouse_move(
145 mut self,
146 handler: impl Fn(&mut Self::ViewState, &MouseMoveEvent, &mut ViewContext<Self::ViewState>)
147 + Send
148 + Sync
149 + 'static,
150 ) -> Self
151 where
152 Self: Sized,
153 {
154 self.stateless_interaction()
155 .mouse_move_listeners
156 .push(Arc::new(move |view, event, bounds, phase, cx| {
157 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
158 handler(view, event, cx);
159 }
160 }));
161 self
162 }
163
164 fn on_scroll_wheel(
165 mut self,
166 handler: impl Fn(&mut Self::ViewState, &ScrollWheelEvent, &mut ViewContext<Self::ViewState>)
167 + Send
168 + Sync
169 + 'static,
170 ) -> Self
171 where
172 Self: Sized,
173 {
174 self.stateless_interaction()
175 .scroll_wheel_listeners
176 .push(Arc::new(move |view, event, bounds, phase, cx| {
177 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
178 handler(view, event, cx);
179 }
180 }));
181 self
182 }
183
184 fn context<C>(mut self, context: C) -> Self
185 where
186 Self: Sized,
187 C: TryInto<DispatchContext>,
188 C::Error: Debug,
189 {
190 self.stateless_interaction().dispatch_context =
191 context.try_into().expect("invalid dispatch context");
192 self
193 }
194
195 fn on_action<A: 'static>(
196 mut self,
197 listener: impl Fn(&mut Self::ViewState, &A, DispatchPhase, &mut ViewContext<Self::ViewState>)
198 + Send
199 + Sync
200 + 'static,
201 ) -> Self
202 where
203 Self: Sized,
204 {
205 self.stateless_interaction().key_listeners.push((
206 TypeId::of::<A>(),
207 Arc::new(move |view, event, _, phase, cx| {
208 let event = event.downcast_ref().unwrap();
209 listener(view, event, phase, cx);
210 None
211 }),
212 ));
213 self
214 }
215
216 fn on_key_down(
217 mut self,
218 listener: impl Fn(
219 &mut Self::ViewState,
220 &KeyDownEvent,
221 DispatchPhase,
222 &mut ViewContext<Self::ViewState>,
223 ) + Send
224 + Sync
225 + 'static,
226 ) -> Self
227 where
228 Self: Sized,
229 {
230 self.stateless_interaction().key_listeners.push((
231 TypeId::of::<KeyDownEvent>(),
232 Arc::new(move |view, event, _, phase, cx| {
233 let event = event.downcast_ref().unwrap();
234 listener(view, event, phase, cx);
235 None
236 }),
237 ));
238 self
239 }
240
241 fn on_key_up(
242 mut self,
243 listener: impl Fn(&mut Self::ViewState, &KeyUpEvent, DispatchPhase, &mut ViewContext<Self::ViewState>)
244 + Send
245 + Sync
246 + 'static,
247 ) -> Self
248 where
249 Self: Sized,
250 {
251 self.stateless_interaction().key_listeners.push((
252 TypeId::of::<KeyUpEvent>(),
253 Arc::new(move |view, event, _, phase, cx| {
254 let event = event.downcast_ref().unwrap();
255 listener(view, event, phase, cx);
256 None
257 }),
258 ));
259 self
260 }
261
262 fn drag_over<S: 'static>(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
263 where
264 Self: Sized,
265 {
266 self.stateless_interaction()
267 .drag_over_styles
268 .push((TypeId::of::<S>(), f(StyleRefinement::default())));
269 self
270 }
271
272 fn group_drag_over<S: 'static>(
273 mut self,
274 group_name: impl Into<SharedString>,
275 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
276 ) -> Self
277 where
278 Self: Sized,
279 {
280 self.stateless_interaction().group_drag_over_styles.push((
281 TypeId::of::<S>(),
282 GroupStyle {
283 group: group_name.into(),
284 style: f(StyleRefinement::default()),
285 },
286 ));
287 self
288 }
289
290 fn on_drop<S: 'static>(
291 mut self,
292 listener: impl Fn(&mut Self::ViewState, S, &mut ViewContext<Self::ViewState>)
293 + Send
294 + Sync
295 + 'static,
296 ) -> Self
297 where
298 Self: Sized,
299 {
300 self.stateless_interaction().drop_listeners.push((
301 TypeId::of::<S>(),
302 Arc::new(move |view, drag_state, cx| {
303 listener(view, *drag_state.downcast().unwrap(), cx);
304 }),
305 ));
306 self
307 }
308}
309
310pub trait StatefulInteractive: StatelessInteractive {
311 fn stateful_interaction(&mut self) -> &mut StatefulInteraction<Self::ViewState>;
312
313 fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
314 where
315 Self: Sized,
316 {
317 self.stateful_interaction().active_style = f(StyleRefinement::default());
318 self
319 }
320
321 fn group_active(
322 mut self,
323 group_name: impl Into<SharedString>,
324 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
325 ) -> Self
326 where
327 Self: Sized,
328 {
329 self.stateful_interaction().group_active_style = Some(GroupStyle {
330 group: group_name.into(),
331 style: f(StyleRefinement::default()),
332 });
333 self
334 }
335
336 fn on_click(
337 mut self,
338 listener: impl Fn(&mut Self::ViewState, &ClickEvent, &mut ViewContext<Self::ViewState>)
339 + Send
340 + Sync
341 + 'static,
342 ) -> Self
343 where
344 Self: Sized,
345 {
346 self.stateful_interaction()
347 .click_listeners
348 .push(Arc::new(move |view, event, cx| listener(view, event, cx)));
349 self
350 }
351
352 fn on_drag<S, R, E>(
353 mut self,
354 listener: impl Fn(
355 &mut Self::ViewState,
356 &mut ViewContext<Self::ViewState>,
357 ) -> Drag<S, R, Self::ViewState, E>
358 + Send
359 + Sync
360 + 'static,
361 ) -> Self
362 where
363 Self: Sized,
364 S: Any + Send + Sync,
365 R: Fn(&mut Self::ViewState, &mut ViewContext<Self::ViewState>) -> E,
366 R: 'static + Send + Sync,
367 E: Element<ViewState = Self::ViewState>,
368 {
369 debug_assert!(
370 self.stateful_interaction().drag_listener.is_none(),
371 "calling on_drag more than once on the same element is not supported"
372 );
373 self.stateful_interaction().drag_listener =
374 Some(Arc::new(move |view_state, cursor_offset, cx| {
375 let drag = listener(view_state, cx);
376 let view_handle = cx.handle().upgrade().unwrap();
377 let drag_handle_view = Some(
378 view(view_handle, move |view_state, cx| {
379 (drag.render_drag_handle)(view_state, cx)
380 })
381 .into_any(),
382 );
383 AnyDrag {
384 drag_handle_view,
385 cursor_offset,
386 state: Box::new(drag.state),
387 state_type: TypeId::of::<S>(),
388 }
389 }));
390 self
391 }
392}
393
394pub trait ElementInteraction<V: 'static>: 'static + Send + Sync {
395 fn as_stateless(&self) -> &StatelessInteraction<V>;
396 fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V>;
397 fn as_stateful(&self) -> Option<&StatefulInteraction<V>>;
398 fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>>;
399
400 fn initialize<R>(
401 &mut self,
402 cx: &mut ViewContext<V>,
403 f: impl FnOnce(&mut ViewContext<V>) -> R,
404 ) -> R {
405 if let Some(stateful) = self.as_stateful_mut() {
406 cx.with_element_id(stateful.id.clone(), |global_id, cx| {
407 stateful.key_listeners.push((
408 TypeId::of::<KeyDownEvent>(),
409 Arc::new(move |_, key_down, context, phase, cx| {
410 if phase == DispatchPhase::Bubble {
411 let key_down = key_down.downcast_ref::<KeyDownEvent>().unwrap();
412 if let KeyMatch::Some(action) =
413 cx.match_keystroke(&global_id, &key_down.keystroke, context)
414 {
415 return Some(action);
416 }
417 }
418
419 None
420 }),
421 ));
422 let result = stateful.stateless.initialize(cx, f);
423 stateful.key_listeners.pop();
424 result
425 })
426 } else {
427 let stateless = self.as_stateless();
428 cx.with_key_dispatch_context(stateless.dispatch_context.clone(), |cx| {
429 cx.with_key_listeners(&stateless.key_listeners, f)
430 })
431 }
432 }
433
434 fn refine_style(
435 &self,
436 style: &mut Style,
437 bounds: Bounds<Pixels>,
438 element_state: &InteractiveElementState,
439 cx: &mut ViewContext<V>,
440 ) {
441 let mouse_position = cx.mouse_position();
442 let stateless = self.as_stateless();
443 if let Some(group_hover) = stateless.group_hover_style.as_ref() {
444 if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
445 if group_bounds.contains_point(&mouse_position) {
446 style.refine(&group_hover.style);
447 }
448 }
449 }
450 if bounds.contains_point(&mouse_position) {
451 style.refine(&stateless.hover_style);
452 }
453
454 if let Some(drag) = cx.active_drag.take() {
455 for (state_type, group_drag_style) in &self.as_stateless().group_drag_over_styles {
456 if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) {
457 if *state_type == drag.state_type
458 && group_bounds.contains_point(&mouse_position)
459 {
460 style.refine(&group_drag_style.style);
461 }
462 }
463 }
464
465 for (state_type, drag_over_style) in &self.as_stateless().drag_over_styles {
466 if *state_type == drag.state_type && bounds.contains_point(&mouse_position) {
467 style.refine(drag_over_style);
468 }
469 }
470
471 cx.active_drag = Some(drag);
472 }
473
474 if let Some(stateful) = self.as_stateful() {
475 let active_state = element_state.active_state.lock();
476 if active_state.group {
477 if let Some(group_style) = stateful.group_active_style.as_ref() {
478 style.refine(&group_style.style);
479 }
480 }
481 if active_state.element {
482 style.refine(&stateful.active_style);
483 }
484 }
485 }
486
487 fn paint(
488 &mut self,
489 bounds: Bounds<Pixels>,
490 content_size: Size<Pixels>,
491 overflow: Point<Overflow>,
492 element_state: &mut InteractiveElementState,
493 cx: &mut ViewContext<V>,
494 ) {
495 let stateless = self.as_stateless();
496 for listener in stateless.mouse_down_listeners.iter().cloned() {
497 cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
498 listener(state, event, &bounds, phase, cx);
499 })
500 }
501
502 for listener in stateless.mouse_up_listeners.iter().cloned() {
503 cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
504 listener(state, event, &bounds, phase, cx);
505 })
506 }
507
508 for listener in stateless.mouse_move_listeners.iter().cloned() {
509 cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
510 listener(state, event, &bounds, phase, cx);
511 })
512 }
513
514 for listener in stateless.scroll_wheel_listeners.iter().cloned() {
515 cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
516 listener(state, event, &bounds, phase, cx);
517 })
518 }
519
520 let hover_group_bounds = stateless
521 .group_hover_style
522 .as_ref()
523 .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
524
525 if let Some(group_bounds) = hover_group_bounds {
526 let hovered = group_bounds.contains_point(&cx.mouse_position());
527 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
528 if phase == DispatchPhase::Capture {
529 if group_bounds.contains_point(&event.position) != hovered {
530 cx.notify();
531 }
532 }
533 });
534 }
535
536 if stateless.hover_style.is_some()
537 || (cx.active_drag.is_some() && !stateless.drag_over_styles.is_empty())
538 {
539 let hovered = bounds.contains_point(&cx.mouse_position());
540 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
541 if phase == DispatchPhase::Capture {
542 if bounds.contains_point(&event.position) != hovered {
543 cx.notify();
544 }
545 }
546 });
547 }
548
549 if cx.active_drag.is_some() {
550 let drop_listeners = stateless.drop_listeners.clone();
551 cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| {
552 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
553 if let Some(drag_state_type) =
554 cx.active_drag.as_ref().map(|drag| drag.state_type)
555 {
556 for (drop_state_type, listener) in &drop_listeners {
557 if *drop_state_type == drag_state_type {
558 let drag = cx
559 .active_drag
560 .take()
561 .expect("checked for type drag state type above");
562 listener(view, drag.state, cx);
563 cx.notify();
564 cx.stop_propagation();
565 }
566 }
567 }
568 }
569 });
570 }
571
572 if let Some(stateful) = self.as_stateful() {
573 let click_listeners = stateful.click_listeners.clone();
574 let drag_listener = stateful.drag_listener.clone();
575
576 if !click_listeners.is_empty() || drag_listener.is_some() {
577 let pending_mouse_down = element_state.pending_mouse_down.clone();
578 let mouse_down = pending_mouse_down.lock().clone();
579 if let Some(mouse_down) = mouse_down {
580 if let Some(drag_listener) = drag_listener {
581 let active_state = element_state.active_state.clone();
582
583 cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
584 if cx.active_drag.is_some() {
585 if phase == DispatchPhase::Capture {
586 cx.notify();
587 }
588 } else if phase == DispatchPhase::Bubble
589 && bounds.contains_point(&event.position)
590 && (event.position - mouse_down.position).magnitude()
591 > DRAG_THRESHOLD
592 {
593 *active_state.lock() = ActiveState::default();
594 let cursor_offset = event.position - bounds.origin;
595 let drag = drag_listener(view_state, cursor_offset, cx);
596 cx.active_drag = Some(drag);
597 cx.notify();
598 cx.stop_propagation();
599 }
600 });
601 }
602
603 cx.on_mouse_event(move |view_state, event: &MouseUpEvent, phase, cx| {
604 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position)
605 {
606 let mouse_click = ClickEvent {
607 down: mouse_down.clone(),
608 up: event.clone(),
609 };
610 for listener in &click_listeners {
611 listener(view_state, &mouse_click, cx);
612 }
613 }
614 *pending_mouse_down.lock() = None;
615 });
616 } else {
617 cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| {
618 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position)
619 {
620 *pending_mouse_down.lock() = Some(event.clone());
621 }
622 });
623 }
624 }
625
626 let active_state = element_state.active_state.clone();
627 if active_state.lock().is_none() {
628 let active_group_bounds = stateful
629 .group_active_style
630 .as_ref()
631 .and_then(|group_active| GroupBounds::get(&group_active.group, cx));
632 cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
633 if phase == DispatchPhase::Bubble {
634 let group = active_group_bounds
635 .map_or(false, |bounds| bounds.contains_point(&down.position));
636 let element = bounds.contains_point(&down.position);
637 if group || element {
638 *active_state.lock() = ActiveState { group, element };
639 cx.notify();
640 }
641 }
642 });
643 } else {
644 cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
645 if phase == DispatchPhase::Capture {
646 *active_state.lock() = ActiveState::default();
647 cx.notify();
648 }
649 });
650 }
651
652 if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
653 let scroll_offset = element_state
654 .scroll_offset
655 .get_or_insert_with(Arc::default)
656 .clone();
657 let line_height = cx.line_height();
658 let scroll_max = (content_size - bounds.size).max(&Size::default());
659
660 cx.on_mouse_event(move |_, event: &ScrollWheelEvent, phase, cx| {
661 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
662 let mut scroll_offset = scroll_offset.lock();
663 let old_scroll_offset = *scroll_offset;
664 let delta = event.delta.pixel_delta(line_height);
665
666 if overflow.x == Overflow::Scroll {
667 scroll_offset.x =
668 (scroll_offset.x + delta.x).clamp(-scroll_max.width, px(0.));
669 }
670
671 if overflow.y == Overflow::Scroll {
672 scroll_offset.y =
673 (scroll_offset.y + delta.y).clamp(-scroll_max.height, px(0.));
674 }
675
676 if *scroll_offset != old_scroll_offset {
677 cx.notify();
678 cx.stop_propagation();
679 }
680 }
681 });
682 }
683 }
684 }
685}
686
687#[derive(Deref, DerefMut)]
688pub struct StatefulInteraction<V> {
689 pub id: ElementId,
690 #[deref]
691 #[deref_mut]
692 stateless: StatelessInteraction<V>,
693 click_listeners: SmallVec<[ClickListener<V>; 2]>,
694 active_style: StyleRefinement,
695 group_active_style: Option<GroupStyle>,
696 drag_listener: Option<DragListener<V>>,
697}
698
699impl<V: 'static> ElementInteraction<V> for StatefulInteraction<V> {
700 fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
701 Some(self)
702 }
703
704 fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
705 Some(self)
706 }
707
708 fn as_stateless(&self) -> &StatelessInteraction<V> {
709 &self.stateless
710 }
711
712 fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
713 &mut self.stateless
714 }
715}
716
717impl<V> From<ElementId> for StatefulInteraction<V> {
718 fn from(id: ElementId) -> Self {
719 Self {
720 id,
721 stateless: StatelessInteraction::default(),
722 click_listeners: SmallVec::new(),
723 drag_listener: None,
724 active_style: StyleRefinement::default(),
725 group_active_style: None,
726 }
727 }
728}
729
730type DropListener<V> = dyn Fn(&mut V, AnyBox, &mut ViewContext<V>) + 'static + Send + Sync;
731
732pub struct StatelessInteraction<V> {
733 pub dispatch_context: DispatchContext,
734 pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
735 pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
736 pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
737 pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
738 pub key_listeners: SmallVec<[(TypeId, KeyListener<V>); 32]>,
739 pub hover_style: StyleRefinement,
740 pub group_hover_style: Option<GroupStyle>,
741 drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
742 group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
743 drop_listeners: SmallVec<[(TypeId, Arc<DropListener<V>>); 2]>,
744}
745
746impl<V> StatelessInteraction<V> {
747 pub fn into_stateful(self, id: impl Into<ElementId>) -> StatefulInteraction<V> {
748 StatefulInteraction {
749 id: id.into(),
750 stateless: self,
751 click_listeners: SmallVec::new(),
752 drag_listener: None,
753 active_style: StyleRefinement::default(),
754 group_active_style: None,
755 }
756 }
757}
758
759pub struct GroupStyle {
760 pub group: SharedString,
761 pub style: StyleRefinement,
762}
763
764#[derive(Default)]
765pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
766
767impl GroupBounds {
768 pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
769 cx.default_global::<Self>()
770 .0
771 .get(name)
772 .and_then(|bounds_stack| bounds_stack.last())
773 .cloned()
774 }
775
776 pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
777 cx.default_global::<Self>()
778 .0
779 .entry(name)
780 .or_default()
781 .push(bounds);
782 }
783
784 pub fn pop(name: &SharedString, cx: &mut AppContext) {
785 cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
786 }
787}
788
789#[derive(Copy, Clone, Default, Eq, PartialEq)]
790struct ActiveState {
791 pub group: bool,
792 pub element: bool,
793}
794
795impl ActiveState {
796 pub fn is_none(&self) -> bool {
797 !self.group && !self.element
798 }
799}
800
801#[derive(Default)]
802pub struct InteractiveElementState {
803 active_state: Arc<Mutex<ActiveState>>,
804 pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
805 scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
806}
807
808impl InteractiveElementState {
809 pub fn scroll_offset(&self) -> Option<Point<Pixels>> {
810 self.scroll_offset
811 .as_ref()
812 .map(|offset| offset.lock().clone())
813 }
814}
815
816impl<V> Default for StatelessInteraction<V> {
817 fn default() -> Self {
818 Self {
819 dispatch_context: DispatchContext::default(),
820 mouse_down_listeners: SmallVec::new(),
821 mouse_up_listeners: SmallVec::new(),
822 mouse_move_listeners: SmallVec::new(),
823 scroll_wheel_listeners: SmallVec::new(),
824 key_listeners: SmallVec::new(),
825 hover_style: StyleRefinement::default(),
826 group_hover_style: None,
827 drag_over_styles: SmallVec::new(),
828 group_drag_over_styles: SmallVec::new(),
829 drop_listeners: SmallVec::new(),
830 }
831 }
832}
833
834impl<V: 'static> ElementInteraction<V> for StatelessInteraction<V> {
835 fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
836 None
837 }
838
839 fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
840 None
841 }
842
843 fn as_stateless(&self) -> &StatelessInteraction<V> {
844 self
845 }
846
847 fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
848 self
849 }
850}
851
852#[derive(Clone, Debug, Eq, PartialEq)]
853pub struct KeyDownEvent {
854 pub keystroke: Keystroke,
855 pub is_held: bool,
856}
857
858#[derive(Clone, Debug)]
859pub struct KeyUpEvent {
860 pub keystroke: Keystroke,
861}
862
863#[derive(Clone, Debug, Default)]
864pub struct ModifiersChangedEvent {
865 pub modifiers: Modifiers,
866}
867
868impl Deref for ModifiersChangedEvent {
869 type Target = Modifiers;
870
871 fn deref(&self) -> &Self::Target {
872 &self.modifiers
873 }
874}
875
876/// The phase of a touch motion event.
877/// Based on the winit enum of the same name.
878#[derive(Clone, Copy, Debug)]
879pub enum TouchPhase {
880 Started,
881 Moved,
882 Ended,
883}
884
885#[derive(Clone, Debug, Default)]
886pub struct MouseDownEvent {
887 pub button: MouseButton,
888 pub position: Point<Pixels>,
889 pub modifiers: Modifiers,
890 pub click_count: usize,
891}
892
893#[derive(Clone, Debug, Default)]
894pub struct MouseUpEvent {
895 pub button: MouseButton,
896 pub position: Point<Pixels>,
897 pub modifiers: Modifiers,
898 pub click_count: usize,
899}
900
901#[derive(Clone, Debug, Default)]
902pub struct ClickEvent {
903 pub down: MouseDownEvent,
904 pub up: MouseUpEvent,
905}
906
907pub struct Drag<S, R, V, E>
908where
909 R: Fn(&mut V, &mut ViewContext<V>) -> E,
910 E: Element<ViewState = V>,
911{
912 pub state: S,
913 pub render_drag_handle: R,
914 view_type: PhantomData<V>,
915}
916
917impl<S, R, V, E> Drag<S, R, V, E>
918where
919 R: Fn(&mut V, &mut ViewContext<V>) -> E,
920 E: Element<ViewState = V>,
921{
922 pub fn new(state: S, render_drag_handle: R) -> Self {
923 Drag {
924 state,
925 render_drag_handle,
926 view_type: PhantomData,
927 }
928 }
929}
930
931#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
932pub enum MouseButton {
933 Left,
934 Right,
935 Middle,
936 Navigate(NavigationDirection),
937}
938
939impl MouseButton {
940 pub fn all() -> Vec<Self> {
941 vec![
942 MouseButton::Left,
943 MouseButton::Right,
944 MouseButton::Middle,
945 MouseButton::Navigate(NavigationDirection::Back),
946 MouseButton::Navigate(NavigationDirection::Forward),
947 ]
948 }
949}
950
951impl Default for MouseButton {
952 fn default() -> Self {
953 Self::Left
954 }
955}
956
957#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
958pub enum NavigationDirection {
959 Back,
960 Forward,
961}
962
963impl Default for NavigationDirection {
964 fn default() -> Self {
965 Self::Back
966 }
967}
968
969#[derive(Clone, Debug, Default)]
970pub struct MouseMoveEvent {
971 pub position: Point<Pixels>,
972 pub pressed_button: Option<MouseButton>,
973 pub modifiers: Modifiers,
974}
975
976#[derive(Clone, Debug)]
977pub struct ScrollWheelEvent {
978 pub position: Point<Pixels>,
979 pub delta: ScrollDelta,
980 pub modifiers: Modifiers,
981 pub touch_phase: TouchPhase,
982}
983
984impl Deref for ScrollWheelEvent {
985 type Target = Modifiers;
986
987 fn deref(&self) -> &Self::Target {
988 &self.modifiers
989 }
990}
991
992#[derive(Clone, Copy, Debug)]
993pub enum ScrollDelta {
994 Pixels(Point<Pixels>),
995 Lines(Point<f32>),
996}
997
998impl Default for ScrollDelta {
999 fn default() -> Self {
1000 Self::Lines(Default::default())
1001 }
1002}
1003
1004impl ScrollDelta {
1005 pub fn precise(&self) -> bool {
1006 match self {
1007 ScrollDelta::Pixels(_) => true,
1008 ScrollDelta::Lines(_) => false,
1009 }
1010 }
1011
1012 pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
1013 match self {
1014 ScrollDelta::Pixels(delta) => *delta,
1015 ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
1016 }
1017 }
1018}
1019
1020#[derive(Clone, Debug, Default)]
1021pub struct MouseExitEvent {
1022 pub position: Point<Pixels>,
1023 pub pressed_button: Option<MouseButton>,
1024 pub modifiers: Modifiers,
1025}
1026
1027impl Deref for MouseExitEvent {
1028 type Target = Modifiers;
1029
1030 fn deref(&self) -> &Self::Target {
1031 &self.modifiers
1032 }
1033}
1034
1035#[derive(Debug, Clone, Default)]
1036pub struct ExternalPaths(pub(crate) SmallVec<[PathBuf; 2]>);
1037
1038#[derive(Debug, Clone)]
1039pub enum FileDropEvent {
1040 Entered {
1041 position: Point<Pixels>,
1042 files: ExternalPaths,
1043 },
1044 Pending {
1045 position: Point<Pixels>,
1046 },
1047 Submit {
1048 position: Point<Pixels>,
1049 },
1050 Exited,
1051}
1052
1053#[derive(Clone, Debug)]
1054pub enum InputEvent {
1055 KeyDown(KeyDownEvent),
1056 KeyUp(KeyUpEvent),
1057 ModifiersChanged(ModifiersChangedEvent),
1058 MouseDown(MouseDownEvent),
1059 MouseUp(MouseUpEvent),
1060 MouseMove(MouseMoveEvent),
1061 MouseExited(MouseExitEvent),
1062 ScrollWheel(ScrollWheelEvent),
1063 FileDrop(FileDropEvent),
1064}
1065
1066impl InputEvent {
1067 pub fn position(&self) -> Option<Point<Pixels>> {
1068 match self {
1069 InputEvent::KeyDown { .. } => None,
1070 InputEvent::KeyUp { .. } => None,
1071 InputEvent::ModifiersChanged { .. } => None,
1072 InputEvent::MouseDown(event) => Some(event.position),
1073 InputEvent::MouseUp(event) => Some(event.position),
1074 InputEvent::MouseMove(event) => Some(event.position),
1075 InputEvent::MouseExited(event) => Some(event.position),
1076 InputEvent::ScrollWheel(event) => Some(event.position),
1077 InputEvent::FileDrop(FileDropEvent::Exited) => None,
1078 InputEvent::FileDrop(
1079 FileDropEvent::Entered { position, .. }
1080 | FileDropEvent::Pending { position, .. }
1081 | FileDropEvent::Submit { position, .. },
1082 ) => Some(*position),
1083 }
1084 }
1085
1086 pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
1087 match self {
1088 InputEvent::KeyDown { .. } => None,
1089 InputEvent::KeyUp { .. } => None,
1090 InputEvent::ModifiersChanged { .. } => None,
1091 InputEvent::MouseDown(event) => Some(event),
1092 InputEvent::MouseUp(event) => Some(event),
1093 InputEvent::MouseMove(event) => Some(event),
1094 InputEvent::MouseExited(event) => Some(event),
1095 InputEvent::ScrollWheel(event) => Some(event),
1096 InputEvent::FileDrop(event) => Some(event),
1097 }
1098 }
1099
1100 pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> {
1101 match self {
1102 InputEvent::KeyDown(event) => Some(event),
1103 InputEvent::KeyUp(event) => Some(event),
1104 InputEvent::ModifiersChanged(event) => Some(event),
1105 InputEvent::MouseDown(_) => None,
1106 InputEvent::MouseUp(_) => None,
1107 InputEvent::MouseMove(_) => None,
1108 InputEvent::MouseExited(_) => None,
1109 InputEvent::ScrollWheel(_) => None,
1110 InputEvent::FileDrop(_) => None,
1111 }
1112 }
1113}
1114
1115pub struct FocusEvent {
1116 pub blurred: Option<FocusHandle>,
1117 pub focused: Option<FocusHandle>,
1118}
1119
1120pub type MouseDownListener<V> = Arc<
1121 dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1122 + Send
1123 + Sync
1124 + 'static,
1125>;
1126pub type MouseUpListener<V> = Arc<
1127 dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1128 + Send
1129 + Sync
1130 + 'static,
1131>;
1132
1133pub type MouseMoveListener<V> = Arc<
1134 dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1135 + Send
1136 + Sync
1137 + 'static,
1138>;
1139
1140pub type ScrollWheelListener<V> = Arc<
1141 dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1142 + Send
1143 + Sync
1144 + 'static,
1145>;
1146
1147pub type ClickListener<V> =
1148 Arc<dyn Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
1149
1150pub(crate) type DragListener<V> =
1151 Arc<dyn Fn(&mut V, Point<Pixels>, &mut ViewContext<V>) -> AnyDrag + Send + Sync + 'static>;
1152
1153pub type KeyListener<V> = Arc<
1154 dyn Fn(
1155 &mut V,
1156 &dyn Any,
1157 &[&DispatchContext],
1158 DispatchPhase,
1159 &mut ViewContext<V>,
1160 ) -> Option<Box<dyn Action>>
1161 + Send
1162 + Sync
1163 + 'static,
1164>;