interactive.rs

  1use parking_lot::Mutex;
  2use smallvec::SmallVec;
  3
  4use crate::{
  5    point, Action, Bounds, DispatchContext, DispatchPhase, Element, FocusHandle, Keystroke,
  6    Modifiers, Pixels, Point, ViewContext,
  7};
  8use std::{
  9    any::{Any, TypeId},
 10    mem,
 11    ops::Deref,
 12    sync::Arc,
 13};
 14
 15pub trait Interactive: Element {
 16    fn interactivity(&mut self) -> &mut Interactivity<Self::ViewState>;
 17
 18    fn on_mouse_down(
 19        mut self,
 20        button: MouseButton,
 21        handler: impl Fn(&mut Self::ViewState, &MouseDownEvent, &mut ViewContext<Self::ViewState>)
 22            + Send
 23            + Sync
 24            + 'static,
 25    ) -> Self
 26    where
 27        Self: Sized,
 28    {
 29        self.interactivity()
 30            .mouse_down
 31            .push(Arc::new(move |view, event, bounds, phase, cx| {
 32                if phase == DispatchPhase::Bubble
 33                    && event.button == button
 34                    && bounds.contains_point(&event.position)
 35                {
 36                    handler(view, event, cx)
 37                }
 38            }));
 39        self
 40    }
 41
 42    fn on_mouse_up(
 43        mut self,
 44        button: MouseButton,
 45        handler: impl Fn(&mut Self::ViewState, &MouseUpEvent, &mut ViewContext<Self::ViewState>)
 46            + Send
 47            + Sync
 48            + 'static,
 49    ) -> Self
 50    where
 51        Self: Sized,
 52    {
 53        self.interactivity()
 54            .mouse_up
 55            .push(Arc::new(move |view, event, bounds, phase, cx| {
 56                if phase == DispatchPhase::Bubble
 57                    && event.button == button
 58                    && bounds.contains_point(&event.position)
 59                {
 60                    handler(view, event, cx)
 61                }
 62            }));
 63        self
 64    }
 65
 66    fn on_mouse_down_out(
 67        mut self,
 68        button: MouseButton,
 69        handler: impl Fn(&mut Self::ViewState, &MouseDownEvent, &mut ViewContext<Self::ViewState>)
 70            + Send
 71            + Sync
 72            + 'static,
 73    ) -> Self
 74    where
 75        Self: Sized,
 76    {
 77        self.interactivity()
 78            .mouse_down
 79            .push(Arc::new(move |view, event, bounds, phase, cx| {
 80                if phase == DispatchPhase::Capture
 81                    && event.button == button
 82                    && !bounds.contains_point(&event.position)
 83                {
 84                    handler(view, event, cx)
 85                }
 86            }));
 87        self
 88    }
 89
 90    fn on_mouse_up_out(
 91        mut self,
 92        button: MouseButton,
 93        handler: impl Fn(&mut Self::ViewState, &MouseUpEvent, &mut ViewContext<Self::ViewState>)
 94            + Send
 95            + Sync
 96            + 'static,
 97    ) -> Self
 98    where
 99        Self: Sized,
100    {
101        self.interactivity()
102            .mouse_up
103            .push(Arc::new(move |view, event, bounds, phase, cx| {
104                if phase == DispatchPhase::Capture
105                    && event.button == button
106                    && !bounds.contains_point(&event.position)
107                {
108                    handler(view, event, cx);
109                }
110            }));
111        self
112    }
113
114    fn on_mouse_move(
115        mut self,
116        handler: impl Fn(&mut Self::ViewState, &MouseMoveEvent, &mut ViewContext<Self::ViewState>)
117            + Send
118            + Sync
119            + 'static,
120    ) -> Self
121    where
122        Self: Sized,
123    {
124        self.interactivity()
125            .mouse_move
126            .push(Arc::new(move |view, event, bounds, phase, cx| {
127                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
128                    handler(view, event, cx);
129                }
130            }));
131        self
132    }
133
134    fn on_scroll_wheel(
135        mut self,
136        handler: impl Fn(&mut Self::ViewState, &ScrollWheelEvent, &mut ViewContext<Self::ViewState>)
137            + Send
138            + Sync
139            + 'static,
140    ) -> Self
141    where
142        Self: Sized,
143    {
144        self.interactivity()
145            .scroll_wheel
146            .push(Arc::new(move |view, event, bounds, phase, cx| {
147                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
148                    handler(view, event, cx);
149                }
150            }));
151        self
152    }
153
154    fn on_key_down(
155        mut self,
156        listener: impl Fn(
157                &mut Self::ViewState,
158                &KeyDownEvent,
159                DispatchPhase,
160                &mut ViewContext<Self::ViewState>,
161            ) + Send
162            + Sync
163            + 'static,
164    ) -> Self
165    where
166        Self: Sized,
167    {
168        self.interactivity().key.push((
169            TypeId::of::<KeyDownEvent>(),
170            Arc::new(move |view, event, _, phase, cx| {
171                let event = event.downcast_ref().unwrap();
172                listener(view, event, phase, cx);
173                None
174            }),
175        ));
176        self
177    }
178
179    fn on_key_up(
180        mut self,
181        listener: impl Fn(&mut Self::ViewState, &KeyUpEvent, DispatchPhase, &mut ViewContext<Self::ViewState>)
182            + Send
183            + Sync
184            + 'static,
185    ) -> Self
186    where
187        Self: Sized,
188    {
189        self.interactivity().key.push((
190            TypeId::of::<KeyUpEvent>(),
191            Arc::new(move |view, event, _, phase, cx| {
192                let event = event.downcast_ref().unwrap();
193                listener(view, event, phase, cx);
194                None
195            }),
196        ));
197        self
198    }
199
200    fn on_action<A: 'static>(
201        mut self,
202        listener: impl Fn(&mut Self::ViewState, &A, DispatchPhase, &mut ViewContext<Self::ViewState>)
203            + Send
204            + Sync
205            + 'static,
206    ) -> Self
207    where
208        Self: Sized,
209    {
210        self.interactivity().key.push((
211            TypeId::of::<A>(),
212            Arc::new(move |view, event, _, phase, cx| {
213                let event = event.downcast_ref().unwrap();
214                listener(view, event, phase, cx);
215                None
216            }),
217        ));
218        self
219    }
220}
221
222pub trait Click: Interactive {
223    fn on_click(
224        mut self,
225        handler: impl Fn(&mut Self::ViewState, &MouseClickEvent, &mut ViewContext<Self::ViewState>)
226            + Send
227            + Sync
228            + 'static,
229    ) -> Self
230    where
231        Self: Sized,
232    {
233        self.interactivity()
234            .mouse_click
235            .push(Arc::new(move |view, event, cx| handler(view, event, cx)));
236        self
237    }
238}
239
240#[derive(Clone, Debug, Eq, PartialEq)]
241pub struct KeyDownEvent {
242    pub keystroke: Keystroke,
243    pub is_held: bool,
244}
245
246#[derive(Clone, Debug)]
247pub struct KeyUpEvent {
248    pub keystroke: Keystroke,
249}
250
251#[derive(Clone, Debug, Default)]
252pub struct ModifiersChangedEvent {
253    pub modifiers: Modifiers,
254}
255
256impl Deref for ModifiersChangedEvent {
257    type Target = Modifiers;
258
259    fn deref(&self) -> &Self::Target {
260        &self.modifiers
261    }
262}
263
264/// The phase of a touch motion event.
265/// Based on the winit enum of the same name.
266#[derive(Clone, Copy, Debug)]
267pub enum TouchPhase {
268    Started,
269    Moved,
270    Ended,
271}
272
273#[derive(Clone, Debug, Default)]
274pub struct MouseDownEvent {
275    pub button: MouseButton,
276    pub position: Point<Pixels>,
277    pub modifiers: Modifiers,
278    pub click_count: usize,
279}
280
281#[derive(Clone, Debug, Default)]
282pub struct MouseUpEvent {
283    pub button: MouseButton,
284    pub position: Point<Pixels>,
285    pub modifiers: Modifiers,
286    pub click_count: usize,
287}
288
289#[derive(Clone, Debug, Default)]
290pub struct MouseClickEvent {
291    pub down: MouseDownEvent,
292    pub up: MouseUpEvent,
293}
294
295#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
296pub enum MouseButton {
297    Left,
298    Right,
299    Middle,
300    Navigate(NavigationDirection),
301}
302
303impl MouseButton {
304    pub fn all() -> Vec<Self> {
305        vec![
306            MouseButton::Left,
307            MouseButton::Right,
308            MouseButton::Middle,
309            MouseButton::Navigate(NavigationDirection::Back),
310            MouseButton::Navigate(NavigationDirection::Forward),
311        ]
312    }
313}
314
315impl Default for MouseButton {
316    fn default() -> Self {
317        Self::Left
318    }
319}
320
321#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
322pub enum NavigationDirection {
323    Back,
324    Forward,
325}
326
327impl Default for NavigationDirection {
328    fn default() -> Self {
329        Self::Back
330    }
331}
332
333#[derive(Clone, Debug, Default)]
334pub struct MouseMoveEvent {
335    pub position: Point<Pixels>,
336    pub pressed_button: Option<MouseButton>,
337    pub modifiers: Modifiers,
338}
339
340#[derive(Clone, Debug)]
341pub struct ScrollWheelEvent {
342    pub position: Point<Pixels>,
343    pub delta: ScrollDelta,
344    pub modifiers: Modifiers,
345    pub touch_phase: TouchPhase,
346}
347
348impl Deref for ScrollWheelEvent {
349    type Target = Modifiers;
350
351    fn deref(&self) -> &Self::Target {
352        &self.modifiers
353    }
354}
355
356#[derive(Clone, Copy, Debug)]
357pub enum ScrollDelta {
358    Pixels(Point<Pixels>),
359    Lines(Point<f32>),
360}
361
362impl Default for ScrollDelta {
363    fn default() -> Self {
364        Self::Lines(Default::default())
365    }
366}
367
368impl ScrollDelta {
369    pub fn precise(&self) -> bool {
370        match self {
371            ScrollDelta::Pixels(_) => true,
372            ScrollDelta::Lines(_) => false,
373        }
374    }
375
376    pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
377        match self {
378            ScrollDelta::Pixels(delta) => *delta,
379            ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
380        }
381    }
382}
383
384#[derive(Clone, Debug, Default)]
385pub struct MouseExitEvent {
386    pub position: Point<Pixels>,
387    pub pressed_button: Option<MouseButton>,
388    pub modifiers: Modifiers,
389}
390
391impl Deref for MouseExitEvent {
392    type Target = Modifiers;
393
394    fn deref(&self) -> &Self::Target {
395        &self.modifiers
396    }
397}
398
399#[derive(Clone, Debug)]
400pub enum InputEvent {
401    KeyDown(KeyDownEvent),
402    KeyUp(KeyUpEvent),
403    ModifiersChanged(ModifiersChangedEvent),
404    MouseDown(MouseDownEvent),
405    MouseUp(MouseUpEvent),
406    MouseMoved(MouseMoveEvent),
407    MouseExited(MouseExitEvent),
408    ScrollWheel(ScrollWheelEvent),
409}
410
411impl InputEvent {
412    pub fn position(&self) -> Option<Point<Pixels>> {
413        match self {
414            InputEvent::KeyDown { .. } => None,
415            InputEvent::KeyUp { .. } => None,
416            InputEvent::ModifiersChanged { .. } => None,
417            InputEvent::MouseDown(event) => Some(event.position),
418            InputEvent::MouseUp(event) => Some(event.position),
419            InputEvent::MouseMoved(event) => Some(event.position),
420            InputEvent::MouseExited(event) => Some(event.position),
421            InputEvent::ScrollWheel(event) => Some(event.position),
422        }
423    }
424
425    pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
426        match self {
427            InputEvent::KeyDown { .. } => None,
428            InputEvent::KeyUp { .. } => None,
429            InputEvent::ModifiersChanged { .. } => None,
430            InputEvent::MouseDown(event) => Some(event),
431            InputEvent::MouseUp(event) => Some(event),
432            InputEvent::MouseMoved(event) => Some(event),
433            InputEvent::MouseExited(event) => Some(event),
434            InputEvent::ScrollWheel(event) => Some(event),
435        }
436    }
437
438    pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> {
439        match self {
440            InputEvent::KeyDown(event) => Some(event),
441            InputEvent::KeyUp(event) => Some(event),
442            InputEvent::ModifiersChanged(event) => Some(event),
443            InputEvent::MouseDown(_) => None,
444            InputEvent::MouseUp(_) => None,
445            InputEvent::MouseMoved(_) => None,
446            InputEvent::MouseExited(_) => None,
447            InputEvent::ScrollWheel(_) => None,
448        }
449    }
450}
451
452pub struct FocusEvent {
453    pub blurred: Option<FocusHandle>,
454    pub focused: Option<FocusHandle>,
455}
456
457pub type MouseDownListener<V> = Arc<
458    dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
459        + Send
460        + Sync
461        + 'static,
462>;
463pub type MouseUpListener<V> = Arc<
464    dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
465        + Send
466        + Sync
467        + 'static,
468>;
469pub type MouseClickListener<V> =
470    Arc<dyn Fn(&mut V, &MouseClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
471
472pub type MouseMoveListener<V> = Arc<
473    dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
474        + Send
475        + Sync
476        + 'static,
477>;
478
479pub type ScrollWheelListener<V> = Arc<
480    dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
481        + Send
482        + Sync
483        + 'static,
484>;
485
486pub type KeyListener<V> = Arc<
487    dyn Fn(
488            &mut V,
489            &dyn Any,
490            &[&DispatchContext],
491            DispatchPhase,
492            &mut ViewContext<V>,
493        ) -> Option<Box<dyn Action>>
494        + Send
495        + Sync
496        + 'static,
497>;
498
499pub struct Interactivity<V> {
500    pub mouse_down: SmallVec<[MouseDownListener<V>; 2]>,
501    pub mouse_up: SmallVec<[MouseUpListener<V>; 2]>,
502    pub mouse_click: SmallVec<[MouseClickListener<V>; 2]>,
503    pub mouse_move: SmallVec<[MouseMoveListener<V>; 2]>,
504    pub scroll_wheel: SmallVec<[ScrollWheelListener<V>; 2]>,
505    pub key: SmallVec<[(TypeId, KeyListener<V>); 32]>,
506}
507
508impl<V> Default for Interactivity<V> {
509    fn default() -> Self {
510        Self {
511            mouse_down: SmallVec::new(),
512            mouse_up: SmallVec::new(),
513            mouse_click: SmallVec::new(),
514            mouse_move: SmallVec::new(),
515            scroll_wheel: SmallVec::new(),
516            key: SmallVec::new(),
517        }
518    }
519}
520
521impl<V> Interactivity<V>
522where
523    V: 'static + Send + Sync,
524{
525    pub fn paint(
526        &mut self,
527        bounds: Bounds<Pixels>,
528        pending_click: Arc<Mutex<Option<MouseDownEvent>>>,
529        cx: &mut ViewContext<V>,
530    ) {
531        let click_listeners = mem::take(&mut self.mouse_click);
532
533        let mouse_down = pending_click.lock().clone();
534        if let Some(mouse_down) = mouse_down {
535            cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
536                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
537                    let mouse_click = MouseClickEvent {
538                        down: mouse_down.clone(),
539                        up: event.clone(),
540                    };
541                    for listener in &click_listeners {
542                        listener(state, &mouse_click, cx);
543                    }
544                }
545
546                *pending_click.lock() = None;
547            });
548        } else {
549            cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| {
550                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
551                    *pending_click.lock() = Some(event.clone());
552                }
553            });
554        }
555
556        for listener in mem::take(&mut self.mouse_down) {
557            cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
558                listener(state, event, &bounds, phase, cx);
559            })
560        }
561
562        for listener in mem::take(&mut self.mouse_up) {
563            cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
564                listener(state, event, &bounds, phase, cx);
565            })
566        }
567
568        for listener in mem::take(&mut self.mouse_move) {
569            cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
570                listener(state, event, &bounds, phase, cx);
571            })
572        }
573
574        for listener in mem::take(&mut self.scroll_wheel) {
575            cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
576                listener(state, event, &bounds, phase, cx);
577            })
578        }
579    }
580}