1use crate::{
2 point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext,
3 BorrowWindow, Bounds, ClickEvent, DispatchPhase, Element, FocusHandle, KeyContext,
4 KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
5 Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, Style, StyleRefinement, Styled,
6 Task, View, ViewContext, Visibility,
7};
8use collections::HashMap;
9use parking_lot::Mutex;
10use refineable::Refineable;
11use smallvec::SmallVec;
12use std::{
13 any::{Any, TypeId},
14 marker::PhantomData,
15 mem,
16 sync::Arc,
17 time::Duration,
18};
19use taffy::style::Overflow;
20
21const DRAG_THRESHOLD: f64 = 2.;
22const TOOLTIP_DELAY: Duration = Duration::from_millis(500);
23const TOOLTIP_OFFSET: Point<Pixels> = Point::new(px(10.0), px(8.0));
24
25pub struct GroupStyle {
26 pub group: SharedString,
27 pub style: StyleRefinement,
28}
29
30pub trait InteractiveComponent<V: 'static>: Sized + Element<V> {
31 fn interactivity(&mut self) -> &mut Interactivity<V>;
32
33 fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
34 where
35 Self: Sized,
36 {
37 self.interactivity().hover_style = f(StyleRefinement::default());
38 self
39 }
40
41 fn group_hover(
42 mut self,
43 group_name: impl Into<SharedString>,
44 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
45 ) -> Self
46 where
47 Self: Sized,
48 {
49 self.interactivity().group_hover_style = Some(GroupStyle {
50 group: group_name.into(),
51 style: f(StyleRefinement::default()),
52 });
53 self
54 }
55
56 fn on_mouse_down(
57 mut self,
58 button: MouseButton,
59 handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
60 ) -> Self
61 where
62 Self: Sized,
63 {
64 self.interactivity().mouse_down_listeners.push(Box::new(
65 move |view, event, bounds, phase, cx| {
66 if phase == DispatchPhase::Bubble
67 && event.button == button
68 && bounds.contains_point(&event.position)
69 {
70 handler(view, event, cx)
71 }
72 },
73 ));
74 self
75 }
76
77 fn on_mouse_up(
78 mut self,
79 button: MouseButton,
80 handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
81 ) -> Self
82 where
83 Self: Sized,
84 {
85 self.interactivity().mouse_up_listeners.push(Box::new(
86 move |view, event, bounds, phase, cx| {
87 if phase == DispatchPhase::Bubble
88 && event.button == button
89 && bounds.contains_point(&event.position)
90 {
91 handler(view, event, cx)
92 }
93 },
94 ));
95 self
96 }
97
98 fn on_mouse_down_out(
99 mut self,
100 handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
101 ) -> Self
102 where
103 Self: Sized,
104 {
105 self.interactivity().mouse_down_listeners.push(Box::new(
106 move |view, event, bounds, phase, cx| {
107 if phase == DispatchPhase::Capture && !bounds.contains_point(&event.position) {
108 handler(view, event, cx)
109 }
110 },
111 ));
112 self
113 }
114
115 fn on_mouse_up_out(
116 mut self,
117 button: MouseButton,
118 handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
119 ) -> Self
120 where
121 Self: Sized,
122 {
123 self.interactivity().mouse_up_listeners.push(Box::new(
124 move |view, event, bounds, phase, cx| {
125 if phase == DispatchPhase::Capture
126 && event.button == button
127 && !bounds.contains_point(&event.position)
128 {
129 handler(view, event, cx);
130 }
131 },
132 ));
133 self
134 }
135
136 fn on_mouse_move(
137 mut self,
138 handler: impl Fn(&mut V, &MouseMoveEvent, &mut ViewContext<V>) + 'static,
139 ) -> Self
140 where
141 Self: Sized,
142 {
143 self.interactivity().mouse_move_listeners.push(Box::new(
144 move |view, event, bounds, phase, cx| {
145 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
146 handler(view, event, cx);
147 }
148 },
149 ));
150 self
151 }
152
153 fn on_scroll_wheel(
154 mut self,
155 handler: impl Fn(&mut V, &ScrollWheelEvent, &mut ViewContext<V>) + 'static,
156 ) -> Self
157 where
158 Self: Sized,
159 {
160 self.interactivity().scroll_wheel_listeners.push(Box::new(
161 move |view, event, bounds, phase, cx| {
162 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
163 handler(view, event, cx);
164 }
165 },
166 ));
167 self
168 }
169
170 /// Capture the given action, fires during the capture phase
171 fn capture_action<A: Action>(
172 mut self,
173 listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
174 ) -> Self
175 where
176 Self: Sized,
177 {
178 self.interactivity().action_listeners.push((
179 TypeId::of::<A>(),
180 Box::new(move |view, action, phase, cx| {
181 let action = action.downcast_ref().unwrap();
182 if phase == DispatchPhase::Capture {
183 listener(view, action, cx)
184 }
185 }),
186 ));
187 self
188 }
189
190 /// Add a listener for the given action, fires during the bubble event phase
191 fn on_action<A: Action>(
192 mut self,
193 listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
194 ) -> Self
195 where
196 Self: Sized,
197 {
198 self.interactivity().action_listeners.push((
199 TypeId::of::<A>(),
200 Box::new(move |view, action, phase, cx| {
201 let action = action.downcast_ref().unwrap();
202 if phase == DispatchPhase::Bubble {
203 listener(view, action, cx)
204 }
205 }),
206 ));
207 self
208 }
209
210 fn on_key_down(
211 mut self,
212 listener: impl Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + 'static,
213 ) -> Self
214 where
215 Self: Sized,
216 {
217 self.interactivity()
218 .key_down_listeners
219 .push(Box::new(move |view, event, phase, cx| {
220 listener(view, event, phase, cx)
221 }));
222 self
223 }
224
225 fn on_key_up(
226 mut self,
227 listener: impl Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + 'static,
228 ) -> Self
229 where
230 Self: Sized,
231 {
232 self.interactivity()
233 .key_up_listeners
234 .push(Box::new(move |view, event, phase, cx| {
235 listener(view, event, phase, cx)
236 }));
237 self
238 }
239
240 fn drag_over<S: 'static>(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
241 where
242 Self: Sized,
243 {
244 self.interactivity()
245 .drag_over_styles
246 .push((TypeId::of::<S>(), f(StyleRefinement::default())));
247 self
248 }
249
250 fn group_drag_over<S: 'static>(
251 mut self,
252 group_name: impl Into<SharedString>,
253 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
254 ) -> Self
255 where
256 Self: Sized,
257 {
258 self.interactivity().group_drag_over_styles.push((
259 TypeId::of::<S>(),
260 GroupStyle {
261 group: group_name.into(),
262 style: f(StyleRefinement::default()),
263 },
264 ));
265 self
266 }
267
268 fn on_drop<W: 'static>(
269 mut self,
270 listener: impl Fn(&mut V, View<W>, &mut ViewContext<V>) + 'static,
271 ) -> Self
272 where
273 Self: Sized,
274 {
275 self.interactivity().drop_listeners.push((
276 TypeId::of::<W>(),
277 Box::new(move |view, dragged_view, cx| {
278 listener(view, dragged_view.downcast().unwrap(), cx);
279 }),
280 ));
281 self
282 }
283}
284
285pub trait StatefulInteractiveComponent<V: 'static, E: Element<V>>: InteractiveComponent<V> {
286 fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
287 where
288 Self: Sized,
289 {
290 self.interactivity().active_style = f(StyleRefinement::default());
291 self
292 }
293
294 fn group_active(
295 mut self,
296 group_name: impl Into<SharedString>,
297 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
298 ) -> Self
299 where
300 Self: Sized,
301 {
302 self.interactivity().group_active_style = Some(GroupStyle {
303 group: group_name.into(),
304 style: f(StyleRefinement::default()),
305 });
306 self
307 }
308
309 fn on_click(
310 mut self,
311 listener: impl Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static,
312 ) -> Self
313 where
314 Self: Sized,
315 {
316 self.interactivity()
317 .click_listeners
318 .push(Box::new(move |view, event, cx| listener(view, event, cx)));
319 self
320 }
321
322 fn on_drag<W>(
323 mut self,
324 listener: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
325 ) -> Self
326 where
327 Self: Sized,
328 W: 'static + Render,
329 {
330 debug_assert!(
331 self.interactivity().drag_listener.is_none(),
332 "calling on_drag more than once on the same element is not supported"
333 );
334 self.interactivity().drag_listener =
335 Some(Box::new(move |view_state, cursor_offset, cx| AnyDrag {
336 view: listener(view_state, cx).into(),
337 cursor_offset,
338 }));
339 self
340 }
341
342 fn on_hover(mut self, listener: impl 'static + Fn(&mut V, bool, &mut ViewContext<V>)) -> Self
343 where
344 Self: Sized,
345 {
346 debug_assert!(
347 self.interactivity().hover_listener.is_none(),
348 "calling on_hover more than once on the same element is not supported"
349 );
350 self.interactivity().hover_listener = Some(Box::new(listener));
351 self
352 }
353
354 fn tooltip<W>(
355 mut self,
356 build_tooltip: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
357 ) -> Self
358 where
359 Self: Sized,
360 W: 'static + Render,
361 {
362 debug_assert!(
363 self.interactivity().tooltip_builder.is_none(),
364 "calling tooltip more than once on the same element is not supported"
365 );
366 self.interactivity().tooltip_builder = Some(Arc::new(move |view_state, cx| {
367 build_tooltip(view_state, cx).into()
368 }));
369
370 self
371 }
372}
373
374pub trait FocusableComponent<V> {
375 fn focusability(&mut self) -> &mut Focusability<V>;
376
377 fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
378 where
379 Self: Sized,
380 {
381 self.focusability().focus_style = f(StyleRefinement::default());
382 self
383 }
384
385 fn focus_in(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
386 where
387 Self: Sized,
388 {
389 self.focusability().focus_in_style = f(StyleRefinement::default());
390 self
391 }
392
393 fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
394 where
395 Self: Sized,
396 {
397 self.focusability().in_focus_style = f(StyleRefinement::default());
398 self
399 }
400
401 fn on_focus(
402 mut self,
403 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
404 ) -> Self
405 where
406 Self: Sized,
407 {
408 self.focusability()
409 .focus_listeners
410 .push(Box::new(move |view, focus_handle, event, cx| {
411 if event.focused.as_ref() == Some(focus_handle) {
412 listener(view, event, cx)
413 }
414 }));
415 self
416 }
417
418 fn on_blur(
419 mut self,
420 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
421 ) -> Self
422 where
423 Self: Sized,
424 {
425 self.focusability()
426 .focus_listeners
427 .push(Box::new(move |view, focus_handle, event, cx| {
428 if event.blurred.as_ref() == Some(focus_handle) {
429 listener(view, event, cx)
430 }
431 }));
432 self
433 }
434
435 fn on_focus_in(
436 mut self,
437 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
438 ) -> Self
439 where
440 Self: Sized,
441 {
442 self.focusability()
443 .focus_listeners
444 .push(Box::new(move |view, focus_handle, event, cx| {
445 let descendant_blurred = event
446 .blurred
447 .as_ref()
448 .map_or(false, |blurred| focus_handle.contains(blurred, cx));
449 let descendant_focused = event
450 .focused
451 .as_ref()
452 .map_or(false, |focused| focus_handle.contains(focused, cx));
453
454 if !descendant_blurred && descendant_focused {
455 listener(view, event, cx)
456 }
457 }));
458 self
459 }
460
461 fn on_focus_out(
462 mut self,
463 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
464 ) -> Self
465 where
466 Self: Sized,
467 {
468 self.focusability()
469 .focus_listeners
470 .push(Box::new(move |view, focus_handle, event, cx| {
471 let descendant_blurred = event
472 .blurred
473 .as_ref()
474 .map_or(false, |blurred| focus_handle.contains(blurred, cx));
475 let descendant_focused = event
476 .focused
477 .as_ref()
478 .map_or(false, |focused| focus_handle.contains(focused, cx));
479 if descendant_blurred && !descendant_focused {
480 listener(view, event, cx)
481 }
482 }));
483 self
484 }
485}
486
487pub type FocusListeners<V> = SmallVec<[FocusListener<V>; 2]>;
488
489pub type FocusListener<V> =
490 Box<dyn Fn(&mut V, &FocusHandle, &FocusEvent, &mut ViewContext<V>) + 'static>;
491
492pub type MouseDownListener<V> = Box<
493 dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
494>;
495pub type MouseUpListener<V> = Box<
496 dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
497>;
498
499pub type MouseMoveListener<V> = Box<
500 dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
501>;
502
503pub type ScrollWheelListener<V> = Box<
504 dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
505 + 'static,
506>;
507
508pub type ClickListener<V> = Box<dyn Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static>;
509
510pub type DragListener<V> =
511 Box<dyn Fn(&mut V, Point<Pixels>, &mut ViewContext<V>) -> AnyDrag + 'static>;
512
513type DropListener<V> = dyn Fn(&mut V, AnyView, &mut ViewContext<V>) + 'static;
514
515pub type HoverListener<V> = Box<dyn Fn(&mut V, bool, &mut ViewContext<V>) + 'static>;
516
517pub type TooltipBuilder<V> = Arc<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static>;
518
519pub type KeyDownListener<V> =
520 Box<dyn Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + 'static>;
521
522pub type KeyUpListener<V> =
523 Box<dyn Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + 'static>;
524
525pub type ActionListener<V> =
526 Box<dyn Fn(&mut V, &dyn Any, DispatchPhase, &mut ViewContext<V>) + 'static>;
527
528pub struct FocusEvent {
529 pub blurred: Option<FocusHandle>,
530 pub focused: Option<FocusHandle>,
531}
532
533pub struct Node<V> {
534 interactivity: Interactivity<V>,
535 children: Vec<AnyElement<V>>,
536}
537
538impl<V> Styled for Node<V> {
539 fn style(&mut self) -> &mut StyleRefinement {
540 &mut self.interactivity.base_style
541 }
542}
543
544impl<V: 'static> InteractiveComponent<V> for Node<V> {
545 fn interactivity(&mut self) -> &mut Interactivity<V> {
546 &mut self.interactivity
547 }
548}
549
550impl<V: 'static> Element<V> for Node<V> {
551 type ElementState = NodeState;
552
553 fn id(&self) -> Option<crate::ElementId> {
554 None
555 }
556
557 fn initialize(
558 &mut self,
559 view_state: &mut V,
560 element_state: Option<Self::ElementState>,
561 cx: &mut ViewContext<V>,
562 ) -> Self::ElementState {
563 let interactive_state =
564 self.interactivity
565 .initialize(element_state.map(|s| s.interactive_state), cx, |cx| {
566 for child in &mut self.children {
567 child.initialize(view_state, cx);
568 }
569 });
570
571 NodeState {
572 interactive_state,
573 child_layout_ids: SmallVec::new(),
574 }
575 }
576
577 fn layout(
578 &mut self,
579 view_state: &mut V,
580 element_state: &mut Self::ElementState,
581 cx: &mut ViewContext<V>,
582 ) -> crate::LayoutId {
583 let mut interactivity = mem::take(&mut self.interactivity);
584 let layout_id =
585 interactivity.layout(&mut element_state.interactive_state, cx, |style, cx| {
586 cx.with_text_style(style.text_style().cloned(), |cx| {
587 element_state.child_layout_ids = self
588 .children
589 .iter_mut()
590 .map(|child| child.layout(view_state, cx))
591 .collect::<SmallVec<_>>();
592 cx.request_layout(&style, element_state.child_layout_ids.iter().copied())
593 })
594 });
595 self.interactivity = interactivity;
596 layout_id
597 }
598
599 fn paint(
600 &mut self,
601 bounds: Bounds<Pixels>,
602 view_state: &mut V,
603 element_state: &mut Self::ElementState,
604 cx: &mut ViewContext<V>,
605 ) {
606 let mut child_min = point(Pixels::MAX, Pixels::MAX);
607 let mut child_max = Point::default();
608 let content_size = if element_state.child_layout_ids.is_empty() {
609 bounds.size
610 } else {
611 for child_layout_id in &element_state.child_layout_ids {
612 let child_bounds = cx.layout_bounds(*child_layout_id);
613 child_min = child_min.min(&child_bounds.origin);
614 child_max = child_max.max(&child_bounds.lower_right());
615 }
616 (child_max - child_min).into()
617 };
618
619 let mut interactivity = mem::take(&mut self.interactivity);
620 interactivity.paint(
621 bounds,
622 content_size,
623 &mut element_state.interactive_state,
624 cx,
625 |style, scroll_offset, cx| {
626 if style.visibility == Visibility::Hidden {
627 return;
628 }
629
630 let z_index = style.z_index.unwrap_or(0);
631
632 cx.with_z_index(z_index, |cx| {
633 cx.with_z_index(0, |cx| {
634 style.paint(bounds, cx);
635 });
636 cx.with_z_index(1, |cx| {
637 cx.with_text_style(style.text_style().cloned(), |cx| {
638 cx.with_content_mask(style.overflow_mask(bounds), |cx| {
639 cx.with_element_offset(scroll_offset, |cx| {
640 for child in &mut self.children {
641 child.paint(view_state, cx);
642 }
643 })
644 })
645 })
646 })
647 })
648 },
649 );
650 self.interactivity = interactivity;
651 }
652}
653
654pub struct NodeState {
655 child_layout_ids: SmallVec<[LayoutId; 4]>,
656 interactive_state: InteractiveElementState,
657}
658
659impl AsMut<InteractiveElementState> for InteractiveElementState {
660 fn as_mut(&mut self) -> &mut InteractiveElementState {
661 self
662 }
663}
664
665pub struct Interactivity<V> {
666 key_context: KeyContext,
667 tracked_focus_handle: Option<FocusHandle>,
668 focusable: bool,
669 scroll_offset: Point<Pixels>,
670 group: Option<SharedString>,
671 base_style: StyleRefinement,
672 focus_style: StyleRefinement,
673 focus_in_style: StyleRefinement,
674 in_focus_style: StyleRefinement,
675 hover_style: StyleRefinement,
676 group_hover_style: Option<GroupStyle>,
677 active_style: StyleRefinement,
678 group_active_style: Option<GroupStyle>,
679 drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
680 group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
681 mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
682 mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
683 mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
684 scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
685 key_down_listeners: SmallVec<[KeyDownListener<V>; 2]>,
686 key_up_listeners: SmallVec<[KeyUpListener<V>; 2]>,
687 action_listeners: SmallVec<[(TypeId, ActionListener<V>); 8]>,
688 drop_listeners: SmallVec<[(TypeId, Box<DropListener<V>>); 2]>,
689 click_listeners: SmallVec<[ClickListener<V>; 2]>,
690 drag_listener: Option<DragListener<V>>,
691 hover_listener: Option<HoverListener<V>>,
692 tooltip_builder: Option<TooltipBuilder<V>>,
693}
694
695impl<V> Interactivity<V>
696where
697 V: 'static,
698{
699 fn initialize(
700 &mut self,
701 element_state: Option<InteractiveElementState>,
702 cx: &mut ViewContext<V>,
703 f: impl FnOnce(&mut ViewContext<V>),
704 ) -> InteractiveElementState {
705 let mut element_state = element_state.unwrap_or_default();
706 // Ensure we store a focus handle in our element state if we're focusable.
707 // If there's an explicit focus handle we're tracking, use that. Otherwise
708 // create a new handle and store it in the element state, which lives for as
709 // as frames contain an element with this id.
710 if self.focusable {
711 element_state.focus_handle.get_or_insert_with(|| {
712 self.tracked_focus_handle
713 .clone()
714 .unwrap_or_else(|| cx.focus_handle())
715 });
716 }
717 element_state
718 }
719
720 fn layout(
721 &mut self,
722 element_state: &mut InteractiveElementState,
723 cx: &mut ViewContext<V>,
724 f: impl FnOnce(Style, &mut ViewContext<V>) -> LayoutId,
725 ) -> LayoutId {
726 let style = self.compute_style(None, element_state, cx);
727 cx.with_key_dispatch(
728 self.key_context.clone(),
729 self.tracked_focus_handle.clone(),
730 |_, cx| f(style, cx),
731 )
732 }
733
734 fn paint(
735 &mut self,
736 bounds: Bounds<Pixels>,
737 content_size: Size<Pixels>,
738 element_state: &mut InteractiveElementState,
739 cx: &mut ViewContext<V>,
740 f: impl FnOnce(Style, Point<Pixels>, &mut ViewContext<V>),
741 ) {
742 let style = self.compute_style(Some(bounds), element_state, cx);
743
744 if let Some(mouse_cursor) = style.mouse_cursor {
745 let hovered = bounds.contains_point(&cx.mouse_position());
746 if hovered {
747 cx.set_cursor_style(mouse_cursor);
748 }
749 }
750
751 for listener in self.mouse_down_listeners.drain(..) {
752 cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
753 listener(state, event, &bounds, phase, cx);
754 })
755 }
756
757 for listener in self.mouse_up_listeners.drain(..) {
758 cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
759 listener(state, event, &bounds, phase, cx);
760 })
761 }
762
763 for listener in self.mouse_move_listeners.drain(..) {
764 cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
765 listener(state, event, &bounds, phase, cx);
766 })
767 }
768
769 for listener in self.scroll_wheel_listeners.drain(..) {
770 cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
771 listener(state, event, &bounds, phase, cx);
772 })
773 }
774
775 let hover_group_bounds = self
776 .group_hover_style
777 .as_ref()
778 .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
779
780 if let Some(group_bounds) = hover_group_bounds {
781 let hovered = group_bounds.contains_point(&cx.mouse_position());
782 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
783 if phase == DispatchPhase::Capture {
784 if group_bounds.contains_point(&event.position) != hovered {
785 cx.notify();
786 }
787 }
788 });
789 }
790
791 if self.hover_style.is_some()
792 || (cx.active_drag.is_some() && !self.drag_over_styles.is_empty())
793 {
794 let hovered = bounds.contains_point(&cx.mouse_position());
795 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
796 if phase == DispatchPhase::Capture {
797 if bounds.contains_point(&event.position) != hovered {
798 cx.notify();
799 }
800 }
801 });
802 }
803
804 if cx.active_drag.is_some() {
805 let drop_listeners = mem::take(&mut self.drop_listeners);
806 cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| {
807 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
808 if let Some(drag_state_type) =
809 cx.active_drag.as_ref().map(|drag| drag.view.entity_type())
810 {
811 for (drop_state_type, listener) in &drop_listeners {
812 if *drop_state_type == drag_state_type {
813 let drag = cx
814 .active_drag
815 .take()
816 .expect("checked for type drag state type above");
817 listener(view, drag.view.clone(), cx);
818 cx.notify();
819 cx.stop_propagation();
820 }
821 }
822 }
823 }
824 });
825 }
826
827 let click_listeners = mem::take(&mut self.click_listeners);
828 let drag_listener = mem::take(&mut self.drag_listener);
829
830 if !click_listeners.is_empty() || drag_listener.is_some() {
831 let pending_mouse_down = element_state.pending_mouse_down.clone();
832 let mouse_down = pending_mouse_down.lock().clone();
833 if let Some(mouse_down) = mouse_down {
834 if let Some(drag_listener) = drag_listener {
835 let active_state = element_state.clicked_state.clone();
836
837 cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
838 if cx.active_drag.is_some() {
839 if phase == DispatchPhase::Capture {
840 cx.notify();
841 }
842 } else if phase == DispatchPhase::Bubble
843 && bounds.contains_point(&event.position)
844 && (event.position - mouse_down.position).magnitude() > DRAG_THRESHOLD
845 {
846 *active_state.lock() = ElementClickedState::default();
847 let cursor_offset = event.position - bounds.origin;
848 let drag = drag_listener(view_state, cursor_offset, cx);
849 cx.active_drag = Some(drag);
850 cx.notify();
851 cx.stop_propagation();
852 }
853 });
854 }
855
856 cx.on_mouse_event(move |view_state, event: &MouseUpEvent, phase, cx| {
857 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
858 let mouse_click = ClickEvent {
859 down: mouse_down.clone(),
860 up: event.clone(),
861 };
862 for listener in &click_listeners {
863 listener(view_state, &mouse_click, cx);
864 }
865 }
866 *pending_mouse_down.lock() = None;
867 });
868 } else {
869 cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| {
870 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
871 *pending_mouse_down.lock() = Some(event.clone());
872 }
873 });
874 }
875 }
876
877 if let Some(hover_listener) = self.hover_listener.take() {
878 let was_hovered = element_state.hover_state.clone();
879 let has_mouse_down = element_state.pending_mouse_down.clone();
880
881 cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
882 if phase != DispatchPhase::Bubble {
883 return;
884 }
885 let is_hovered =
886 bounds.contains_point(&event.position) && has_mouse_down.lock().is_none();
887 let mut was_hovered = was_hovered.lock();
888
889 if is_hovered != was_hovered.clone() {
890 *was_hovered = is_hovered;
891 drop(was_hovered);
892
893 hover_listener(view_state, is_hovered, cx);
894 }
895 });
896 }
897
898 if let Some(tooltip_builder) = self.tooltip_builder.take() {
899 let active_tooltip = element_state.active_tooltip.clone();
900 let pending_mouse_down = element_state.pending_mouse_down.clone();
901
902 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
903 if phase != DispatchPhase::Bubble {
904 return;
905 }
906
907 let is_hovered =
908 bounds.contains_point(&event.position) && pending_mouse_down.lock().is_none();
909 if !is_hovered {
910 active_tooltip.lock().take();
911 return;
912 }
913
914 if active_tooltip.lock().is_none() {
915 let task = cx.spawn({
916 let active_tooltip = active_tooltip.clone();
917 let tooltip_builder = tooltip_builder.clone();
918
919 move |view, mut cx| async move {
920 cx.background_executor().timer(TOOLTIP_DELAY).await;
921 view.update(&mut cx, move |view_state, cx| {
922 active_tooltip.lock().replace(ActiveTooltip {
923 waiting: None,
924 tooltip: Some(AnyTooltip {
925 view: tooltip_builder(view_state, cx),
926 cursor_offset: cx.mouse_position() + TOOLTIP_OFFSET,
927 }),
928 });
929 cx.notify();
930 })
931 .ok();
932 }
933 });
934 active_tooltip.lock().replace(ActiveTooltip {
935 waiting: Some(task),
936 tooltip: None,
937 });
938 }
939 });
940
941 if let Some(active_tooltip) = element_state.active_tooltip.lock().as_ref() {
942 if active_tooltip.tooltip.is_some() {
943 cx.active_tooltip = active_tooltip.tooltip.clone()
944 }
945 }
946 }
947
948 let active_state = element_state.clicked_state.clone();
949 if !active_state.lock().is_clicked() {
950 cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
951 if phase == DispatchPhase::Capture {
952 *active_state.lock() = ElementClickedState::default();
953 cx.notify();
954 }
955 });
956 } else {
957 let active_group_bounds = self
958 .group_active_style
959 .as_ref()
960 .and_then(|group_active| GroupBounds::get(&group_active.group, cx));
961 cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
962 if phase == DispatchPhase::Bubble {
963 let group = active_group_bounds
964 .map_or(false, |bounds| bounds.contains_point(&down.position));
965 let element = bounds.contains_point(&down.position);
966 if group || element {
967 *active_state.lock() = ElementClickedState { group, element };
968 cx.notify();
969 }
970 }
971 });
972 }
973
974 let overflow = style.overflow;
975 if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
976 let scroll_offset = element_state
977 .scroll_offset
978 .get_or_insert_with(Arc::default)
979 .clone();
980 let line_height = cx.line_height();
981 let scroll_max = (content_size - bounds.size).max(&Size::default());
982
983 cx.on_mouse_event(move |_, event: &ScrollWheelEvent, phase, cx| {
984 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
985 let mut scroll_offset = scroll_offset.lock();
986 let old_scroll_offset = *scroll_offset;
987 let delta = event.delta.pixel_delta(line_height);
988
989 if overflow.x == Overflow::Scroll {
990 scroll_offset.x =
991 (scroll_offset.x + delta.x).clamp(-scroll_max.width, px(0.));
992 }
993
994 if overflow.y == Overflow::Scroll {
995 scroll_offset.y =
996 (scroll_offset.y + delta.y).clamp(-scroll_max.height, px(0.));
997 }
998
999 if *scroll_offset != old_scroll_offset {
1000 cx.notify();
1001 cx.stop_propagation();
1002 }
1003 }
1004 });
1005 }
1006
1007 if let Some(group) = self.group.clone() {
1008 GroupBounds::push(group, bounds, cx);
1009 }
1010
1011 todo!();
1012 // cx.with_element_id(self.i, f);
1013 cx.with_key_dispatch(
1014 self.key_context.clone(),
1015 self.tracked_focus_handle.clone(),
1016 |_, cx| {
1017 for listener in self.key_down_listeners.drain(..) {
1018 cx.on_key_event(move |state, event: &KeyDownEvent, phase, cx| {
1019 listener(state, event, phase, cx);
1020 })
1021 }
1022
1023 for listener in self.key_up_listeners.drain(..) {
1024 cx.on_key_event(move |state, event: &KeyUpEvent, phase, cx| {
1025 listener(state, event, phase, cx);
1026 })
1027 }
1028
1029 for (action_type, listener) in self.action_listeners.drain(..) {
1030 cx.on_action(action_type, listener)
1031 }
1032
1033 f(style, self.scroll_offset, cx)
1034 },
1035 );
1036
1037 if let Some(group) = self.group.as_ref() {
1038 GroupBounds::pop(group, cx);
1039 }
1040 }
1041
1042 fn compute_style(
1043 &self,
1044 bounds: Option<Bounds<Pixels>>,
1045 element_state: &mut InteractiveElementState,
1046 cx: &mut ViewContext<V>,
1047 ) -> Style {
1048 let mut style = Style::default();
1049 style.refine(&self.base_style);
1050
1051 if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
1052 if focus_handle.contains_focused(cx) {
1053 style.refine(&self.focus_in_style);
1054 }
1055
1056 if focus_handle.within_focused(cx) {
1057 style.refine(&self.in_focus_style);
1058 }
1059
1060 if focus_handle.is_focused(cx) {
1061 style.refine(&self.focus_style);
1062 }
1063 }
1064
1065 if let Some(bounds) = bounds {
1066 let mouse_position = cx.mouse_position();
1067 if let Some(group_hover) = self.group_hover_style.as_ref() {
1068 if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
1069 if group_bounds.contains_point(&mouse_position) {
1070 style.refine(&group_hover.style);
1071 }
1072 }
1073 }
1074 if bounds.contains_point(&mouse_position) {
1075 style.refine(&self.hover_style);
1076 }
1077
1078 if let Some(drag) = cx.active_drag.take() {
1079 for (state_type, group_drag_style) in &self.group_drag_over_styles {
1080 if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) {
1081 if *state_type == drag.view.entity_type()
1082 && group_bounds.contains_point(&mouse_position)
1083 {
1084 style.refine(&group_drag_style.style);
1085 }
1086 }
1087 }
1088
1089 for (state_type, drag_over_style) in &self.drag_over_styles {
1090 if *state_type == drag.view.entity_type()
1091 && bounds.contains_point(&mouse_position)
1092 {
1093 style.refine(drag_over_style);
1094 }
1095 }
1096
1097 cx.active_drag = Some(drag);
1098 }
1099 }
1100
1101 let clicked_state = element_state.clicked_state.lock();
1102 if clicked_state.group {
1103 if let Some(group) = self.group_active_style.as_ref() {
1104 style.refine(&group.style)
1105 }
1106 }
1107
1108 if clicked_state.element {
1109 style.refine(&self.active_style)
1110 }
1111
1112 style
1113 }
1114}
1115
1116impl<V: 'static> Default for Interactivity<V> {
1117 fn default() -> Self {
1118 Self {
1119 key_context: KeyContext::default(),
1120 tracked_focus_handle: None,
1121 scroll_offset: Point::default(),
1122 group: None,
1123 base_style: StyleRefinement::default(),
1124 focus_style: StyleRefinement::default(),
1125 focus_in_style: StyleRefinement::default(),
1126 in_focus_style: StyleRefinement::default(),
1127 hover_style: StyleRefinement::default(),
1128 group_hover_style: None,
1129 active_style: StyleRefinement::default(),
1130 group_active_style: None,
1131 drag_over_styles: SmallVec::new(),
1132 group_drag_over_styles: SmallVec::new(),
1133 mouse_down_listeners: SmallVec::new(),
1134 mouse_up_listeners: SmallVec::new(),
1135 mouse_move_listeners: SmallVec::new(),
1136 scroll_wheel_listeners: SmallVec::new(),
1137 key_down_listeners: SmallVec::new(),
1138 key_up_listeners: SmallVec::new(),
1139 action_listeners: SmallVec::new(),
1140 drop_listeners: SmallVec::new(),
1141 click_listeners: SmallVec::new(),
1142 drag_listener: None,
1143 hover_listener: None,
1144 tooltip_builder: None,
1145 focusable: false,
1146 }
1147 }
1148}
1149
1150#[derive(Default)]
1151pub struct InteractiveElementState {
1152 focus_handle: Option<FocusHandle>,
1153 clicked_state: Arc<Mutex<ElementClickedState>>,
1154 hover_state: Arc<Mutex<bool>>,
1155 pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
1156 scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
1157 active_tooltip: Arc<Mutex<Option<ActiveTooltip>>>,
1158}
1159
1160struct ActiveTooltip {
1161 #[allow(unused)] // used to drop the task
1162 waiting: Option<Task<()>>,
1163 tooltip: Option<AnyTooltip>,
1164}
1165
1166/// Whether or not the element or a group that contains it is clicked by the mouse.
1167#[derive(Copy, Clone, Default, Eq, PartialEq)]
1168struct ElementClickedState {
1169 pub group: bool,
1170 pub element: bool,
1171}
1172
1173impl ElementClickedState {
1174 fn is_clicked(&self) -> bool {
1175 self.group || self.element
1176 }
1177}
1178
1179#[derive(Default)]
1180pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
1181
1182impl GroupBounds {
1183 pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
1184 cx.default_global::<Self>()
1185 .0
1186 .get(name)
1187 .and_then(|bounds_stack| bounds_stack.last())
1188 .cloned()
1189 }
1190
1191 pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
1192 cx.default_global::<Self>()
1193 .0
1194 .entry(name)
1195 .or_default()
1196 .push(bounds);
1197 }
1198
1199 pub fn pop(name: &SharedString, cx: &mut AppContext) {
1200 cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
1201 }
1202}
1203
1204pub struct Focusable<V, E> {
1205 focusability: Focusability<V>,
1206 view_type: PhantomData<V>,
1207 element: E,
1208}
1209
1210pub struct Focusability<V> {
1211 focus_handle: Option<FocusHandle>,
1212 focus_listeners: FocusListeners<V>,
1213 focus_style: StyleRefinement,
1214 focus_in_style: StyleRefinement,
1215 in_focus_style: StyleRefinement,
1216}
1217
1218impl<V, E> FocusableComponent<V> for Focusable<V, E> {
1219 fn focusability(&mut self) -> &mut Focusability<V> {
1220 &mut self.focusability
1221 }
1222}
1223
1224impl<V, E> InteractiveComponent<V> for Focusable<V, E>
1225where
1226 V: 'static,
1227 E: InteractiveComponent<V>,
1228{
1229 fn interactivity(&mut self) -> &mut Interactivity<V> {
1230 self.element.interactivity()
1231 }
1232}
1233
1234impl<V: 'static, E: StatefulInteractiveComponent<V, E>> StatefulInteractiveComponent<V, E>
1235 for Focusable<V, E>
1236{
1237}
1238
1239impl<V, E> Element<V> for Focusable<V, E>
1240where
1241 V: 'static,
1242 E: Element<V>,
1243{
1244 type ElementState = E::ElementState;
1245
1246 fn id(&self) -> Option<crate::ElementId> {
1247 self.element.id()
1248 }
1249
1250 fn initialize(
1251 &mut self,
1252 view_state: &mut V,
1253 element_state: Option<Self::ElementState>,
1254 cx: &mut ViewContext<V>,
1255 ) -> Self::ElementState {
1256 self.element.initialize(view_state, element_state, cx)
1257 }
1258
1259 fn layout(
1260 &mut self,
1261 view_state: &mut V,
1262 element_state: &mut Self::ElementState,
1263 cx: &mut ViewContext<V>,
1264 ) -> LayoutId {
1265 self.element.layout(view_state, element_state, cx)
1266 }
1267
1268 fn paint(
1269 &mut self,
1270 bounds: Bounds<Pixels>,
1271 view_state: &mut V,
1272 element_state: &mut Self::ElementState,
1273 cx: &mut ViewContext<V>,
1274 ) {
1275 self.element.paint(bounds, view_state, element_state, cx);
1276 }
1277}
1278
1279pub struct Stateful<V, E> {
1280 id: SharedString,
1281 view_type: PhantomData<V>,
1282 element: E,
1283}
1284
1285impl<V, E> StatefulInteractiveComponent<V, E> for Stateful<V, E>
1286where
1287 V: 'static,
1288 E: Element<V>,
1289 Self: InteractiveComponent<V>,
1290{
1291}
1292
1293impl<V, E> InteractiveComponent<V> for Stateful<V, E>
1294where
1295 V: 'static,
1296 E: InteractiveComponent<V>,
1297{
1298 fn interactivity(&mut self) -> &mut Interactivity<V> {
1299 self.element.interactivity()
1300 }
1301}
1302
1303impl<V, E: FocusableComponent<V>> FocusableComponent<V> for Stateful<V, E> {
1304 fn focusability(&mut self) -> &mut Focusability<V> {
1305 self.element.focusability()
1306 }
1307}
1308
1309impl<V, E> Element<V> for Stateful<V, E>
1310where
1311 V: 'static,
1312 E: Element<V>,
1313{
1314 type ElementState = E::ElementState;
1315
1316 fn id(&self) -> Option<crate::ElementId> {
1317 self.element.id()
1318 }
1319
1320 fn initialize(
1321 &mut self,
1322 view_state: &mut V,
1323 element_state: Option<Self::ElementState>,
1324 cx: &mut ViewContext<V>,
1325 ) -> Self::ElementState {
1326 self.element.initialize(view_state, element_state, cx)
1327 }
1328
1329 fn layout(
1330 &mut self,
1331 view_state: &mut V,
1332 element_state: &mut Self::ElementState,
1333 cx: &mut ViewContext<V>,
1334 ) -> LayoutId {
1335 self.element.layout(view_state, element_state, cx)
1336 }
1337
1338 fn paint(
1339 &mut self,
1340 bounds: Bounds<Pixels>,
1341 view_state: &mut V,
1342 element_state: &mut Self::ElementState,
1343 cx: &mut ViewContext<V>,
1344 ) {
1345 self.element.paint(bounds, view_state, element_state, cx)
1346 }
1347}