1use std::{any::TypeId, fmt::Debug, mem::Discriminant, rc::Rc};
2
3use collections::HashMap;
4
5use pathfinder_geometry::rect::RectF;
6
7use crate::{EventContext, MouseButton};
8
9use super::{
10 mouse_event::{
11 MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseEvent, MouseHover, MouseMove, MouseUp,
12 MouseUpOut,
13 },
14 MouseScrollWheel,
15};
16
17#[derive(Clone)]
18pub struct MouseRegion {
19 pub id: MouseRegionId,
20 pub bounds: RectF,
21 pub handlers: HandlerSet,
22 pub hoverable: bool,
23 pub notify_on_hover: bool,
24 pub notify_on_click: bool,
25}
26
27impl MouseRegion {
28 /// Region ID is used to track semantically equivalent mouse regions across render passes.
29 /// e.g. if you have mouse handlers attached to a list item type, then each item of the list
30 /// should pass a different (consistent) region_id. If you have one big region that covers your
31 /// whole component, just pass the view_id again.
32 pub fn new<Tag: 'static>(view_id: usize, region_id: usize, bounds: RectF) -> Self {
33 Self::from_handlers::<Tag>(view_id, region_id, bounds, Default::default())
34 }
35
36 pub fn handle_all<Tag: 'static>(view_id: usize, region_id: usize, bounds: RectF) -> Self {
37 Self::from_handlers::<Tag>(view_id, region_id, bounds, HandlerSet::capture_all())
38 }
39
40 pub fn from_handlers<Tag: 'static>(
41 view_id: usize,
42 region_id: usize,
43 bounds: RectF,
44 handlers: HandlerSet,
45 ) -> Self {
46 Self {
47 id: MouseRegionId {
48 view_id,
49 tag: TypeId::of::<Tag>(),
50 region_id,
51 #[cfg(debug_assertions)]
52 tag_type_name: std::any::type_name::<Tag>(),
53 },
54 bounds,
55 handlers,
56 hoverable: true,
57 notify_on_hover: false,
58 notify_on_click: false,
59 }
60 }
61
62 pub fn on_down(
63 mut self,
64 button: MouseButton,
65 handler: impl Fn(MouseDown, &mut EventContext) + 'static,
66 ) -> Self {
67 self.handlers = self.handlers.on_down(button, handler);
68 self
69 }
70
71 pub fn on_up(
72 mut self,
73 button: MouseButton,
74 handler: impl Fn(MouseUp, &mut EventContext) + 'static,
75 ) -> Self {
76 self.handlers = self.handlers.on_up(button, handler);
77 self
78 }
79
80 pub fn on_click(
81 mut self,
82 button: MouseButton,
83 handler: impl Fn(MouseClick, &mut EventContext) + 'static,
84 ) -> Self {
85 self.handlers = self.handlers.on_click(button, handler);
86 self
87 }
88
89 pub fn on_down_out(
90 mut self,
91 button: MouseButton,
92 handler: impl Fn(MouseDownOut, &mut EventContext) + 'static,
93 ) -> Self {
94 self.handlers = self.handlers.on_down_out(button, handler);
95 self
96 }
97
98 pub fn on_up_out(
99 mut self,
100 button: MouseButton,
101 handler: impl Fn(MouseUpOut, &mut EventContext) + 'static,
102 ) -> Self {
103 self.handlers = self.handlers.on_up_out(button, handler);
104 self
105 }
106
107 pub fn on_drag(
108 mut self,
109 button: MouseButton,
110 handler: impl Fn(MouseDrag, &mut EventContext) + 'static,
111 ) -> Self {
112 self.handlers = self.handlers.on_drag(button, handler);
113 self
114 }
115
116 pub fn on_hover(mut self, handler: impl Fn(MouseHover, &mut EventContext) + 'static) -> Self {
117 self.handlers = self.handlers.on_hover(handler);
118 self
119 }
120
121 pub fn on_move(mut self, handler: impl Fn(MouseMove, &mut EventContext) + 'static) -> Self {
122 self.handlers = self.handlers.on_move(handler);
123 self
124 }
125
126 pub fn on_scroll(
127 mut self,
128 handler: impl Fn(MouseScrollWheel, &mut EventContext) + 'static,
129 ) -> Self {
130 self.handlers = self.handlers.on_scroll(handler);
131 self
132 }
133
134 pub fn with_hoverable(mut self, is_hoverable: bool) -> Self {
135 self.hoverable = is_hoverable;
136 self
137 }
138
139 pub fn with_notify_on_hover(mut self, notify: bool) -> Self {
140 self.notify_on_hover = notify;
141 self
142 }
143
144 pub fn with_notify_on_click(mut self, notify: bool) -> Self {
145 self.notify_on_click = notify;
146 self
147 }
148}
149
150#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
151pub struct MouseRegionId {
152 view_id: usize,
153 tag: TypeId,
154 region_id: usize,
155 #[cfg(debug_assertions)]
156 tag_type_name: &'static str,
157}
158
159impl MouseRegionId {
160 pub(crate) fn new<Tag: 'static>(view_id: usize, region_id: usize) -> Self {
161 MouseRegionId {
162 view_id,
163 region_id,
164 tag: TypeId::of::<Tag>(),
165 #[cfg(debug_assertions)]
166 tag_type_name: std::any::type_name::<Tag>(),
167 }
168 }
169
170 pub fn view_id(&self) -> usize {
171 self.view_id
172 }
173
174 #[cfg(debug_assertions)]
175 pub fn tag_type_name(&self) -> &'static str {
176 self.tag_type_name
177 }
178}
179
180#[derive(Clone, Default)]
181pub struct HandlerSet {
182 #[allow(clippy::type_complexity)]
183 pub set: HashMap<
184 (Discriminant<MouseEvent>, Option<MouseButton>),
185 Rc<dyn Fn(MouseEvent, &mut EventContext)>,
186 >,
187}
188
189impl HandlerSet {
190 pub fn capture_all() -> Self {
191 #[allow(clippy::type_complexity)]
192 let mut set: HashMap<
193 (Discriminant<MouseEvent>, Option<MouseButton>),
194 Rc<dyn Fn(MouseEvent, &mut EventContext)>,
195 > = Default::default();
196
197 set.insert((MouseEvent::move_disc(), None), Rc::new(|_, _| {}));
198 set.insert((MouseEvent::hover_disc(), None), Rc::new(|_, _| {}));
199 for button in MouseButton::all() {
200 set.insert((MouseEvent::drag_disc(), Some(button)), Rc::new(|_, _| {}));
201 set.insert((MouseEvent::down_disc(), Some(button)), Rc::new(|_, _| {}));
202 set.insert((MouseEvent::up_disc(), Some(button)), Rc::new(|_, _| {}));
203 set.insert((MouseEvent::click_disc(), Some(button)), Rc::new(|_, _| {}));
204 set.insert(
205 (MouseEvent::down_out_disc(), Some(button)),
206 Rc::new(|_, _| {}),
207 );
208 set.insert(
209 (MouseEvent::up_out_disc(), Some(button)),
210 Rc::new(|_, _| {}),
211 );
212 }
213 set.insert((MouseEvent::scroll_wheel_disc(), None), Rc::new(|_, _| {}));
214
215 HandlerSet { set }
216 }
217
218 pub fn get(
219 &self,
220 key: &(Discriminant<MouseEvent>, Option<MouseButton>),
221 ) -> Option<Rc<dyn Fn(MouseEvent, &mut EventContext)>> {
222 self.set.get(key).cloned()
223 }
224
225 pub fn contains_handler(
226 &self,
227 event: Discriminant<MouseEvent>,
228 button: Option<MouseButton>,
229 ) -> bool {
230 self.set.contains_key(&(event, button))
231 }
232
233 pub fn on_move(mut self, handler: impl Fn(MouseMove, &mut EventContext) + 'static) -> Self {
234 self.set.insert((MouseEvent::move_disc(), None),
235 Rc::new(move |region_event, cx| {
236 if let MouseEvent::Move(e) = region_event {
237 handler(e, cx);
238 } else {
239 panic!(
240 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Move, found {:?}",
241 region_event);
242 }
243 }));
244 self
245 }
246
247 pub fn on_down(
248 mut self,
249 button: MouseButton,
250 handler: impl Fn(MouseDown, &mut EventContext) + 'static,
251 ) -> Self {
252 self.set.insert((MouseEvent::down_disc(), Some(button)),
253 Rc::new(move |region_event, cx| {
254 if let MouseEvent::Down(e) = region_event {
255 handler(e, cx);
256 } else {
257 panic!(
258 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Down, found {:?}",
259 region_event);
260 }
261 }));
262 self
263 }
264
265 pub fn on_up(
266 mut self,
267 button: MouseButton,
268 handler: impl Fn(MouseUp, &mut EventContext) + 'static,
269 ) -> Self {
270 self.set.insert((MouseEvent::up_disc(), Some(button)),
271 Rc::new(move |region_event, cx| {
272 if let MouseEvent::Up(e) = region_event {
273 handler(e, cx);
274 } else {
275 panic!(
276 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Up, found {:?}",
277 region_event);
278 }
279 }));
280 self
281 }
282
283 pub fn on_click(
284 mut self,
285 button: MouseButton,
286 handler: impl Fn(MouseClick, &mut EventContext) + 'static,
287 ) -> Self {
288 self.set.insert((MouseEvent::click_disc(), Some(button)),
289 Rc::new(move |region_event, cx| {
290 if let MouseEvent::Click(e) = region_event {
291 handler(e, cx);
292 } else {
293 panic!(
294 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Click, found {:?}",
295 region_event);
296 }
297 }));
298 self
299 }
300
301 pub fn on_down_out(
302 mut self,
303 button: MouseButton,
304 handler: impl Fn(MouseDownOut, &mut EventContext) + 'static,
305 ) -> Self {
306 self.set.insert((MouseEvent::down_out_disc(), Some(button)),
307 Rc::new(move |region_event, cx| {
308 if let MouseEvent::DownOut(e) = region_event {
309 handler(e, cx);
310 } else {
311 panic!(
312 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::DownOut, found {:?}",
313 region_event);
314 }
315 }));
316 self
317 }
318
319 pub fn on_up_out(
320 mut self,
321 button: MouseButton,
322 handler: impl Fn(MouseUpOut, &mut EventContext) + 'static,
323 ) -> Self {
324 self.set.insert((MouseEvent::up_out_disc(), Some(button)),
325 Rc::new(move |region_event, cx| {
326 if let MouseEvent::UpOut(e) = region_event {
327 handler(e, cx);
328 } else {
329 panic!(
330 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::UpOut, found {:?}",
331 region_event);
332 }
333 }));
334 self
335 }
336
337 pub fn on_drag(
338 mut self,
339 button: MouseButton,
340 handler: impl Fn(MouseDrag, &mut EventContext) + 'static,
341 ) -> Self {
342 self.set.insert((MouseEvent::drag_disc(), Some(button)),
343 Rc::new(move |region_event, cx| {
344 if let MouseEvent::Drag(e) = region_event {
345 handler(e, cx);
346 } else {
347 panic!(
348 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Drag, found {:?}",
349 region_event);
350 }
351 }));
352 self
353 }
354
355 pub fn on_hover(mut self, handler: impl Fn(MouseHover, &mut EventContext) + 'static) -> Self {
356 self.set.insert((MouseEvent::hover_disc(), None),
357 Rc::new(move |region_event, cx| {
358 if let MouseEvent::Hover(e) = region_event {
359 handler(e, cx);
360 } else {
361 panic!(
362 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Hover, found {:?}",
363 region_event);
364 }
365 }));
366 self
367 }
368
369 pub fn on_scroll(
370 mut self,
371 handler: impl Fn(MouseScrollWheel, &mut EventContext) + 'static,
372 ) -> Self {
373 self.set.insert((MouseEvent::scroll_wheel_disc(), None),
374 Rc::new(move |region_event, cx| {
375 if let MouseEvent::ScrollWheel(e) = region_event {
376 handler(e, cx);
377 } else {
378 panic!(
379 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::ScrollWheel, found {:?}",
380 region_event
381 );
382 }
383 }));
384 self
385 }
386}