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