1use crate::{
2 geometry::vector::Vector2F, presenter::MeasurementContext, scene::HandlerSet, CursorRegion,
3 DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, MouseButton,
4 MouseButtonEvent, MouseRegion, NavigationDirection, PaintContext, SizeConstraint,
5};
6use pathfinder_geometry::rect::RectF;
7use serde_json::json;
8use std::{any::TypeId, ops::Range};
9
10pub struct EventHandler {
11 child: ElementBox,
12 capture_all: Option<(TypeId, usize)>,
13 mouse_down: Option<Box<dyn FnMut(&mut EventContext) -> bool>>,
14 right_mouse_down: Option<Box<dyn FnMut(&mut EventContext) -> bool>>,
15 navigate_mouse_down: Option<Box<dyn FnMut(NavigationDirection, &mut EventContext) -> bool>>,
16}
17
18impl EventHandler {
19 pub fn new(child: ElementBox) -> Self {
20 Self {
21 child,
22 capture_all: None,
23 mouse_down: None,
24 right_mouse_down: None,
25 navigate_mouse_down: None,
26 }
27 }
28
29 pub fn on_mouse_down<F>(mut self, callback: F) -> Self
30 where
31 F: 'static + FnMut(&mut EventContext) -> bool,
32 {
33 self.mouse_down = Some(Box::new(callback));
34 self
35 }
36
37 pub fn on_right_mouse_down<F>(mut self, callback: F) -> Self
38 where
39 F: 'static + FnMut(&mut EventContext) -> bool,
40 {
41 self.right_mouse_down = Some(Box::new(callback));
42 self
43 }
44
45 pub fn on_navigate_mouse_down<F>(mut self, callback: F) -> Self
46 where
47 F: 'static + FnMut(NavigationDirection, &mut EventContext) -> bool,
48 {
49 self.navigate_mouse_down = Some(Box::new(callback));
50 self
51 }
52
53 pub fn capture_all<T: 'static>(mut self, id: usize) -> Self {
54 self.capture_all = Some((TypeId::of::<T>(), id));
55 self
56 }
57}
58
59impl Element for EventHandler {
60 type LayoutState = ();
61 type PaintState = ();
62
63 fn layout(
64 &mut self,
65 constraint: SizeConstraint,
66 cx: &mut LayoutContext,
67 ) -> (Vector2F, Self::LayoutState) {
68 let size = self.child.layout(constraint, cx);
69 (size, ())
70 }
71
72 fn paint(
73 &mut self,
74 bounds: RectF,
75 visible_bounds: RectF,
76 _: &mut Self::LayoutState,
77 cx: &mut PaintContext,
78 ) -> Self::PaintState {
79 if let Some(discriminant) = self.capture_all {
80 cx.scene.push_stacking_context(None);
81 cx.scene.push_cursor_region(CursorRegion {
82 bounds: visible_bounds,
83 style: Default::default(),
84 });
85 cx.scene.push_mouse_region(MouseRegion {
86 view_id: cx.current_view_id(),
87 discriminant,
88 bounds: visible_bounds,
89 handlers: HandlerSet::capture_all(),
90 hoverable: true,
91 });
92 cx.scene.pop_stacking_context();
93 }
94 self.child.paint(bounds.origin(), visible_bounds, cx);
95 }
96
97 fn dispatch_event(
98 &mut self,
99 event: &Event,
100 _: RectF,
101 visible_bounds: RectF,
102 _: &mut Self::LayoutState,
103 _: &mut Self::PaintState,
104 cx: &mut EventContext,
105 ) -> bool {
106 if self.capture_all.is_some() {
107 return true;
108 }
109
110 if self.child.dispatch_event(event, cx) {
111 true
112 } else {
113 match event {
114 Event::MouseDown(MouseButtonEvent {
115 button: MouseButton::Left,
116 position,
117 ..
118 }) => {
119 if let Some(callback) = self.mouse_down.as_mut() {
120 if visible_bounds.contains_point(*position) {
121 return callback(cx);
122 }
123 }
124 false
125 }
126 Event::MouseDown(MouseButtonEvent {
127 button: MouseButton::Right,
128 position,
129 ..
130 }) => {
131 if let Some(callback) = self.right_mouse_down.as_mut() {
132 if visible_bounds.contains_point(*position) {
133 return callback(cx);
134 }
135 }
136 false
137 }
138 Event::MouseDown(MouseButtonEvent {
139 button: MouseButton::Navigate(direction),
140 position,
141 ..
142 }) => {
143 if let Some(callback) = self.navigate_mouse_down.as_mut() {
144 if visible_bounds.contains_point(*position) {
145 return callback(*direction, cx);
146 }
147 }
148 false
149 }
150 _ => false,
151 }
152 }
153 }
154
155 fn rect_for_text_range(
156 &self,
157 range_utf16: Range<usize>,
158 _: RectF,
159 _: RectF,
160 _: &Self::LayoutState,
161 _: &Self::PaintState,
162 cx: &MeasurementContext,
163 ) -> Option<RectF> {
164 self.child.rect_for_text_range(range_utf16, cx)
165 }
166
167 fn debug(
168 &self,
169 _: RectF,
170 _: &Self::LayoutState,
171 _: &Self::PaintState,
172 cx: &DebugContext,
173 ) -> serde_json::Value {
174 json!({
175 "type": "EventHandler",
176 "child": self.child.debug(cx),
177 })
178 }
179}