interactive.rs

  1use crate::{
  2    point, Action, AppContext, BorrowWindow, Bounds, DispatchContext, DispatchPhase, Element,
  3    ElementId, FocusHandle, KeyMatch, Keystroke, Modifiers, Pixels, Point, SharedString, Style,
  4    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(
309        &mut self,
310        element_state: Option<InteractiveElementState>,
311        focus_handle: Option<FocusHandle>,
312        cx: &mut ViewContext<V>,
313        f: impl FnOnce(&mut ViewContext<V>),
314    ) -> InteractiveElementState {
315        if let Some(stateful) = self.as_stateful_mut() {
316            cx.with_element_id(stateful.id.clone(), |global_id, cx| {
317                stateful.key_listeners.push((
318                    TypeId::of::<KeyDownEvent>(),
319                    Arc::new(move |_, key_down, context, phase, cx| {
320                        if phase == DispatchPhase::Bubble {
321                            let key_down = key_down.downcast_ref::<KeyDownEvent>().unwrap();
322                            if let KeyMatch::Some(action) =
323                                cx.match_keystroke(&global_id, &key_down.keystroke, context)
324                            {
325                                return Some(action);
326                            }
327                        }
328
329                        None
330                    }),
331                ));
332                let mut element_state = stateful.stateless.initialize(element_state, None, cx, f);
333                element_state.focus_handle = focus_handle
334                    .or(element_state.focus_handle.take())
335                    .or_else(|| cx.focused());
336                stateful.key_listeners.pop();
337                element_state
338            })
339        } else {
340            let stateless = self.as_stateless();
341            cx.with_key_dispatch_context(stateless.dispatch_context.clone(), |cx| {
342                cx.with_key_listeners(&stateless.key_listeners, f)
343            });
344            element_state.unwrap_or_default()
345        }
346    }
347
348    fn refine_style(
349        &self,
350        style: &mut Style,
351        bounds: Bounds<Pixels>,
352        element_state: &InteractiveElementState,
353        cx: &mut ViewContext<V>,
354    ) {
355        let mouse_position = cx.mouse_position();
356        let stateless = self.as_stateless();
357        if let Some(group_hover) = stateless.group_hover_style.as_ref() {
358            if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
359                if group_bounds.contains_point(&mouse_position) {
360                    style.refine(&group_hover.style);
361                }
362            }
363        }
364        if bounds.contains_point(&mouse_position) {
365            style.refine(&stateless.hover_style);
366        }
367
368        if let Some(stateful) = self.as_stateful() {
369            let active_state = element_state.active_state.lock();
370            if active_state.group {
371                if let Some(group_style) = stateful.group_active_style.as_ref() {
372                    style.refine(&group_style.style);
373                }
374            }
375            if active_state.element {
376                style.refine(&stateful.active_style);
377            }
378        }
379    }
380
381    fn paint(
382        &mut self,
383        bounds: Bounds<Pixels>,
384        element_state: &InteractiveElementState,
385        cx: &mut ViewContext<V>,
386    ) {
387        let stateless = self.as_stateless();
388        for listener in stateless.mouse_down_listeners.iter().cloned() {
389            cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
390                listener(state, event, &bounds, phase, cx);
391            })
392        }
393
394        for listener in stateless.mouse_up_listeners.iter().cloned() {
395            cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
396                listener(state, event, &bounds, phase, cx);
397            })
398        }
399
400        for listener in stateless.mouse_move_listeners.iter().cloned() {
401            cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
402                listener(state, event, &bounds, phase, cx);
403            })
404        }
405
406        for listener in stateless.scroll_wheel_listeners.iter().cloned() {
407            cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
408                listener(state, event, &bounds, phase, cx);
409            })
410        }
411
412        let hover_group_bounds = stateless
413            .group_hover_style
414            .as_ref()
415            .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
416
417        if let Some(group_bounds) = hover_group_bounds {
418            paint_hover_listener(group_bounds, cx);
419        }
420
421        if stateless.hover_style.is_some() {
422            paint_hover_listener(bounds, cx);
423        }
424
425        if let Some(stateful) = self.as_stateful() {
426            let click_listeners = stateful.mouse_click_listeners.clone();
427
428            let pending_click = element_state.pending_click.clone();
429            let mouse_down = pending_click.lock().clone();
430            if let Some(mouse_down) = mouse_down {
431                cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
432                    if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
433                        let mouse_click = MouseClickEvent {
434                            down: mouse_down.clone(),
435                            up: event.clone(),
436                        };
437                        for listener in &click_listeners {
438                            listener(state, &mouse_click, cx);
439                        }
440                    }
441
442                    *pending_click.lock() = None;
443                });
444            } else {
445                cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| {
446                    if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
447                        *pending_click.lock() = Some(event.clone());
448                    }
449                });
450            }
451
452            let active_state = element_state.active_state.clone();
453            if active_state.lock().is_none() {
454                let active_group_bounds = stateful
455                    .group_active_style
456                    .as_ref()
457                    .and_then(|group_active| GroupBounds::get(&group_active.group, cx));
458                cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
459                    if phase == DispatchPhase::Bubble {
460                        let group = active_group_bounds
461                            .map_or(false, |bounds| bounds.contains_point(&down.position));
462                        let element = bounds.contains_point(&down.position);
463                        if group || element {
464                            *active_state.lock() = ActiveState { group, element };
465                            cx.notify();
466                        }
467                    }
468                });
469            } else {
470                cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
471                    if phase == DispatchPhase::Capture {
472                        *active_state.lock() = ActiveState::default();
473                        cx.notify();
474                    }
475                });
476            }
477        }
478    }
479}
480
481fn paint_hover_listener<V>(bounds: Bounds<Pixels>, cx: &mut ViewContext<V>)
482where
483    V: 'static + Send + Sync,
484{
485    let hovered = bounds.contains_point(&cx.mouse_position());
486    cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
487        if phase == DispatchPhase::Capture {
488            if bounds.contains_point(&event.position) != hovered {
489                cx.notify();
490            }
491        }
492    });
493}
494
495#[derive(Deref, DerefMut)]
496pub struct StatefulInteraction<V: 'static + Send + Sync> {
497    pub id: ElementId,
498    #[deref]
499    #[deref_mut]
500    stateless: StatelessInteraction<V>,
501    pub mouse_click_listeners: SmallVec<[MouseClickListener<V>; 2]>,
502    pub active_style: StyleRefinement,
503    pub group_active_style: Option<GroupStyle>,
504}
505
506impl<V> ElementInteraction<V> for StatefulInteraction<V>
507where
508    V: 'static + Send + Sync,
509{
510    fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
511        Some(self)
512    }
513
514    fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
515        Some(self)
516    }
517
518    fn as_stateless(&self) -> &StatelessInteraction<V> {
519        &self.stateless
520    }
521
522    fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
523        &mut self.stateless
524    }
525}
526
527impl<V> From<ElementId> for StatefulInteraction<V>
528where
529    V: 'static + Send + Sync,
530{
531    fn from(id: ElementId) -> Self {
532        Self {
533            id,
534            stateless: StatelessInteraction::default(),
535            mouse_click_listeners: SmallVec::new(),
536            active_style: StyleRefinement::default(),
537            group_active_style: None,
538        }
539    }
540}
541
542pub struct StatelessInteraction<V> {
543    pub dispatch_context: DispatchContext,
544    pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
545    pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
546    pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
547    pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
548    pub key_listeners: SmallVec<[(TypeId, KeyListener<V>); 32]>,
549    pub hover_style: StyleRefinement,
550    pub group_hover_style: Option<GroupStyle>,
551}
552
553impl<V> StatelessInteraction<V>
554where
555    V: 'static + Send + Sync,
556{
557    pub fn into_stateful(self, id: impl Into<ElementId>) -> StatefulInteraction<V> {
558        StatefulInteraction {
559            id: id.into(),
560            stateless: self,
561            mouse_click_listeners: SmallVec::new(),
562            active_style: StyleRefinement::default(),
563            group_active_style: None,
564        }
565    }
566}
567
568pub struct GroupStyle {
569    pub group: SharedString,
570    pub style: StyleRefinement,
571}
572
573#[derive(Default)]
574pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
575
576impl GroupBounds {
577    pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
578        cx.default_global::<Self>()
579            .0
580            .get(name)
581            .and_then(|bounds_stack| bounds_stack.last())
582            .cloned()
583    }
584
585    pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
586        cx.default_global::<Self>()
587            .0
588            .entry(name)
589            .or_default()
590            .push(bounds);
591    }
592
593    pub fn pop(name: &SharedString, cx: &mut AppContext) {
594        cx.default_global::<GroupBounds>()
595            .0
596            .get_mut(name)
597            .unwrap()
598            .pop();
599    }
600}
601
602#[derive(Copy, Clone, Default, Eq, PartialEq)]
603struct ActiveState {
604    pub group: bool,
605    pub element: bool,
606}
607
608impl ActiveState {
609    pub fn is_none(&self) -> bool {
610        !self.group && !self.element
611    }
612}
613
614#[derive(Default)]
615pub struct InteractiveElementState {
616    focus_handle: Option<FocusHandle>,
617    active_state: Arc<Mutex<ActiveState>>,
618    pending_click: Arc<Mutex<Option<MouseDownEvent>>>,
619}
620
621impl<V> Default for StatelessInteraction<V> {
622    fn default() -> Self {
623        Self {
624            dispatch_context: DispatchContext::default(),
625            mouse_down_listeners: SmallVec::new(),
626            mouse_up_listeners: SmallVec::new(),
627            mouse_move_listeners: SmallVec::new(),
628            scroll_wheel_listeners: SmallVec::new(),
629            key_listeners: SmallVec::new(),
630            hover_style: StyleRefinement::default(),
631            group_hover_style: None,
632        }
633    }
634}
635
636impl<V> ElementInteraction<V> for StatelessInteraction<V>
637where
638    V: 'static + Send + Sync,
639{
640    fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
641        None
642    }
643
644    fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
645        None
646    }
647
648    fn as_stateless(&self) -> &StatelessInteraction<V> {
649        self
650    }
651
652    fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
653        self
654    }
655}
656
657#[derive(Clone, Debug, Eq, PartialEq)]
658pub struct KeyDownEvent {
659    pub keystroke: Keystroke,
660    pub is_held: bool,
661}
662
663#[derive(Clone, Debug)]
664pub struct KeyUpEvent {
665    pub keystroke: Keystroke,
666}
667
668#[derive(Clone, Debug, Default)]
669pub struct ModifiersChangedEvent {
670    pub modifiers: Modifiers,
671}
672
673impl Deref for ModifiersChangedEvent {
674    type Target = Modifiers;
675
676    fn deref(&self) -> &Self::Target {
677        &self.modifiers
678    }
679}
680
681/// The phase of a touch motion event.
682/// Based on the winit enum of the same name.
683#[derive(Clone, Copy, Debug)]
684pub enum TouchPhase {
685    Started,
686    Moved,
687    Ended,
688}
689
690#[derive(Clone, Debug, Default)]
691pub struct MouseDownEvent {
692    pub button: MouseButton,
693    pub position: Point<Pixels>,
694    pub modifiers: Modifiers,
695    pub click_count: usize,
696}
697
698#[derive(Clone, Debug, Default)]
699pub struct MouseUpEvent {
700    pub button: MouseButton,
701    pub position: Point<Pixels>,
702    pub modifiers: Modifiers,
703    pub click_count: usize,
704}
705
706#[derive(Clone, Debug, Default)]
707pub struct MouseClickEvent {
708    pub down: MouseDownEvent,
709    pub up: MouseUpEvent,
710}
711
712#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
713pub enum MouseButton {
714    Left,
715    Right,
716    Middle,
717    Navigate(NavigationDirection),
718}
719
720impl MouseButton {
721    pub fn all() -> Vec<Self> {
722        vec![
723            MouseButton::Left,
724            MouseButton::Right,
725            MouseButton::Middle,
726            MouseButton::Navigate(NavigationDirection::Back),
727            MouseButton::Navigate(NavigationDirection::Forward),
728        ]
729    }
730}
731
732impl Default for MouseButton {
733    fn default() -> Self {
734        Self::Left
735    }
736}
737
738#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
739pub enum NavigationDirection {
740    Back,
741    Forward,
742}
743
744impl Default for NavigationDirection {
745    fn default() -> Self {
746        Self::Back
747    }
748}
749
750#[derive(Clone, Debug, Default)]
751pub struct MouseMoveEvent {
752    pub position: Point<Pixels>,
753    pub pressed_button: Option<MouseButton>,
754    pub modifiers: Modifiers,
755}
756
757#[derive(Clone, Debug)]
758pub struct ScrollWheelEvent {
759    pub position: Point<Pixels>,
760    pub delta: ScrollDelta,
761    pub modifiers: Modifiers,
762    pub touch_phase: TouchPhase,
763}
764
765impl Deref for ScrollWheelEvent {
766    type Target = Modifiers;
767
768    fn deref(&self) -> &Self::Target {
769        &self.modifiers
770    }
771}
772
773#[derive(Clone, Copy, Debug)]
774pub enum ScrollDelta {
775    Pixels(Point<Pixels>),
776    Lines(Point<f32>),
777}
778
779impl Default for ScrollDelta {
780    fn default() -> Self {
781        Self::Lines(Default::default())
782    }
783}
784
785impl ScrollDelta {
786    pub fn precise(&self) -> bool {
787        match self {
788            ScrollDelta::Pixels(_) => true,
789            ScrollDelta::Lines(_) => false,
790        }
791    }
792
793    pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
794        match self {
795            ScrollDelta::Pixels(delta) => *delta,
796            ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
797        }
798    }
799}
800
801#[derive(Clone, Debug, Default)]
802pub struct MouseExitEvent {
803    pub position: Point<Pixels>,
804    pub pressed_button: Option<MouseButton>,
805    pub modifiers: Modifiers,
806}
807
808impl Deref for MouseExitEvent {
809    type Target = Modifiers;
810
811    fn deref(&self) -> &Self::Target {
812        &self.modifiers
813    }
814}
815
816#[derive(Clone, Debug)]
817pub enum InputEvent {
818    KeyDown(KeyDownEvent),
819    KeyUp(KeyUpEvent),
820    ModifiersChanged(ModifiersChangedEvent),
821    MouseDown(MouseDownEvent),
822    MouseUp(MouseUpEvent),
823    MouseMoved(MouseMoveEvent),
824    MouseExited(MouseExitEvent),
825    ScrollWheel(ScrollWheelEvent),
826}
827
828impl InputEvent {
829    pub fn position(&self) -> Option<Point<Pixels>> {
830        match self {
831            InputEvent::KeyDown { .. } => None,
832            InputEvent::KeyUp { .. } => None,
833            InputEvent::ModifiersChanged { .. } => None,
834            InputEvent::MouseDown(event) => Some(event.position),
835            InputEvent::MouseUp(event) => Some(event.position),
836            InputEvent::MouseMoved(event) => Some(event.position),
837            InputEvent::MouseExited(event) => Some(event.position),
838            InputEvent::ScrollWheel(event) => Some(event.position),
839        }
840    }
841
842    pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
843        match self {
844            InputEvent::KeyDown { .. } => None,
845            InputEvent::KeyUp { .. } => None,
846            InputEvent::ModifiersChanged { .. } => None,
847            InputEvent::MouseDown(event) => Some(event),
848            InputEvent::MouseUp(event) => Some(event),
849            InputEvent::MouseMoved(event) => Some(event),
850            InputEvent::MouseExited(event) => Some(event),
851            InputEvent::ScrollWheel(event) => Some(event),
852        }
853    }
854
855    pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> {
856        match self {
857            InputEvent::KeyDown(event) => Some(event),
858            InputEvent::KeyUp(event) => Some(event),
859            InputEvent::ModifiersChanged(event) => Some(event),
860            InputEvent::MouseDown(_) => None,
861            InputEvent::MouseUp(_) => None,
862            InputEvent::MouseMoved(_) => None,
863            InputEvent::MouseExited(_) => None,
864            InputEvent::ScrollWheel(_) => None,
865        }
866    }
867}
868
869pub struct FocusEvent {
870    pub blurred: Option<FocusHandle>,
871    pub focused: Option<FocusHandle>,
872}
873
874pub type MouseDownListener<V> = Arc<
875    dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
876        + Send
877        + Sync
878        + 'static,
879>;
880pub type MouseUpListener<V> = Arc<
881    dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
882        + Send
883        + Sync
884        + 'static,
885>;
886pub type MouseClickListener<V> =
887    Arc<dyn Fn(&mut V, &MouseClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
888
889pub type MouseMoveListener<V> = Arc<
890    dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
891        + Send
892        + Sync
893        + 'static,
894>;
895
896pub type ScrollWheelListener<V> = Arc<
897    dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
898        + Send
899        + Sync
900        + 'static,
901>;
902
903pub type KeyListener<V> = Arc<
904    dyn Fn(
905            &mut V,
906            &dyn Any,
907            &[&DispatchContext],
908            DispatchPhase,
909            &mut ViewContext<V>,
910        ) -> Option<Box<dyn Action>>
911        + Send
912        + Sync
913        + 'static,
914>;