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