interactive.rs

  1use crate::{
  2    Bounds, DispatchPhase, MouseButton, MouseDownEvent, MouseUpEvent, Pixels, ViewContext,
  3};
  4use parking_lot::Mutex;
  5use smallvec::SmallVec;
  6use std::sync::Arc;
  7
  8pub trait Interactive<S: 'static + Send + Sync> {
  9    fn listeners(&mut self) -> &mut MouseEventListeners<S>;
 10
 11    fn on_mouse_down(
 12        mut self,
 13        button: MouseButton,
 14        handler: impl Fn(&mut S, &MouseDownEvent, &mut ViewContext<S>) + Send + Sync + 'static,
 15    ) -> Self
 16    where
 17        Self: Sized,
 18    {
 19        self.listeners()
 20            .mouse_down
 21            .push(Arc::new(move |view, event, bounds, phase, cx| {
 22                if phase == DispatchPhase::Bubble
 23                    && event.button == button
 24                    && bounds.contains_point(event.position)
 25                {
 26                    handler(view, event, cx)
 27                }
 28            }));
 29        self
 30    }
 31
 32    fn on_mouse_up(
 33        mut self,
 34        button: MouseButton,
 35        handler: impl Fn(&mut S, &MouseUpEvent, &mut ViewContext<S>) + Send + Sync + 'static,
 36    ) -> Self
 37    where
 38        Self: Sized,
 39    {
 40        self.listeners()
 41            .mouse_up
 42            .push(Arc::new(move |view, event, bounds, phase, cx| {
 43                if phase == DispatchPhase::Bubble
 44                    && event.button == button
 45                    && bounds.contains_point(event.position)
 46                {
 47                    handler(view, event, cx)
 48                }
 49            }));
 50        self
 51    }
 52
 53    fn on_mouse_down_out(
 54        mut self,
 55        button: MouseButton,
 56        handler: impl Fn(&mut S, &MouseDownEvent, &mut ViewContext<S>) + Send + Sync + 'static,
 57    ) -> Self
 58    where
 59        Self: Sized,
 60    {
 61        self.listeners()
 62            .mouse_down
 63            .push(Arc::new(move |view, event, bounds, phase, cx| {
 64                if phase == DispatchPhase::Capture
 65                    && event.button == button
 66                    && !bounds.contains_point(event.position)
 67                {
 68                    handler(view, event, cx)
 69                }
 70            }));
 71        self
 72    }
 73
 74    fn on_mouse_up_out(
 75        mut self,
 76        button: MouseButton,
 77        handler: impl Fn(&mut S, &MouseUpEvent, &mut ViewContext<S>) + Send + Sync + 'static,
 78    ) -> Self
 79    where
 80        Self: Sized,
 81    {
 82        self.listeners()
 83            .mouse_up
 84            .push(Arc::new(move |view, event, bounds, phase, cx| {
 85                if phase == DispatchPhase::Capture
 86                    && event.button == button
 87                    && !bounds.contains_point(event.position)
 88                {
 89                    handler(view, event, cx);
 90                }
 91            }));
 92        self
 93    }
 94
 95    fn on_click(
 96        self,
 97        button: MouseButton,
 98        handler: impl Fn(&mut S, (&MouseDownEvent, &MouseUpEvent), &mut ViewContext<S>)
 99            + Send
100            + Sync
101            + 'static,
102    ) -> Self
103    where
104        Self: Sized,
105    {
106        let down_event = Arc::new(Mutex::new(None));
107        self.on_mouse_down(button, {
108            let down_event = down_event.clone();
109            move |_, event, _| {
110                down_event.lock().replace(event.clone());
111            }
112        })
113        .on_mouse_up_out(button, {
114            let down_event = down_event.clone();
115            move |_, _, _| {
116                down_event.lock().take();
117            }
118        })
119        .on_mouse_up(button, move |view, event, cx| {
120            if let Some(down_event) = down_event.lock().take() {
121                handler(view, (&down_event, event), cx);
122            }
123        })
124    }
125}
126
127type MouseDownHandler<V> = Arc<
128    dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
129        + Send
130        + Sync
131        + 'static,
132>;
133type MouseUpHandler<V> = Arc<
134    dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
135        + Send
136        + Sync
137        + 'static,
138>;
139
140pub struct MouseEventListeners<V: 'static> {
141    mouse_down: SmallVec<[MouseDownHandler<V>; 2]>,
142    mouse_up: SmallVec<[MouseUpHandler<V>; 2]>,
143}
144
145impl<S: Send + Sync + 'static> MouseEventListeners<S> {
146    pub fn paint(&self, bounds: Bounds<Pixels>, cx: &mut ViewContext<S>) {
147        for handler in self.mouse_down.iter().cloned() {
148            cx.on_mouse_event(move |view, event: &MouseDownEvent, phase, cx| {
149                if bounds.contains_point(event.position) {
150                    handler(view, event, &bounds, phase, cx);
151                }
152            })
153        }
154        for handler in self.mouse_up.iter().cloned() {
155            cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| {
156                if bounds.contains_point(event.position) {
157                    handler(view, event, &bounds, phase, cx);
158                }
159            })
160        }
161    }
162}
163
164impl<V> Default for MouseEventListeners<V> {
165    fn default() -> Self {
166        Self {
167            mouse_down: Default::default(),
168            mouse_up: Default::default(),
169        }
170    }
171}