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