1use std::{any::TypeId, rc::Rc};
2
3use super::Padding;
4use crate::{
5 geometry::{
6 rect::RectF,
7 vector::{vec2f, Vector2F},
8 },
9 platform::CursorStyle,
10 scene::CursorRegion,
11 DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, MouseRegion, MouseState,
12 PaintContext, RenderContext, SizeConstraint, View,
13};
14use serde_json::json;
15
16pub struct MouseEventHandler {
17 child: ElementBox,
18 tag: TypeId,
19 id: usize,
20 cursor_style: Option<CursorStyle>,
21 mouse_down_handler: Option<Rc<dyn Fn(Vector2F, &mut EventContext)>>,
22 click_handler: Option<Rc<dyn Fn(Vector2F, usize, &mut EventContext)>>,
23 drag_handler: Option<Rc<dyn Fn(Vector2F, &mut EventContext)>>,
24 padding: Padding,
25}
26
27impl MouseEventHandler {
28 pub fn new<Tag, V, F>(id: usize, cx: &mut RenderContext<V>, render_child: F) -> Self
29 where
30 Tag: 'static,
31 V: View,
32 F: FnOnce(MouseState, &mut RenderContext<V>) -> ElementBox,
33 {
34 Self {
35 id,
36 tag: TypeId::of::<Tag>(),
37 child: render_child(cx.mouse_state::<Tag>(id), cx),
38 cursor_style: None,
39 mouse_down_handler: None,
40 click_handler: None,
41 drag_handler: None,
42 padding: Default::default(),
43 }
44 }
45
46 pub fn with_cursor_style(mut self, cursor: CursorStyle) -> Self {
47 self.cursor_style = Some(cursor);
48 self
49 }
50
51 pub fn on_mouse_down(
52 mut self,
53 handler: impl Fn(Vector2F, &mut EventContext) + 'static,
54 ) -> Self {
55 self.mouse_down_handler = Some(Rc::new(handler));
56 self
57 }
58
59 pub fn on_click(
60 mut self,
61 handler: impl Fn(Vector2F, usize, &mut EventContext) + 'static,
62 ) -> Self {
63 self.click_handler = Some(Rc::new(handler));
64 self
65 }
66
67 pub fn on_drag(mut self, handler: impl Fn(Vector2F, &mut EventContext) + 'static) -> Self {
68 self.drag_handler = Some(Rc::new(handler));
69 self
70 }
71
72 pub fn with_padding(mut self, padding: Padding) -> Self {
73 self.padding = padding;
74 self
75 }
76
77 fn hit_bounds(&self, bounds: RectF) -> RectF {
78 RectF::from_points(
79 bounds.origin() - vec2f(self.padding.left, self.padding.top),
80 bounds.lower_right() + vec2f(self.padding.right, self.padding.bottom),
81 )
82 .round_out()
83 }
84}
85
86impl Element for MouseEventHandler {
87 type LayoutState = ();
88 type PaintState = ();
89
90 fn layout(
91 &mut self,
92 constraint: SizeConstraint,
93 cx: &mut LayoutContext,
94 ) -> (Vector2F, Self::LayoutState) {
95 (self.child.layout(constraint, cx), ())
96 }
97
98 fn paint(
99 &mut self,
100 bounds: RectF,
101 visible_bounds: RectF,
102 _: &mut Self::LayoutState,
103 cx: &mut PaintContext,
104 ) -> Self::PaintState {
105 if let Some(style) = self.cursor_style {
106 cx.scene.push_cursor_region(CursorRegion {
107 bounds: self.hit_bounds(bounds),
108 style,
109 });
110 }
111
112 cx.scene.push_mouse_region(MouseRegion {
113 view_id: cx.current_view_id(),
114 tag: self.tag,
115 region_id: self.id,
116 bounds,
117 hover: None,
118 click: self.click_handler.clone(),
119 mouse_down: self.mouse_down_handler.clone(),
120 drag: self.drag_handler.clone(),
121 });
122
123 self.child.paint(bounds.origin(), visible_bounds, cx);
124 }
125
126 fn dispatch_event(
127 &mut self,
128 event: &Event,
129 _: RectF,
130 _: RectF,
131 _: &mut Self::LayoutState,
132 _: &mut Self::PaintState,
133 cx: &mut EventContext,
134 ) -> bool {
135 self.child.dispatch_event(event, cx)
136 }
137
138 fn debug(
139 &self,
140 _: RectF,
141 _: &Self::LayoutState,
142 _: &Self::PaintState,
143 cx: &DebugContext,
144 ) -> serde_json::Value {
145 json!({
146 "type": "MouseEventHandler",
147 "child": self.child.debug(cx),
148 })
149 }
150}