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