1use crate::{
2 point, Action, Bounds, DispatchContext, DispatchPhase, Element, FocusHandle, GroupStyle,
3 Keystroke, Modifiers, Pixels, Point, SharedString, StatefulInteractivity,
4 StatelessInteractivity, StyleRefinement, ViewContext,
5};
6use std::{
7 any::{Any, TypeId},
8 ops::Deref,
9 sync::Arc,
10};
11
12pub trait StatelesslyInteractive: Element {
13 fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<Self::ViewState>;
14
15 fn on_mouse_down(
16 mut self,
17 button: MouseButton,
18 handler: impl Fn(&mut Self::ViewState, &MouseDownEvent, &mut ViewContext<Self::ViewState>)
19 + Send
20 + Sync
21 + 'static,
22 ) -> Self
23 where
24 Self: Sized,
25 {
26 self.stateless_interactivity()
27 .mouse_down_listeners
28 .push(Arc::new(move |view, event, bounds, phase, cx| {
29 if phase == DispatchPhase::Bubble
30 && event.button == button
31 && bounds.contains_point(&event.position)
32 {
33 handler(view, event, cx)
34 }
35 }));
36 self
37 }
38
39 fn on_mouse_up(
40 mut self,
41 button: MouseButton,
42 handler: impl Fn(&mut Self::ViewState, &MouseUpEvent, &mut ViewContext<Self::ViewState>)
43 + Send
44 + Sync
45 + 'static,
46 ) -> Self
47 where
48 Self: Sized,
49 {
50 self.stateless_interactivity()
51 .mouse_up_listeners
52 .push(Arc::new(move |view, event, bounds, phase, cx| {
53 if phase == DispatchPhase::Bubble
54 && event.button == button
55 && bounds.contains_point(&event.position)
56 {
57 handler(view, event, cx)
58 }
59 }));
60 self
61 }
62
63 fn on_mouse_down_out(
64 mut self,
65 button: MouseButton,
66 handler: impl Fn(&mut Self::ViewState, &MouseDownEvent, &mut ViewContext<Self::ViewState>)
67 + Send
68 + Sync
69 + 'static,
70 ) -> Self
71 where
72 Self: Sized,
73 {
74 self.stateless_interactivity()
75 .mouse_down_listeners
76 .push(Arc::new(move |view, event, bounds, phase, cx| {
77 if phase == DispatchPhase::Capture
78 && event.button == button
79 && !bounds.contains_point(&event.position)
80 {
81 handler(view, event, cx)
82 }
83 }));
84 self
85 }
86
87 fn on_mouse_up_out(
88 mut self,
89 button: MouseButton,
90 handler: impl Fn(&mut Self::ViewState, &MouseUpEvent, &mut ViewContext<Self::ViewState>)
91 + Send
92 + Sync
93 + 'static,
94 ) -> Self
95 where
96 Self: Sized,
97 {
98 self.stateless_interactivity()
99 .mouse_up_listeners
100 .push(Arc::new(move |view, event, bounds, phase, cx| {
101 if phase == DispatchPhase::Capture
102 && event.button == button
103 && !bounds.contains_point(&event.position)
104 {
105 handler(view, event, cx);
106 }
107 }));
108 self
109 }
110
111 fn on_mouse_move(
112 mut self,
113 handler: impl Fn(&mut Self::ViewState, &MouseMoveEvent, &mut ViewContext<Self::ViewState>)
114 + Send
115 + Sync
116 + 'static,
117 ) -> Self
118 where
119 Self: Sized,
120 {
121 self.stateless_interactivity()
122 .mouse_move_listeners
123 .push(Arc::new(move |view, event, bounds, phase, cx| {
124 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
125 handler(view, event, cx);
126 }
127 }));
128 self
129 }
130
131 fn on_scroll_wheel(
132 mut self,
133 handler: impl Fn(&mut Self::ViewState, &ScrollWheelEvent, &mut ViewContext<Self::ViewState>)
134 + Send
135 + Sync
136 + 'static,
137 ) -> Self
138 where
139 Self: Sized,
140 {
141 self.stateless_interactivity()
142 .scroll_wheel_listeners
143 .push(Arc::new(move |view, event, bounds, phase, cx| {
144 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
145 handler(view, event, cx);
146 }
147 }));
148 self
149 }
150
151 fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
152 where
153 Self: Sized,
154 {
155 self.stateless_interactivity().hover_style = f(StyleRefinement::default());
156 self
157 }
158
159 fn group_hover(
160 mut self,
161 group_name: impl Into<SharedString>,
162 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
163 ) -> Self
164 where
165 Self: Sized,
166 {
167 self.stateless_interactivity().group_hover_style = Some(GroupStyle {
168 group: group_name.into(),
169 style: f(StyleRefinement::default()),
170 });
171 self
172 }
173
174 fn on_key_down(
175 mut self,
176 listener: impl Fn(
177 &mut Self::ViewState,
178 &KeyDownEvent,
179 DispatchPhase,
180 &mut ViewContext<Self::ViewState>,
181 ) + Send
182 + Sync
183 + 'static,
184 ) -> Self
185 where
186 Self: Sized,
187 {
188 self.stateless_interactivity().key_listeners.push((
189 TypeId::of::<KeyDownEvent>(),
190 Arc::new(move |view, event, _, phase, cx| {
191 let event = event.downcast_ref().unwrap();
192 listener(view, event, phase, cx);
193 None
194 }),
195 ));
196 self
197 }
198
199 fn on_key_up(
200 mut self,
201 listener: impl Fn(&mut Self::ViewState, &KeyUpEvent, DispatchPhase, &mut ViewContext<Self::ViewState>)
202 + Send
203 + Sync
204 + 'static,
205 ) -> Self
206 where
207 Self: Sized,
208 {
209 self.stateless_interactivity().key_listeners.push((
210 TypeId::of::<KeyUpEvent>(),
211 Arc::new(move |view, event, _, phase, cx| {
212 let event = event.downcast_ref().unwrap();
213 listener(view, event, phase, cx);
214 None
215 }),
216 ));
217 self
218 }
219
220 fn on_action<A: 'static>(
221 mut self,
222 listener: impl Fn(&mut Self::ViewState, &A, DispatchPhase, &mut ViewContext<Self::ViewState>)
223 + Send
224 + Sync
225 + 'static,
226 ) -> Self
227 where
228 Self: Sized,
229 {
230 self.stateless_interactivity().key_listeners.push((
231 TypeId::of::<A>(),
232 Arc::new(move |view, event, _, phase, cx| {
233 let event = event.downcast_ref().unwrap();
234 listener(view, event, phase, cx);
235 None
236 }),
237 ));
238 self
239 }
240}
241
242pub trait StatefullyInteractive: StatelesslyInteractive {
243 fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<Self::ViewState>;
244
245 fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
246 where
247 Self: Sized,
248 {
249 self.stateful_interactivity().active_style = f(StyleRefinement::default());
250 self
251 }
252
253 fn group_active(
254 mut self,
255 group_name: impl Into<SharedString>,
256 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
257 ) -> Self
258 where
259 Self: Sized,
260 {
261 self.stateful_interactivity().group_active_style = Some(GroupStyle {
262 group: group_name.into(),
263 style: f(StyleRefinement::default()),
264 });
265 self
266 }
267
268 fn on_click(
269 mut self,
270 handler: impl Fn(&mut Self::ViewState, &MouseClickEvent, &mut ViewContext<Self::ViewState>)
271 + Send
272 + Sync
273 + 'static,
274 ) -> Self
275 where
276 Self: Sized,
277 {
278 self.stateful_interactivity()
279 .mouse_click_listeners
280 .push(Arc::new(move |view, event, cx| handler(view, event, cx)));
281 self
282 }
283}
284
285#[derive(Clone, Debug, Eq, PartialEq)]
286pub struct KeyDownEvent {
287 pub keystroke: Keystroke,
288 pub is_held: bool,
289}
290
291#[derive(Clone, Debug)]
292pub struct KeyUpEvent {
293 pub keystroke: Keystroke,
294}
295
296#[derive(Clone, Debug, Default)]
297pub struct ModifiersChangedEvent {
298 pub modifiers: Modifiers,
299}
300
301impl Deref for ModifiersChangedEvent {
302 type Target = Modifiers;
303
304 fn deref(&self) -> &Self::Target {
305 &self.modifiers
306 }
307}
308
309/// The phase of a touch motion event.
310/// Based on the winit enum of the same name.
311#[derive(Clone, Copy, Debug)]
312pub enum TouchPhase {
313 Started,
314 Moved,
315 Ended,
316}
317
318#[derive(Clone, Debug, Default)]
319pub struct MouseDownEvent {
320 pub button: MouseButton,
321 pub position: Point<Pixels>,
322 pub modifiers: Modifiers,
323 pub click_count: usize,
324}
325
326#[derive(Clone, Debug, Default)]
327pub struct MouseUpEvent {
328 pub button: MouseButton,
329 pub position: Point<Pixels>,
330 pub modifiers: Modifiers,
331 pub click_count: usize,
332}
333
334#[derive(Clone, Debug, Default)]
335pub struct MouseClickEvent {
336 pub down: MouseDownEvent,
337 pub up: MouseUpEvent,
338}
339
340#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
341pub enum MouseButton {
342 Left,
343 Right,
344 Middle,
345 Navigate(NavigationDirection),
346}
347
348impl MouseButton {
349 pub fn all() -> Vec<Self> {
350 vec![
351 MouseButton::Left,
352 MouseButton::Right,
353 MouseButton::Middle,
354 MouseButton::Navigate(NavigationDirection::Back),
355 MouseButton::Navigate(NavigationDirection::Forward),
356 ]
357 }
358}
359
360impl Default for MouseButton {
361 fn default() -> Self {
362 Self::Left
363 }
364}
365
366#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
367pub enum NavigationDirection {
368 Back,
369 Forward,
370}
371
372impl Default for NavigationDirection {
373 fn default() -> Self {
374 Self::Back
375 }
376}
377
378#[derive(Clone, Debug, Default)]
379pub struct MouseMoveEvent {
380 pub position: Point<Pixels>,
381 pub pressed_button: Option<MouseButton>,
382 pub modifiers: Modifiers,
383}
384
385#[derive(Clone, Debug)]
386pub struct ScrollWheelEvent {
387 pub position: Point<Pixels>,
388 pub delta: ScrollDelta,
389 pub modifiers: Modifiers,
390 pub touch_phase: TouchPhase,
391}
392
393impl Deref for ScrollWheelEvent {
394 type Target = Modifiers;
395
396 fn deref(&self) -> &Self::Target {
397 &self.modifiers
398 }
399}
400
401#[derive(Clone, Copy, Debug)]
402pub enum ScrollDelta {
403 Pixels(Point<Pixels>),
404 Lines(Point<f32>),
405}
406
407impl Default for ScrollDelta {
408 fn default() -> Self {
409 Self::Lines(Default::default())
410 }
411}
412
413impl ScrollDelta {
414 pub fn precise(&self) -> bool {
415 match self {
416 ScrollDelta::Pixels(_) => true,
417 ScrollDelta::Lines(_) => false,
418 }
419 }
420
421 pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
422 match self {
423 ScrollDelta::Pixels(delta) => *delta,
424 ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
425 }
426 }
427}
428
429#[derive(Clone, Debug, Default)]
430pub struct MouseExitEvent {
431 pub position: Point<Pixels>,
432 pub pressed_button: Option<MouseButton>,
433 pub modifiers: Modifiers,
434}
435
436impl Deref for MouseExitEvent {
437 type Target = Modifiers;
438
439 fn deref(&self) -> &Self::Target {
440 &self.modifiers
441 }
442}
443
444#[derive(Clone, Debug)]
445pub enum InputEvent {
446 KeyDown(KeyDownEvent),
447 KeyUp(KeyUpEvent),
448 ModifiersChanged(ModifiersChangedEvent),
449 MouseDown(MouseDownEvent),
450 MouseUp(MouseUpEvent),
451 MouseMoved(MouseMoveEvent),
452 MouseExited(MouseExitEvent),
453 ScrollWheel(ScrollWheelEvent),
454}
455
456impl InputEvent {
457 pub fn position(&self) -> Option<Point<Pixels>> {
458 match self {
459 InputEvent::KeyDown { .. } => None,
460 InputEvent::KeyUp { .. } => None,
461 InputEvent::ModifiersChanged { .. } => None,
462 InputEvent::MouseDown(event) => Some(event.position),
463 InputEvent::MouseUp(event) => Some(event.position),
464 InputEvent::MouseMoved(event) => Some(event.position),
465 InputEvent::MouseExited(event) => Some(event.position),
466 InputEvent::ScrollWheel(event) => Some(event.position),
467 }
468 }
469
470 pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
471 match self {
472 InputEvent::KeyDown { .. } => None,
473 InputEvent::KeyUp { .. } => None,
474 InputEvent::ModifiersChanged { .. } => None,
475 InputEvent::MouseDown(event) => Some(event),
476 InputEvent::MouseUp(event) => Some(event),
477 InputEvent::MouseMoved(event) => Some(event),
478 InputEvent::MouseExited(event) => Some(event),
479 InputEvent::ScrollWheel(event) => Some(event),
480 }
481 }
482
483 pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> {
484 match self {
485 InputEvent::KeyDown(event) => Some(event),
486 InputEvent::KeyUp(event) => Some(event),
487 InputEvent::ModifiersChanged(event) => Some(event),
488 InputEvent::MouseDown(_) => None,
489 InputEvent::MouseUp(_) => None,
490 InputEvent::MouseMoved(_) => None,
491 InputEvent::MouseExited(_) => None,
492 InputEvent::ScrollWheel(_) => None,
493 }
494 }
495}
496
497pub struct FocusEvent {
498 pub blurred: Option<FocusHandle>,
499 pub focused: Option<FocusHandle>,
500}
501
502pub type MouseDownListener<V> = Arc<
503 dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
504 + Send
505 + Sync
506 + 'static,
507>;
508pub type MouseUpListener<V> = Arc<
509 dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
510 + Send
511 + Sync
512 + 'static,
513>;
514pub type MouseClickListener<V> =
515 Arc<dyn Fn(&mut V, &MouseClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
516
517pub type MouseMoveListener<V> = Arc<
518 dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
519 + Send
520 + Sync
521 + 'static,
522>;
523
524pub type ScrollWheelListener<V> = Arc<
525 dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
526 + Send
527 + Sync
528 + 'static,
529>;
530
531pub type KeyListener<V> = Arc<
532 dyn Fn(
533 &mut V,
534 &dyn Any,
535 &[&DispatchContext],
536 DispatchPhase,
537 &mut ViewContext<V>,
538 ) -> Option<Box<dyn Action>>
539 + Send
540 + Sync
541 + 'static,
542>;