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_region_event::{
11 ClickRegionEvent, DownOutRegionEvent, DownRegionEvent, DragRegionEvent, HoverRegionEvent,
12 MouseRegionEvent, MoveRegionEvent, UpOutRegionEvent, UpRegionEvent,
13 },
14 ScrollWheelRegionEvent,
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(DownRegionEvent, &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(UpRegionEvent, &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(ClickRegionEvent, &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(DownOutRegionEvent, &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(UpOutRegionEvent, &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(DragRegionEvent, &mut EventContext) + 'static,
111 ) -> Self {
112 self.handlers = self.handlers.on_drag(button, handler);
113 self
114 }
115
116 pub fn on_hover(
117 mut self,
118 handler: impl Fn(HoverRegionEvent, &mut EventContext) + 'static,
119 ) -> Self {
120 self.handlers = self.handlers.on_hover(handler);
121 self
122 }
123
124 pub fn on_move(
125 mut self,
126 handler: impl Fn(MoveRegionEvent, &mut EventContext) + 'static,
127 ) -> Self {
128 self.handlers = self.handlers.on_move(handler);
129 self
130 }
131
132 pub fn on_scroll(
133 mut self,
134 handler: impl Fn(ScrollWheelRegionEvent, &mut EventContext) + 'static,
135 ) -> Self {
136 self.handlers = self.handlers.on_scroll(handler);
137 self
138 }
139
140 pub fn with_hoverable(mut self, is_hoverable: bool) -> Self {
141 self.hoverable = is_hoverable;
142 self
143 }
144
145 pub fn with_notify_on_hover(mut self, notify: bool) -> Self {
146 self.notify_on_hover = notify;
147 self
148 }
149
150 pub fn with_notify_on_click(mut self, notify: bool) -> Self {
151 self.notify_on_click = notify;
152 self
153 }
154}
155
156#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
157pub struct MouseRegionId {
158 view_id: usize,
159 tag: TypeId,
160 region_id: usize,
161 #[cfg(debug_assertions)]
162 tag_type_name: &'static str,
163}
164
165impl MouseRegionId {
166 pub(crate) fn new<Tag: 'static>(view_id: usize, region_id: usize) -> Self {
167 MouseRegionId {
168 view_id,
169 region_id,
170 tag: TypeId::of::<Tag>(),
171 #[cfg(debug_assertions)]
172 tag_type_name: std::any::type_name::<Tag>(),
173 }
174 }
175
176 pub fn view_id(&self) -> usize {
177 self.view_id
178 }
179
180 #[cfg(debug_assertions)]
181 pub fn tag_type_name(&self) -> &'static str {
182 self.tag_type_name
183 }
184}
185
186#[derive(Clone, Default)]
187pub struct HandlerSet {
188 #[allow(clippy::type_complexity)]
189 pub set: HashMap<
190 (Discriminant<MouseRegionEvent>, Option<MouseButton>),
191 Rc<dyn Fn(MouseRegionEvent, &mut EventContext)>,
192 >,
193}
194
195impl HandlerSet {
196 pub fn capture_all() -> Self {
197 #[allow(clippy::type_complexity)]
198 let mut set: HashMap<
199 (Discriminant<MouseRegionEvent>, Option<MouseButton>),
200 Rc<dyn Fn(MouseRegionEvent, &mut EventContext)>,
201 > = Default::default();
202
203 set.insert((MouseRegionEvent::move_disc(), None), Rc::new(|_, _| {}));
204 set.insert((MouseRegionEvent::hover_disc(), None), Rc::new(|_, _| {}));
205 for button in MouseButton::all() {
206 set.insert(
207 (MouseRegionEvent::drag_disc(), Some(button)),
208 Rc::new(|_, _| {}),
209 );
210 set.insert(
211 (MouseRegionEvent::down_disc(), Some(button)),
212 Rc::new(|_, _| {}),
213 );
214 set.insert(
215 (MouseRegionEvent::up_disc(), Some(button)),
216 Rc::new(|_, _| {}),
217 );
218 set.insert(
219 (MouseRegionEvent::click_disc(), Some(button)),
220 Rc::new(|_, _| {}),
221 );
222 set.insert(
223 (MouseRegionEvent::down_out_disc(), Some(button)),
224 Rc::new(|_, _| {}),
225 );
226 set.insert(
227 (MouseRegionEvent::up_out_disc(), Some(button)),
228 Rc::new(|_, _| {}),
229 );
230 }
231 set.insert(
232 (MouseRegionEvent::scroll_wheel_disc(), None),
233 Rc::new(|_, _| {}),
234 );
235
236 HandlerSet { set }
237 }
238
239 pub fn get(
240 &self,
241 key: &(Discriminant<MouseRegionEvent>, Option<MouseButton>),
242 ) -> Option<Rc<dyn Fn(MouseRegionEvent, &mut EventContext)>> {
243 self.set.get(key).cloned()
244 }
245
246 pub fn contains_handler(
247 &self,
248 event: Discriminant<MouseRegionEvent>,
249 button: Option<MouseButton>,
250 ) -> bool {
251 self.set.contains_key(&(event, button))
252 }
253
254 pub fn on_move(
255 mut self,
256 handler: impl Fn(MoveRegionEvent, &mut EventContext) + 'static,
257 ) -> Self {
258 self.set.insert((MouseRegionEvent::move_disc(), None),
259 Rc::new(move |region_event, cx| {
260 if let MouseRegionEvent::Move(e) = region_event {
261 handler(e, cx);
262 } else {
263 panic!(
264 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Move, found {:?}",
265 region_event);
266 }
267 }));
268 self
269 }
270
271 pub fn on_down(
272 mut self,
273 button: MouseButton,
274 handler: impl Fn(DownRegionEvent, &mut EventContext) + 'static,
275 ) -> Self {
276 self.set.insert((MouseRegionEvent::down_disc(), Some(button)),
277 Rc::new(move |region_event, cx| {
278 if let MouseRegionEvent::Down(e) = region_event {
279 handler(e, cx);
280 } else {
281 panic!(
282 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Down, found {:?}",
283 region_event);
284 }
285 }));
286 self
287 }
288
289 pub fn on_up(
290 mut self,
291 button: MouseButton,
292 handler: impl Fn(UpRegionEvent, &mut EventContext) + 'static,
293 ) -> Self {
294 self.set.insert((MouseRegionEvent::up_disc(), Some(button)),
295 Rc::new(move |region_event, cx| {
296 if let MouseRegionEvent::Up(e) = region_event {
297 handler(e, cx);
298 } else {
299 panic!(
300 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Up, found {:?}",
301 region_event);
302 }
303 }));
304 self
305 }
306
307 pub fn on_click(
308 mut self,
309 button: MouseButton,
310 handler: impl Fn(ClickRegionEvent, &mut EventContext) + 'static,
311 ) -> Self {
312 self.set.insert((MouseRegionEvent::click_disc(), Some(button)),
313 Rc::new(move |region_event, cx| {
314 if let MouseRegionEvent::Click(e) = region_event {
315 handler(e, cx);
316 } else {
317 panic!(
318 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Click, found {:?}",
319 region_event);
320 }
321 }));
322 self
323 }
324
325 pub fn on_down_out(
326 mut self,
327 button: MouseButton,
328 handler: impl Fn(DownOutRegionEvent, &mut EventContext) + 'static,
329 ) -> Self {
330 self.set.insert((MouseRegionEvent::down_out_disc(), Some(button)),
331 Rc::new(move |region_event, cx| {
332 if let MouseRegionEvent::DownOut(e) = region_event {
333 handler(e, cx);
334 } else {
335 panic!(
336 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::DownOut, found {:?}",
337 region_event);
338 }
339 }));
340 self
341 }
342
343 pub fn on_up_out(
344 mut self,
345 button: MouseButton,
346 handler: impl Fn(UpOutRegionEvent, &mut EventContext) + 'static,
347 ) -> Self {
348 self.set.insert((MouseRegionEvent::up_out_disc(), Some(button)),
349 Rc::new(move |region_event, cx| {
350 if let MouseRegionEvent::UpOut(e) = region_event {
351 handler(e, cx);
352 } else {
353 panic!(
354 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::UpOut, found {:?}",
355 region_event);
356 }
357 }));
358 self
359 }
360
361 pub fn on_drag(
362 mut self,
363 button: MouseButton,
364 handler: impl Fn(DragRegionEvent, &mut EventContext) + 'static,
365 ) -> Self {
366 self.set.insert((MouseRegionEvent::drag_disc(), Some(button)),
367 Rc::new(move |region_event, cx| {
368 if let MouseRegionEvent::Drag(e) = region_event {
369 handler(e, cx);
370 } else {
371 panic!(
372 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Drag, found {:?}",
373 region_event);
374 }
375 }));
376 self
377 }
378
379 pub fn on_hover(
380 mut self,
381 handler: impl Fn(HoverRegionEvent, &mut EventContext) + 'static,
382 ) -> Self {
383 self.set.insert((MouseRegionEvent::hover_disc(), None),
384 Rc::new(move |region_event, cx| {
385 if let MouseRegionEvent::Hover(e) = region_event {
386 handler(e, cx);
387 } else {
388 panic!(
389 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::Hover, found {:?}",
390 region_event);
391 }
392 }));
393 self
394 }
395
396 pub fn on_scroll(
397 mut self,
398 handler: impl Fn(ScrollWheelRegionEvent, &mut EventContext) + 'static,
399 ) -> Self {
400 self.set.insert((MouseRegionEvent::scroll_wheel_disc(), None),
401 Rc::new(move |region_event, cx| {
402 if let MouseRegionEvent::ScrollWheel(e) = region_event {
403 handler(e, cx);
404 } else {
405 panic!(
406 "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::ScrollWheel, found {:?}",
407 region_event
408 );
409 }
410 }));
411 self
412 }
413}