interactive.rs

  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>;