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