interactive.rs

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