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}