1use crate::{
2 geometry::vector::Vector2F, presenter::MeasurementContext, CursorRegion, DebugContext, Element,
3 ElementBox, Event, EventContext, LayoutContext, MouseButton, MouseButtonEvent, MouseRegion,
4 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::handle_all(
86 cx.current_view_id(),
87 Some(discriminant),
88 visible_bounds,
89 ));
90 cx.scene.pop_stacking_context();
91 }
92 self.child.paint(bounds.origin(), visible_bounds, cx);
93 }
94
95 fn dispatch_event(
96 &mut self,
97 event: &Event,
98 _: RectF,
99 visible_bounds: RectF,
100 _: &mut Self::LayoutState,
101 _: &mut Self::PaintState,
102 cx: &mut EventContext,
103 ) -> bool {
104 if self.capture_all.is_some() {
105 return true;
106 }
107
108 if self.child.dispatch_event(event, cx) {
109 true
110 } else {
111 match event {
112 Event::MouseDown(MouseButtonEvent {
113 button: MouseButton::Left,
114 position,
115 ..
116 }) => {
117 if let Some(callback) = self.mouse_down.as_mut() {
118 if visible_bounds.contains_point(*position) {
119 return callback(cx);
120 }
121 }
122 false
123 }
124 Event::MouseDown(MouseButtonEvent {
125 button: MouseButton::Right,
126 position,
127 ..
128 }) => {
129 if let Some(callback) = self.right_mouse_down.as_mut() {
130 if visible_bounds.contains_point(*position) {
131 return callback(cx);
132 }
133 }
134 false
135 }
136 Event::MouseDown(MouseButtonEvent {
137 button: MouseButton::Navigate(direction),
138 position,
139 ..
140 }) => {
141 if let Some(callback) = self.navigate_mouse_down.as_mut() {
142 if visible_bounds.contains_point(*position) {
143 return callback(*direction, cx);
144 }
145 }
146 false
147 }
148 _ => false,
149 }
150 }
151 }
152
153 fn rect_for_text_range(
154 &self,
155 range_utf16: Range<usize>,
156 _: RectF,
157 _: RectF,
158 _: &Self::LayoutState,
159 _: &Self::PaintState,
160 cx: &MeasurementContext,
161 ) -> Option<RectF> {
162 self.child.rect_for_text_range(range_utf16, cx)
163 }
164
165 fn debug(
166 &self,
167 _: RectF,
168 _: &Self::LayoutState,
169 _: &Self::PaintState,
170 cx: &DebugContext,
171 ) -> serde_json::Value {
172 json!({
173 "type": "EventHandler",
174 "child": self.child.debug(cx),
175 })
176 }
177}