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