1use crate::{
2 div, point, px, Action, AnyDrag, AnyView, AppContext, BorrowWindow, Bounds, Component,
3 DispatchContext, DispatchPhase, Div, Element, ElementId, FocusHandle, KeyMatch, Keystroke,
4 Modifiers, Overflow, Pixels, Point, Render, SharedString, Size, Style, StyleRefinement, View,
5 ViewContext,
6};
7use collections::HashMap;
8use derive_more::{Deref, DerefMut};
9use parking_lot::Mutex;
10use refineable::Refineable;
11use smallvec::SmallVec;
12use std::{
13 any::{Any, TypeId},
14 fmt::Debug,
15 marker::PhantomData,
16 mem,
17 ops::Deref,
18 path::PathBuf,
19 sync::Arc,
20};
21
22const DRAG_THRESHOLD: f64 = 2.;
23
24pub trait StatelessInteractive<V: 'static>: Element<V> {
25 fn stateless_interaction(&mut self) -> &mut StatelessInteraction<V>;
26
27 fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
28 where
29 Self: Sized,
30 {
31 self.stateless_interaction().hover_style = f(StyleRefinement::default());
32 self
33 }
34
35 fn group_hover(
36 mut self,
37 group_name: impl Into<SharedString>,
38 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
39 ) -> Self
40 where
41 Self: Sized,
42 {
43 self.stateless_interaction().group_hover_style = Some(GroupStyle {
44 group: group_name.into(),
45 style: f(StyleRefinement::default()),
46 });
47 self
48 }
49
50 fn on_mouse_down(
51 mut self,
52 button: MouseButton,
53 handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
54 ) -> Self
55 where
56 Self: Sized,
57 {
58 self.stateless_interaction()
59 .mouse_down_listeners
60 .push(Box::new(move |view, event, bounds, phase, cx| {
61 if phase == DispatchPhase::Bubble
62 && event.button == button
63 && bounds.contains_point(&event.position)
64 {
65 handler(view, event, cx)
66 }
67 }));
68 self
69 }
70
71 fn on_mouse_up(
72 mut self,
73 button: MouseButton,
74 handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
75 ) -> Self
76 where
77 Self: Sized,
78 {
79 self.stateless_interaction()
80 .mouse_up_listeners
81 .push(Box::new(move |view, event, bounds, phase, cx| {
82 if phase == DispatchPhase::Bubble
83 && event.button == button
84 && bounds.contains_point(&event.position)
85 {
86 handler(view, event, cx)
87 }
88 }));
89 self
90 }
91
92 fn on_mouse_down_out(
93 mut self,
94 button: MouseButton,
95 handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
96 ) -> Self
97 where
98 Self: Sized,
99 {
100 self.stateless_interaction()
101 .mouse_down_listeners
102 .push(Box::new(move |view, event, bounds, phase, cx| {
103 if phase == DispatchPhase::Capture
104 && event.button == button
105 && !bounds.contains_point(&event.position)
106 {
107 handler(view, event, cx)
108 }
109 }));
110 self
111 }
112
113 fn on_mouse_up_out(
114 mut self,
115 button: MouseButton,
116 handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
117 ) -> Self
118 where
119 Self: Sized,
120 {
121 self.stateless_interaction()
122 .mouse_up_listeners
123 .push(Box::new(move |view, event, bounds, phase, cx| {
124 if phase == DispatchPhase::Capture
125 && event.button == button
126 && !bounds.contains_point(&event.position)
127 {
128 handler(view, event, cx);
129 }
130 }));
131 self
132 }
133
134 fn on_mouse_move(
135 mut self,
136 handler: impl Fn(&mut V, &MouseMoveEvent, &mut ViewContext<V>) + 'static,
137 ) -> Self
138 where
139 Self: Sized,
140 {
141 self.stateless_interaction()
142 .mouse_move_listeners
143 .push(Box::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 on_scroll_wheel(
152 mut self,
153 handler: impl Fn(&mut V, &ScrollWheelEvent, &mut ViewContext<V>) + 'static,
154 ) -> Self
155 where
156 Self: Sized,
157 {
158 self.stateless_interaction()
159 .scroll_wheel_listeners
160 .push(Box::new(move |view, event, bounds, phase, cx| {
161 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
162 handler(view, event, cx);
163 }
164 }));
165 self
166 }
167
168 fn context<C>(mut self, context: C) -> Self
169 where
170 Self: Sized,
171 C: TryInto<DispatchContext>,
172 C::Error: Debug,
173 {
174 self.stateless_interaction().dispatch_context =
175 context.try_into().expect("invalid dispatch context");
176 self
177 }
178
179 fn on_action<A: 'static>(
180 mut self,
181 listener: impl Fn(&mut V, &A, DispatchPhase, &mut ViewContext<V>) + 'static,
182 ) -> Self
183 where
184 Self: Sized,
185 {
186 self.stateless_interaction().key_listeners.push((
187 TypeId::of::<A>(),
188 Box::new(move |view, event, _, phase, cx| {
189 let event = event.downcast_ref().unwrap();
190 listener(view, event, phase, cx);
191 None
192 }),
193 ));
194 self
195 }
196
197 fn on_key_down(
198 mut self,
199 listener: impl Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + 'static,
200 ) -> Self
201 where
202 Self: Sized,
203 {
204 self.stateless_interaction().key_listeners.push((
205 TypeId::of::<KeyDownEvent>(),
206 Box::new(move |view, event, _, phase, cx| {
207 let event = event.downcast_ref().unwrap();
208 listener(view, event, phase, cx);
209 None
210 }),
211 ));
212 self
213 }
214
215 fn on_key_up(
216 mut self,
217 listener: impl Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + 'static,
218 ) -> Self
219 where
220 Self: Sized,
221 {
222 self.stateless_interaction().key_listeners.push((
223 TypeId::of::<KeyUpEvent>(),
224 Box::new(move |view, event, _, phase, cx| {
225 let event = event.downcast_ref().unwrap();
226 listener(view, event, phase, cx);
227 None
228 }),
229 ));
230 self
231 }
232
233 fn drag_over<S: 'static>(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
234 where
235 Self: Sized,
236 {
237 self.stateless_interaction()
238 .drag_over_styles
239 .push((TypeId::of::<S>(), f(StyleRefinement::default())));
240 self
241 }
242
243 fn group_drag_over<S: 'static>(
244 mut self,
245 group_name: impl Into<SharedString>,
246 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
247 ) -> Self
248 where
249 Self: Sized,
250 {
251 self.stateless_interaction().group_drag_over_styles.push((
252 TypeId::of::<S>(),
253 GroupStyle {
254 group: group_name.into(),
255 style: f(StyleRefinement::default()),
256 },
257 ));
258 self
259 }
260
261 fn on_drop<W: 'static>(
262 mut self,
263 listener: impl Fn(&mut V, View<W>, &mut ViewContext<V>) + 'static,
264 ) -> Self
265 where
266 Self: Sized,
267 {
268 self.stateless_interaction().drop_listeners.push((
269 TypeId::of::<W>(),
270 Box::new(move |view, dragged_view, cx| {
271 listener(view, dragged_view.downcast().unwrap(), cx);
272 }),
273 ));
274 self
275 }
276}
277
278pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
279 fn stateful_interaction(&mut self) -> &mut StatefulInteraction<V>;
280
281 fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
282 where
283 Self: Sized,
284 {
285 self.stateful_interaction().active_style = f(StyleRefinement::default());
286 self
287 }
288
289 fn group_active(
290 mut self,
291 group_name: impl Into<SharedString>,
292 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
293 ) -> Self
294 where
295 Self: Sized,
296 {
297 self.stateful_interaction().group_active_style = Some(GroupStyle {
298 group: group_name.into(),
299 style: f(StyleRefinement::default()),
300 });
301 self
302 }
303
304 fn on_click(
305 mut self,
306 listener: impl Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static,
307 ) -> Self
308 where
309 Self: Sized,
310 {
311 self.stateful_interaction()
312 .click_listeners
313 .push(Box::new(move |view, event, cx| listener(view, event, cx)));
314 self
315 }
316
317 fn on_drag<W>(
318 mut self,
319 listener: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
320 ) -> Self
321 where
322 Self: Sized,
323 W: 'static + Render,
324 {
325 debug_assert!(
326 self.stateful_interaction().drag_listener.is_none(),
327 "calling on_drag more than once on the same element is not supported"
328 );
329 self.stateful_interaction().drag_listener =
330 Some(Box::new(move |view_state, cursor_offset, cx| AnyDrag {
331 view: listener(view_state, cx).into(),
332 cursor_offset,
333 }));
334 self
335 }
336}
337
338pub trait ElementInteraction<V: 'static>: 'static {
339 fn as_stateless(&self) -> &StatelessInteraction<V>;
340 fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V>;
341 fn as_stateful(&self) -> Option<&StatefulInteraction<V>>;
342 fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>>;
343
344 fn initialize<R>(
345 &mut self,
346 cx: &mut ViewContext<V>,
347 f: impl FnOnce(&mut ViewContext<V>) -> R,
348 ) -> R {
349 if let Some(stateful) = self.as_stateful_mut() {
350 cx.with_element_id(stateful.id.clone(), |global_id, cx| {
351 stateful.key_listeners.push((
352 TypeId::of::<KeyDownEvent>(),
353 Box::new(move |_, key_down, context, phase, cx| {
354 if phase == DispatchPhase::Bubble {
355 let key_down = key_down.downcast_ref::<KeyDownEvent>().unwrap();
356 if let KeyMatch::Some(action) =
357 cx.match_keystroke(&global_id, &key_down.keystroke, context)
358 {
359 return Some(action);
360 }
361 }
362
363 None
364 }),
365 ));
366 let result = stateful.stateless.initialize(cx, f);
367 stateful.key_listeners.pop();
368 result
369 })
370 } else {
371 let stateless = self.as_stateless_mut();
372 cx.with_key_dispatch_context(stateless.dispatch_context.clone(), |cx| {
373 cx.with_key_listeners(mem::take(&mut stateless.key_listeners), f)
374 })
375 }
376 }
377
378 fn refine_style(
379 &self,
380 style: &mut Style,
381 bounds: Bounds<Pixels>,
382 element_state: &InteractiveElementState,
383 cx: &mut ViewContext<V>,
384 ) {
385 let mouse_position = cx.mouse_position();
386 let stateless = self.as_stateless();
387 if let Some(group_hover) = stateless.group_hover_style.as_ref() {
388 if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
389 if group_bounds.contains_point(&mouse_position) {
390 style.refine(&group_hover.style);
391 }
392 }
393 }
394 if bounds.contains_point(&mouse_position) {
395 style.refine(&stateless.hover_style);
396 }
397
398 if let Some(drag) = cx.active_drag.take() {
399 for (state_type, group_drag_style) in &self.as_stateless().group_drag_over_styles {
400 if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) {
401 if *state_type == drag.view.entity_type()
402 && group_bounds.contains_point(&mouse_position)
403 {
404 style.refine(&group_drag_style.style);
405 }
406 }
407 }
408
409 for (state_type, drag_over_style) in &self.as_stateless().drag_over_styles {
410 if *state_type == drag.view.entity_type() && bounds.contains_point(&mouse_position)
411 {
412 style.refine(drag_over_style);
413 }
414 }
415
416 cx.active_drag = Some(drag);
417 }
418
419 if let Some(stateful) = self.as_stateful() {
420 let active_state = element_state.active_state.lock();
421 if active_state.group {
422 if let Some(group_style) = stateful.group_active_style.as_ref() {
423 style.refine(&group_style.style);
424 }
425 }
426 if active_state.element {
427 style.refine(&stateful.active_style);
428 }
429 }
430 }
431
432 fn paint(
433 &mut self,
434 bounds: Bounds<Pixels>,
435 content_size: Size<Pixels>,
436 overflow: Point<Overflow>,
437 element_state: &mut InteractiveElementState,
438 cx: &mut ViewContext<V>,
439 ) {
440 let stateless = self.as_stateless_mut();
441 for listener in stateless.mouse_down_listeners.drain(..) {
442 cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
443 listener(state, event, &bounds, phase, cx);
444 })
445 }
446
447 for listener in stateless.mouse_up_listeners.drain(..) {
448 cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
449 listener(state, event, &bounds, phase, cx);
450 })
451 }
452
453 for listener in stateless.mouse_move_listeners.drain(..) {
454 cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
455 listener(state, event, &bounds, phase, cx);
456 })
457 }
458
459 for listener in stateless.scroll_wheel_listeners.drain(..) {
460 cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
461 listener(state, event, &bounds, phase, cx);
462 })
463 }
464
465 let hover_group_bounds = stateless
466 .group_hover_style
467 .as_ref()
468 .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
469
470 if let Some(group_bounds) = hover_group_bounds {
471 let hovered = group_bounds.contains_point(&cx.mouse_position());
472 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
473 if phase == DispatchPhase::Capture {
474 if group_bounds.contains_point(&event.position) != hovered {
475 cx.notify();
476 }
477 }
478 });
479 }
480
481 if stateless.hover_style.is_some()
482 || (cx.active_drag.is_some() && !stateless.drag_over_styles.is_empty())
483 {
484 let hovered = bounds.contains_point(&cx.mouse_position());
485 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
486 if phase == DispatchPhase::Capture {
487 if bounds.contains_point(&event.position) != hovered {
488 cx.notify();
489 }
490 }
491 });
492 }
493
494 if cx.active_drag.is_some() {
495 let drop_listeners = mem::take(&mut stateless.drop_listeners);
496 cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| {
497 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
498 if let Some(drag_state_type) =
499 cx.active_drag.as_ref().map(|drag| drag.view.entity_type())
500 {
501 for (drop_state_type, listener) in &drop_listeners {
502 if *drop_state_type == drag_state_type {
503 let drag = cx
504 .active_drag
505 .take()
506 .expect("checked for type drag state type above");
507 listener(view, drag.view.clone(), cx);
508 cx.notify();
509 cx.stop_propagation();
510 }
511 }
512 }
513 }
514 });
515 }
516
517 if let Some(stateful) = self.as_stateful_mut() {
518 let click_listeners = mem::take(&mut stateful.click_listeners);
519 let drag_listener = mem::take(&mut stateful.drag_listener);
520
521 if !click_listeners.is_empty() || drag_listener.is_some() {
522 let pending_mouse_down = element_state.pending_mouse_down.clone();
523 let mouse_down = pending_mouse_down.lock().clone();
524 if let Some(mouse_down) = mouse_down {
525 if let Some(drag_listener) = drag_listener {
526 let active_state = element_state.active_state.clone();
527
528 cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
529 if cx.active_drag.is_some() {
530 if phase == DispatchPhase::Capture {
531 cx.notify();
532 }
533 } else if phase == DispatchPhase::Bubble
534 && bounds.contains_point(&event.position)
535 && (event.position - mouse_down.position).magnitude()
536 > DRAG_THRESHOLD
537 {
538 *active_state.lock() = ActiveState::default();
539 let cursor_offset = event.position - bounds.origin;
540 let drag = drag_listener(view_state, cursor_offset, cx);
541 cx.active_drag = Some(drag);
542 cx.notify();
543 cx.stop_propagation();
544 }
545 });
546 }
547
548 cx.on_mouse_event(move |view_state, event: &MouseUpEvent, phase, cx| {
549 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position)
550 {
551 let mouse_click = ClickEvent {
552 down: mouse_down.clone(),
553 up: event.clone(),
554 };
555 for listener in &click_listeners {
556 listener(view_state, &mouse_click, cx);
557 }
558 }
559 *pending_mouse_down.lock() = None;
560 });
561 } else {
562 cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| {
563 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position)
564 {
565 *pending_mouse_down.lock() = Some(event.clone());
566 }
567 });
568 }
569 }
570
571 let active_state = element_state.active_state.clone();
572 if active_state.lock().is_none() {
573 let active_group_bounds = stateful
574 .group_active_style
575 .as_ref()
576 .and_then(|group_active| GroupBounds::get(&group_active.group, cx));
577 cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
578 if phase == DispatchPhase::Bubble {
579 let group = active_group_bounds
580 .map_or(false, |bounds| bounds.contains_point(&down.position));
581 let element = bounds.contains_point(&down.position);
582 if group || element {
583 *active_state.lock() = ActiveState { group, element };
584 cx.notify();
585 }
586 }
587 });
588 } else {
589 cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
590 if phase == DispatchPhase::Capture {
591 *active_state.lock() = ActiveState::default();
592 cx.notify();
593 }
594 });
595 }
596
597 if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
598 let scroll_offset = element_state
599 .scroll_offset
600 .get_or_insert_with(Arc::default)
601 .clone();
602 let line_height = cx.line_height();
603 let scroll_max = (content_size - bounds.size).max(&Size::default());
604
605 cx.on_mouse_event(move |_, event: &ScrollWheelEvent, phase, cx| {
606 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
607 let mut scroll_offset = scroll_offset.lock();
608 let old_scroll_offset = *scroll_offset;
609 let delta = event.delta.pixel_delta(line_height);
610
611 if overflow.x == Overflow::Scroll {
612 scroll_offset.x =
613 (scroll_offset.x + delta.x).clamp(-scroll_max.width, px(0.));
614 }
615
616 if overflow.y == Overflow::Scroll {
617 scroll_offset.y =
618 (scroll_offset.y + delta.y).clamp(-scroll_max.height, px(0.));
619 }
620
621 if *scroll_offset != old_scroll_offset {
622 cx.notify();
623 cx.stop_propagation();
624 }
625 }
626 });
627 }
628 }
629 }
630}
631
632#[derive(Deref, DerefMut)]
633pub struct StatefulInteraction<V> {
634 pub id: ElementId,
635 #[deref]
636 #[deref_mut]
637 stateless: StatelessInteraction<V>,
638 click_listeners: SmallVec<[ClickListener<V>; 2]>,
639 active_style: StyleRefinement,
640 group_active_style: Option<GroupStyle>,
641 drag_listener: Option<DragListener<V>>,
642}
643
644impl<V: 'static> ElementInteraction<V> for StatefulInteraction<V> {
645 fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
646 Some(self)
647 }
648
649 fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
650 Some(self)
651 }
652
653 fn as_stateless(&self) -> &StatelessInteraction<V> {
654 &self.stateless
655 }
656
657 fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
658 &mut self.stateless
659 }
660}
661
662impl<V> From<ElementId> for StatefulInteraction<V> {
663 fn from(id: ElementId) -> Self {
664 Self {
665 id,
666 stateless: StatelessInteraction::default(),
667 click_listeners: SmallVec::new(),
668 drag_listener: None,
669 active_style: StyleRefinement::default(),
670 group_active_style: None,
671 }
672 }
673}
674
675type DropListener<V> = dyn Fn(&mut V, AnyView, &mut ViewContext<V>) + 'static;
676
677pub struct StatelessInteraction<V> {
678 pub dispatch_context: DispatchContext,
679 pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
680 pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
681 pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
682 pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
683 pub key_listeners: SmallVec<[(TypeId, KeyListener<V>); 32]>,
684 pub hover_style: StyleRefinement,
685 pub group_hover_style: Option<GroupStyle>,
686 drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
687 group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
688 drop_listeners: SmallVec<[(TypeId, Box<DropListener<V>>); 2]>,
689}
690
691impl<V> StatelessInteraction<V> {
692 pub fn into_stateful(self, id: impl Into<ElementId>) -> StatefulInteraction<V> {
693 StatefulInteraction {
694 id: id.into(),
695 stateless: self,
696 click_listeners: SmallVec::new(),
697 drag_listener: None,
698 active_style: StyleRefinement::default(),
699 group_active_style: None,
700 }
701 }
702}
703
704pub struct GroupStyle {
705 pub group: SharedString,
706 pub style: StyleRefinement,
707}
708
709#[derive(Default)]
710pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
711
712impl GroupBounds {
713 pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
714 cx.default_global::<Self>()
715 .0
716 .get(name)
717 .and_then(|bounds_stack| bounds_stack.last())
718 .cloned()
719 }
720
721 pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
722 cx.default_global::<Self>()
723 .0
724 .entry(name)
725 .or_default()
726 .push(bounds);
727 }
728
729 pub fn pop(name: &SharedString, cx: &mut AppContext) {
730 cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
731 }
732}
733
734#[derive(Copy, Clone, Default, Eq, PartialEq)]
735struct ActiveState {
736 pub group: bool,
737 pub element: bool,
738}
739
740impl ActiveState {
741 pub fn is_none(&self) -> bool {
742 !self.group && !self.element
743 }
744}
745
746#[derive(Default)]
747pub struct InteractiveElementState {
748 active_state: Arc<Mutex<ActiveState>>,
749 pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
750 scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
751}
752
753impl InteractiveElementState {
754 pub fn scroll_offset(&self) -> Option<Point<Pixels>> {
755 self.scroll_offset
756 .as_ref()
757 .map(|offset| offset.lock().clone())
758 }
759}
760
761impl<V> Default for StatelessInteraction<V> {
762 fn default() -> Self {
763 Self {
764 dispatch_context: DispatchContext::default(),
765 mouse_down_listeners: SmallVec::new(),
766 mouse_up_listeners: SmallVec::new(),
767 mouse_move_listeners: SmallVec::new(),
768 scroll_wheel_listeners: SmallVec::new(),
769 key_listeners: SmallVec::new(),
770 hover_style: StyleRefinement::default(),
771 group_hover_style: None,
772 drag_over_styles: SmallVec::new(),
773 group_drag_over_styles: SmallVec::new(),
774 drop_listeners: SmallVec::new(),
775 }
776 }
777}
778
779impl<V: 'static> ElementInteraction<V> for StatelessInteraction<V> {
780 fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
781 None
782 }
783
784 fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
785 None
786 }
787
788 fn as_stateless(&self) -> &StatelessInteraction<V> {
789 self
790 }
791
792 fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
793 self
794 }
795}
796
797#[derive(Clone, Debug, Eq, PartialEq)]
798pub struct KeyDownEvent {
799 pub keystroke: Keystroke,
800 pub is_held: bool,
801}
802
803#[derive(Clone, Debug)]
804pub struct KeyUpEvent {
805 pub keystroke: Keystroke,
806}
807
808#[derive(Clone, Debug, Default)]
809pub struct ModifiersChangedEvent {
810 pub modifiers: Modifiers,
811}
812
813impl Deref for ModifiersChangedEvent {
814 type Target = Modifiers;
815
816 fn deref(&self) -> &Self::Target {
817 &self.modifiers
818 }
819}
820
821/// The phase of a touch motion event.
822/// Based on the winit enum of the same name.
823#[derive(Clone, Copy, Debug)]
824pub enum TouchPhase {
825 Started,
826 Moved,
827 Ended,
828}
829
830#[derive(Clone, Debug, Default)]
831pub struct MouseDownEvent {
832 pub button: MouseButton,
833 pub position: Point<Pixels>,
834 pub modifiers: Modifiers,
835 pub click_count: usize,
836}
837
838#[derive(Clone, Debug, Default)]
839pub struct MouseUpEvent {
840 pub button: MouseButton,
841 pub position: Point<Pixels>,
842 pub modifiers: Modifiers,
843 pub click_count: usize,
844}
845
846#[derive(Clone, Debug, Default)]
847pub struct ClickEvent {
848 pub down: MouseDownEvent,
849 pub up: MouseUpEvent,
850}
851
852pub struct Drag<S, R, V, E>
853where
854 R: Fn(&mut V, &mut ViewContext<V>) -> E,
855 V: 'static,
856 E: Component<()>,
857{
858 pub state: S,
859 pub render_drag_handle: R,
860 view_type: PhantomData<V>,
861}
862
863impl<S, R, V, E> Drag<S, R, V, E>
864where
865 R: Fn(&mut V, &mut ViewContext<V>) -> E,
866 V: 'static,
867 E: Component<()>,
868{
869 pub fn new(state: S, render_drag_handle: R) -> Self {
870 Drag {
871 state,
872 render_drag_handle,
873 view_type: PhantomData,
874 }
875 }
876}
877
878// impl<S, R, V, E> Render for Drag<S, R, V, E> {
879// // fn render(&mut self, cx: ViewContext<Self>) ->
880// }
881
882#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
883pub enum MouseButton {
884 Left,
885 Right,
886 Middle,
887 Navigate(NavigationDirection),
888}
889
890impl MouseButton {
891 pub fn all() -> Vec<Self> {
892 vec![
893 MouseButton::Left,
894 MouseButton::Right,
895 MouseButton::Middle,
896 MouseButton::Navigate(NavigationDirection::Back),
897 MouseButton::Navigate(NavigationDirection::Forward),
898 ]
899 }
900}
901
902impl Default for MouseButton {
903 fn default() -> Self {
904 Self::Left
905 }
906}
907
908#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
909pub enum NavigationDirection {
910 Back,
911 Forward,
912}
913
914impl Default for NavigationDirection {
915 fn default() -> Self {
916 Self::Back
917 }
918}
919
920#[derive(Clone, Debug, Default)]
921pub struct MouseMoveEvent {
922 pub position: Point<Pixels>,
923 pub pressed_button: Option<MouseButton>,
924 pub modifiers: Modifiers,
925}
926
927#[derive(Clone, Debug)]
928pub struct ScrollWheelEvent {
929 pub position: Point<Pixels>,
930 pub delta: ScrollDelta,
931 pub modifiers: Modifiers,
932 pub touch_phase: TouchPhase,
933}
934
935impl Deref for ScrollWheelEvent {
936 type Target = Modifiers;
937
938 fn deref(&self) -> &Self::Target {
939 &self.modifiers
940 }
941}
942
943#[derive(Clone, Copy, Debug)]
944pub enum ScrollDelta {
945 Pixels(Point<Pixels>),
946 Lines(Point<f32>),
947}
948
949impl Default for ScrollDelta {
950 fn default() -> Self {
951 Self::Lines(Default::default())
952 }
953}
954
955impl ScrollDelta {
956 pub fn precise(&self) -> bool {
957 match self {
958 ScrollDelta::Pixels(_) => true,
959 ScrollDelta::Lines(_) => false,
960 }
961 }
962
963 pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
964 match self {
965 ScrollDelta::Pixels(delta) => *delta,
966 ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
967 }
968 }
969}
970
971#[derive(Clone, Debug, Default)]
972pub struct MouseExitEvent {
973 pub position: Point<Pixels>,
974 pub pressed_button: Option<MouseButton>,
975 pub modifiers: Modifiers,
976}
977
978impl Deref for MouseExitEvent {
979 type Target = Modifiers;
980
981 fn deref(&self) -> &Self::Target {
982 &self.modifiers
983 }
984}
985
986#[derive(Debug, Clone, Default)]
987pub struct ExternalPaths(pub(crate) SmallVec<[PathBuf; 2]>);
988
989impl Render for ExternalPaths {
990 type Element = Div<Self>;
991
992 fn render(&mut self, _: &mut ViewContext<Self>) -> Self::Element {
993 div() // Intentionally left empty because the platform will render icons for the dragged files
994 }
995}
996
997#[derive(Debug, Clone)]
998pub enum FileDropEvent {
999 Entered {
1000 position: Point<Pixels>,
1001 files: ExternalPaths,
1002 },
1003 Pending {
1004 position: Point<Pixels>,
1005 },
1006 Submit {
1007 position: Point<Pixels>,
1008 },
1009 Exited,
1010}
1011
1012#[derive(Clone, Debug)]
1013pub enum InputEvent {
1014 KeyDown(KeyDownEvent),
1015 KeyUp(KeyUpEvent),
1016 ModifiersChanged(ModifiersChangedEvent),
1017 MouseDown(MouseDownEvent),
1018 MouseUp(MouseUpEvent),
1019 MouseMove(MouseMoveEvent),
1020 MouseExited(MouseExitEvent),
1021 ScrollWheel(ScrollWheelEvent),
1022 FileDrop(FileDropEvent),
1023}
1024
1025impl InputEvent {
1026 pub fn position(&self) -> Option<Point<Pixels>> {
1027 match self {
1028 InputEvent::KeyDown { .. } => None,
1029 InputEvent::KeyUp { .. } => None,
1030 InputEvent::ModifiersChanged { .. } => None,
1031 InputEvent::MouseDown(event) => Some(event.position),
1032 InputEvent::MouseUp(event) => Some(event.position),
1033 InputEvent::MouseMove(event) => Some(event.position),
1034 InputEvent::MouseExited(event) => Some(event.position),
1035 InputEvent::ScrollWheel(event) => Some(event.position),
1036 InputEvent::FileDrop(FileDropEvent::Exited) => None,
1037 InputEvent::FileDrop(
1038 FileDropEvent::Entered { position, .. }
1039 | FileDropEvent::Pending { position, .. }
1040 | FileDropEvent::Submit { position, .. },
1041 ) => Some(*position),
1042 }
1043 }
1044
1045 pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
1046 match self {
1047 InputEvent::KeyDown { .. } => None,
1048 InputEvent::KeyUp { .. } => None,
1049 InputEvent::ModifiersChanged { .. } => None,
1050 InputEvent::MouseDown(event) => Some(event),
1051 InputEvent::MouseUp(event) => Some(event),
1052 InputEvent::MouseMove(event) => Some(event),
1053 InputEvent::MouseExited(event) => Some(event),
1054 InputEvent::ScrollWheel(event) => Some(event),
1055 InputEvent::FileDrop(event) => Some(event),
1056 }
1057 }
1058
1059 pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> {
1060 match self {
1061 InputEvent::KeyDown(event) => Some(event),
1062 InputEvent::KeyUp(event) => Some(event),
1063 InputEvent::ModifiersChanged(event) => Some(event),
1064 InputEvent::MouseDown(_) => None,
1065 InputEvent::MouseUp(_) => None,
1066 InputEvent::MouseMove(_) => None,
1067 InputEvent::MouseExited(_) => None,
1068 InputEvent::ScrollWheel(_) => None,
1069 InputEvent::FileDrop(_) => None,
1070 }
1071 }
1072}
1073
1074pub struct FocusEvent {
1075 pub blurred: Option<FocusHandle>,
1076 pub focused: Option<FocusHandle>,
1077}
1078
1079pub type MouseDownListener<V> = Box<
1080 dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
1081>;
1082pub type MouseUpListener<V> = Box<
1083 dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
1084>;
1085
1086pub type MouseMoveListener<V> = Box<
1087 dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
1088>;
1089
1090pub type ScrollWheelListener<V> = Box<
1091 dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1092 + 'static,
1093>;
1094
1095pub type ClickListener<V> = Box<dyn Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static>;
1096
1097pub(crate) type DragListener<V> =
1098 Box<dyn Fn(&mut V, Point<Pixels>, &mut ViewContext<V>) -> AnyDrag + 'static>;
1099
1100pub type KeyListener<V> = Box<
1101 dyn Fn(
1102 &mut V,
1103 &dyn Any,
1104 &[&DispatchContext],
1105 DispatchPhase,
1106 &mut ViewContext<V>,
1107 ) -> Option<Box<dyn Action>>
1108 + 'static,
1109>;