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