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