1use crate::{CanvasEvent, CanvasKey, CanvasKeyModifiers, PointerButton};
2use open_gpui::{
3 Bounds, Context, DispatchPhase, Entity, FocusHandle, KeyDownEvent, Keystroke, Modifiers,
4 MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point, ScrollWheelEvent,
5 Window, px,
6};
7use std::rc::Rc;
8
9#[derive(Clone, Copy, Debug, PartialEq)]
10pub struct CanvasInputMapper {
11 bounds: Bounds<Pixels>,
12 line_height: Pixels,
13}
14
15impl CanvasInputMapper {
16 pub fn new(bounds: Bounds<Pixels>) -> Self {
17 Self {
18 bounds,
19 line_height: px(16.0),
20 }
21 }
22
23 pub fn with_line_height(mut self, line_height: Pixels) -> Self {
24 self.line_height = line_height;
25 self
26 }
27
28 pub fn mouse_down(&self, event: &MouseDownEvent) -> Option<CanvasEvent> {
29 Some(CanvasEvent::PointerDown {
30 position: self.local_position(event.position)?,
31 button: pointer_button(event.button)?,
32 modifiers: Self::modifiers(event.modifiers),
33 })
34 }
35
36 pub fn mouse_move(&self, event: &MouseMoveEvent) -> Option<CanvasEvent> {
37 Some(CanvasEvent::PointerMove {
38 position: self.local_position(event.position)?,
39 modifiers: Self::modifiers(event.modifiers),
40 })
41 }
42
43 pub fn mouse_up(&self, event: &MouseUpEvent) -> Option<CanvasEvent> {
44 Some(CanvasEvent::PointerUp {
45 position: self.local_position(event.position)?,
46 button: pointer_button(event.button)?,
47 modifiers: Self::modifiers(event.modifiers),
48 })
49 }
50
51 pub fn scroll_wheel(&self, event: &ScrollWheelEvent) -> Option<CanvasEvent> {
52 if self.local_position(event.position).is_none() {
53 return None;
54 }
55
56 Some(CanvasEvent::Wheel {
57 delta: event.delta.pixel_delta(self.line_height),
58 })
59 }
60
61 pub fn key_down_event(event: &KeyDownEvent) -> CanvasEvent {
62 let key = canvas_key(&event.keystroke);
63 if key == CanvasKey::Escape {
64 return CanvasEvent::Cancel;
65 }
66
67 CanvasEvent::KeyDown {
68 key,
69 modifiers: Self::modifiers(event.keystroke.modifiers),
70 repeat: event.is_held,
71 }
72 }
73
74 pub fn modifiers(modifiers: Modifiers) -> CanvasKeyModifiers {
75 canvas_key_modifiers(modifiers)
76 }
77
78 pub fn local_position(&self, position: Point<Pixels>) -> Option<Point<Pixels>> {
79 self.bounds
80 .contains(&position)
81 .then(|| position - self.bounds.origin)
82 }
83}
84
85#[derive(Clone, Copy, Debug, PartialEq)]
86pub struct CanvasEditorInputMapper {
87 mapper: CanvasInputMapper,
88 pointer_interacting: bool,
89}
90
91impl CanvasEditorInputMapper {
92 pub fn new(bounds: Bounds<Pixels>) -> Self {
93 Self {
94 mapper: CanvasInputMapper::new(bounds),
95 pointer_interacting: false,
96 }
97 }
98
99 pub fn with_line_height(mut self, line_height: Pixels) -> Self {
100 self.mapper = self.mapper.with_line_height(line_height);
101 self
102 }
103
104 pub fn with_pointer_interacting(mut self, pointer_interacting: bool) -> Self {
105 self.pointer_interacting = pointer_interacting;
106 self
107 }
108
109 pub fn mouse_down(&self, event: &MouseDownEvent) -> Option<CanvasEvent> {
110 self.mapper.mouse_down(event)
111 }
112
113 pub fn mouse_move(&self, event: &MouseMoveEvent) -> Option<CanvasEvent> {
114 if self.pointer_interacting {
115 return Some(CanvasEvent::PointerMove {
116 position: event.position - self.mapper.bounds.origin,
117 modifiers: CanvasInputMapper::modifiers(event.modifiers),
118 });
119 }
120
121 self.mapper.mouse_move(event)
122 }
123
124 pub fn mouse_up(&self, event: &MouseUpEvent) -> Option<CanvasEvent> {
125 if self.pointer_interacting {
126 return pointer_button(event.button).map(|button| CanvasEvent::PointerUp {
127 position: event.position - self.mapper.bounds.origin,
128 button,
129 modifiers: CanvasInputMapper::modifiers(event.modifiers),
130 });
131 }
132
133 self.mapper.mouse_up(event)
134 }
135
136 pub fn scroll_wheel(&self, event: &ScrollWheelEvent) -> Option<CanvasEvent> {
137 self.mapper.scroll_wheel(event)
138 }
139}
140
141pub struct CanvasEditorInputHandler<T> {
142 pointer_interacting: Rc<dyn Fn(&T) -> bool>,
143 dispatch: Rc<dyn Fn(&mut T, CanvasEvent, &mut Context<T>)>,
144}
145
146impl<T> Clone for CanvasEditorInputHandler<T> {
147 fn clone(&self) -> Self {
148 Self {
149 pointer_interacting: self.pointer_interacting.clone(),
150 dispatch: self.dispatch.clone(),
151 }
152 }
153}
154
155impl<T> CanvasEditorInputHandler<T> {
156 pub fn new(
157 pointer_interacting: impl Fn(&T) -> bool + 'static,
158 dispatch: impl Fn(&mut T, CanvasEvent, &mut Context<T>) + 'static,
159 ) -> Self {
160 Self {
161 pointer_interacting: Rc::new(pointer_interacting),
162 dispatch: Rc::new(dispatch),
163 }
164 }
165
166 pub fn pointer_interacting(&self, target: &T) -> bool {
167 (self.pointer_interacting)(target)
168 }
169
170 pub fn dispatch_event(&self, target: &mut T, event: CanvasEvent, cx: &mut Context<T>) {
171 (self.dispatch)(target, event, cx)
172 }
173
174 pub fn dispatch_key_down(&self, target: &mut T, event: &KeyDownEvent, cx: &mut Context<T>) {
175 self.dispatch_event(target, CanvasInputMapper::key_down_event(event), cx);
176 }
177}
178
179pub fn register_canvas_editor_input<T>(
180 entity: Entity<T>,
181 focus_handle: FocusHandle,
182 bounds: Bounds<Pixels>,
183 handler: CanvasEditorInputHandler<T>,
184 window: &mut Window,
185) where
186 T: 'static,
187{
188 let mapper = CanvasEditorInputMapper::new(bounds);
189
190 window.on_mouse_event({
191 let entity = entity.clone();
192 let handler = handler.clone();
193 move |event: &MouseDownEvent, phase, window, cx| {
194 if phase != DispatchPhase::Bubble {
195 return;
196 }
197
198 let Some(event) = mapper.mouse_down(event) else {
199 return;
200 };
201
202 window.focus(&focus_handle, cx);
203 entity.update(cx, |target, cx| handler.dispatch_event(target, event, cx));
204 }
205 });
206
207 window.on_mouse_event({
208 let entity = entity.clone();
209 let handler = handler.clone();
210 move |event: &MouseMoveEvent, phase, _, cx| {
211 entity.update(cx, |target, cx| {
212 let pointer_interacting = handler.pointer_interacting(target);
213 if pointer_interacting {
214 if phase != DispatchPhase::Capture {
215 return;
216 }
217 let mapper = mapper.with_pointer_interacting(true);
218 if let Some(event) = mapper.mouse_move(event) {
219 handler.dispatch_event(target, event, cx);
220 cx.stop_propagation();
221 }
222 return;
223 }
224
225 if phase == DispatchPhase::Bubble
226 && let Some(event) = mapper.mouse_move(event)
227 {
228 handler.dispatch_event(target, event, cx);
229 }
230 });
231 }
232 });
233
234 window.on_mouse_event({
235 let entity = entity.clone();
236 let handler = handler.clone();
237 move |event: &MouseUpEvent, phase, _, cx| {
238 entity.update(cx, |target, cx| {
239 let pointer_interacting = handler.pointer_interacting(target);
240 if pointer_interacting {
241 if phase != DispatchPhase::Capture {
242 return;
243 }
244 let mapper = mapper.with_pointer_interacting(true);
245 if let Some(event) = mapper.mouse_up(event) {
246 handler.dispatch_event(target, event, cx);
247 cx.stop_propagation();
248 }
249 return;
250 }
251
252 if phase == DispatchPhase::Bubble
253 && let Some(event) = mapper.mouse_up(event)
254 {
255 handler.dispatch_event(target, event, cx);
256 }
257 });
258 }
259 });
260
261 window.on_mouse_event({
262 let entity = entity.clone();
263 let handler = handler.clone();
264 move |event: &ScrollWheelEvent, phase, _, cx| {
265 if phase != DispatchPhase::Bubble {
266 return;
267 }
268
269 if let Some(event) = mapper.scroll_wheel(event) {
270 entity.update(cx, |target, cx| handler.dispatch_event(target, event, cx));
271 }
272 }
273 });
274}
275
276fn pointer_button(button: MouseButton) -> Option<PointerButton> {
277 match button {
278 MouseButton::Left => Some(PointerButton::Primary),
279 MouseButton::Right => Some(PointerButton::Secondary),
280 MouseButton::Middle => Some(PointerButton::Middle),
281 MouseButton::Navigate(_) => None,
282 }
283}
284
285fn canvas_key(keystroke: &Keystroke) -> CanvasKey {
286 match keystroke.key.as_str() {
287 "delete" | "del" => CanvasKey::Delete,
288 "backspace" => CanvasKey::Backspace,
289 "escape" | "esc" => CanvasKey::Escape,
290 "enter" | "return" => CanvasKey::Enter,
291 key if key.chars().count() == 1 => CanvasKey::Character(
292 keystroke
293 .key_char
294 .clone()
295 .unwrap_or_else(|| key.to_string()),
296 ),
297 key => CanvasKey::Named(key.to_string()),
298 }
299}
300
301fn canvas_key_modifiers(modifiers: Modifiers) -> CanvasKeyModifiers {
302 CanvasKeyModifiers {
303 shift: modifiers.shift,
304 alt: modifiers.alt,
305 control: modifiers.control,
306 platform: modifiers.platform,
307 function: modifiers.function,
308 }
309}