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: Option<Rc<dyn Fn(Vector2F, &mut EventContext)>>,
22 click: Option<Rc<dyn Fn(Vector2F, usize, &mut EventContext)>>,
23 right_mouse_down: Option<Rc<dyn Fn(Vector2F, &mut EventContext)>>,
24 right_click: Option<Rc<dyn Fn(Vector2F, usize, &mut EventContext)>>,
25 mouse_down_out: Option<Rc<dyn Fn(Vector2F, &mut EventContext)>>,
26 right_mouse_down_out: Option<Rc<dyn Fn(Vector2F, &mut EventContext)>>,
27 drag: Option<Rc<dyn Fn(Vector2F, Vector2F, &mut EventContext)>>,
28 hover: Option<Rc<dyn Fn(Vector2F, bool, &mut EventContext)>>,
29 padding: Padding,
30}
31
32impl MouseEventHandler {
33 pub fn new<Tag, V, F>(id: usize, cx: &mut RenderContext<V>, render_child: F) -> Self
34 where
35 Tag: 'static,
36 V: View,
37 F: FnOnce(MouseState, &mut RenderContext<V>) -> ElementBox,
38 {
39 Self {
40 id,
41 tag: TypeId::of::<Tag>(),
42 child: render_child(cx.mouse_state::<Tag>(id), cx),
43 cursor_style: None,
44 mouse_down: None,
45 click: None,
46 right_mouse_down: None,
47 right_click: None,
48 mouse_down_out: None,
49 right_mouse_down_out: None,
50 drag: None,
51 hover: None,
52 padding: Default::default(),
53 }
54 }
55
56 pub fn with_cursor_style(mut self, cursor: CursorStyle) -> Self {
57 self.cursor_style = Some(cursor);
58 self
59 }
60
61 pub fn on_mouse_down(
62 mut self,
63 handler: impl Fn(Vector2F, &mut EventContext) + 'static,
64 ) -> Self {
65 self.mouse_down = Some(Rc::new(handler));
66 self
67 }
68
69 pub fn on_click(
70 mut self,
71 handler: impl Fn(Vector2F, usize, &mut EventContext) + 'static,
72 ) -> Self {
73 self.click = Some(Rc::new(handler));
74 self
75 }
76
77 pub fn on_right_mouse_down(
78 mut self,
79 handler: impl Fn(Vector2F, &mut EventContext) + 'static,
80 ) -> Self {
81 self.right_mouse_down = Some(Rc::new(handler));
82 self
83 }
84
85 pub fn on_right_click(
86 mut self,
87 handler: impl Fn(Vector2F, usize, &mut EventContext) + 'static,
88 ) -> Self {
89 self.right_click = Some(Rc::new(handler));
90 self
91 }
92
93 pub fn on_mouse_down_out(
94 mut self,
95 handler: impl Fn(Vector2F, &mut EventContext) + 'static,
96 ) -> Self {
97 self.mouse_down_out = Some(Rc::new(handler));
98 self
99 }
100
101 pub fn on_right_mouse_down_out(
102 mut self,
103 handler: impl Fn(Vector2F, &mut EventContext) + 'static,
104 ) -> Self {
105 self.right_mouse_down_out = Some(Rc::new(handler));
106 self
107 }
108
109 pub fn on_drag(
110 mut self,
111 handler: impl Fn(Vector2F, Vector2F, &mut EventContext) + 'static,
112 ) -> Self {
113 self.drag = Some(Rc::new(handler));
114 self
115 }
116
117 pub fn on_hover(
118 mut self,
119 handler: impl Fn(Vector2F, bool, &mut EventContext) + 'static,
120 ) -> Self {
121 self.hover = Some(Rc::new(handler));
122 self
123 }
124
125 pub fn with_padding(mut self, padding: Padding) -> Self {
126 self.padding = padding;
127 self
128 }
129
130 fn hit_bounds(&self, bounds: RectF) -> RectF {
131 RectF::from_points(
132 bounds.origin() - vec2f(self.padding.left, self.padding.top),
133 bounds.lower_right() + vec2f(self.padding.right, self.padding.bottom),
134 )
135 .round_out()
136 }
137}
138
139impl Element for MouseEventHandler {
140 type LayoutState = ();
141 type PaintState = ();
142
143 fn layout(
144 &mut self,
145 constraint: SizeConstraint,
146 cx: &mut LayoutContext,
147 ) -> (Vector2F, Self::LayoutState) {
148 (self.child.layout(constraint, cx), ())
149 }
150
151 fn paint(
152 &mut self,
153 bounds: RectF,
154 visible_bounds: RectF,
155 _: &mut Self::LayoutState,
156 cx: &mut PaintContext,
157 ) -> Self::PaintState {
158 let hit_bounds = self.hit_bounds(visible_bounds);
159 if let Some(style) = self.cursor_style {
160 cx.scene.push_cursor_region(CursorRegion {
161 bounds: hit_bounds,
162 style,
163 });
164 }
165
166 cx.scene.push_mouse_region(MouseRegion {
167 view_id: cx.current_view_id(),
168 discriminant: Some((self.tag, self.id)),
169 bounds: hit_bounds,
170 hover: self.hover.clone(),
171 click: self.click.clone(),
172 mouse_down: self.mouse_down.clone(),
173 right_click: self.right_click.clone(),
174 right_mouse_down: self.right_mouse_down.clone(),
175 mouse_down_out: self.mouse_down_out.clone(),
176 right_mouse_down_out: self.right_mouse_down_out.clone(),
177 drag: self.drag.clone(),
178 });
179
180 self.child.paint(bounds.origin(), visible_bounds, cx);
181 }
182
183 fn dispatch_event(
184 &mut self,
185 event: &Event,
186 _: RectF,
187 _: RectF,
188 _: &mut Self::LayoutState,
189 _: &mut Self::PaintState,
190 cx: &mut EventContext,
191 ) -> bool {
192 self.child.dispatch_event(event, cx)
193 }
194
195 fn debug(
196 &self,
197 _: RectF,
198 _: &Self::LayoutState,
199 _: &Self::PaintState,
200 cx: &DebugContext,
201 ) -> serde_json::Value {
202 json!({
203 "type": "MouseEventHandler",
204 "child": self.child.debug(cx),
205 })
206 }
207}