1use crate::{
2 point, Action, AnyDrag, AnyElement, AnyView, AppContext, BorrowWindow, Bounds, ClickEvent,
3 DispatchPhase, Element, FocusHandle, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId,
4 MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point, Render,
5 ScrollWheelEvent, SharedString, Style, StyleRefinement, Styled, View, ViewContext, Visibility,
6};
7use collections::HashMap;
8use refineable::Refineable;
9use smallvec::SmallVec;
10use std::{
11 any::{Any, TypeId},
12 marker::PhantomData,
13 mem,
14 sync::Arc,
15};
16
17pub struct GroupStyle {
18 pub group: SharedString,
19 pub style: StyleRefinement,
20}
21
22pub trait InteractiveComponent<V: 'static> {
23 fn interactivity(&mut self) -> &mut Interactivity<V>;
24
25 fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
26 where
27 Self: Sized,
28 {
29 self.interactivity().hover_style = f(StyleRefinement::default());
30 self
31 }
32
33 fn group_hover(
34 mut self,
35 group_name: impl Into<SharedString>,
36 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
37 ) -> Self
38 where
39 Self: Sized,
40 {
41 self.interactivity().group_hover_style = Some(GroupStyle {
42 group: group_name.into(),
43 style: f(StyleRefinement::default()),
44 });
45 self
46 }
47
48 fn on_mouse_down(
49 mut self,
50 button: MouseButton,
51 handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
52 ) -> Self
53 where
54 Self: Sized,
55 {
56 self.interactivity().mouse_down_listeners.push(Box::new(
57 move |view, event, bounds, phase, cx| {
58 if phase == DispatchPhase::Bubble
59 && event.button == button
60 && bounds.contains_point(&event.position)
61 {
62 handler(view, event, cx)
63 }
64 },
65 ));
66 self
67 }
68
69 fn on_mouse_up(
70 mut self,
71 button: MouseButton,
72 handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
73 ) -> Self
74 where
75 Self: Sized,
76 {
77 self.interactivity().mouse_up_listeners.push(Box::new(
78 move |view, event, bounds, phase, cx| {
79 if phase == DispatchPhase::Bubble
80 && event.button == button
81 && bounds.contains_point(&event.position)
82 {
83 handler(view, event, cx)
84 }
85 },
86 ));
87 self
88 }
89
90 fn on_mouse_down_out(
91 mut self,
92 handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
93 ) -> Self
94 where
95 Self: Sized,
96 {
97 self.interactivity().mouse_down_listeners.push(Box::new(
98 move |view, event, bounds, phase, cx| {
99 if phase == DispatchPhase::Capture && !bounds.contains_point(&event.position) {
100 handler(view, event, cx)
101 }
102 },
103 ));
104 self
105 }
106
107 fn on_mouse_up_out(
108 mut self,
109 button: MouseButton,
110 handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
111 ) -> Self
112 where
113 Self: Sized,
114 {
115 self.interactivity().mouse_up_listeners.push(Box::new(
116 move |view, event, bounds, phase, cx| {
117 if phase == DispatchPhase::Capture
118 && event.button == button
119 && !bounds.contains_point(&event.position)
120 {
121 handler(view, event, cx);
122 }
123 },
124 ));
125 self
126 }
127
128 fn on_mouse_move(
129 mut self,
130 handler: impl Fn(&mut V, &MouseMoveEvent, &mut ViewContext<V>) + 'static,
131 ) -> Self
132 where
133 Self: Sized,
134 {
135 self.interactivity().mouse_move_listeners.push(Box::new(
136 move |view, event, bounds, phase, cx| {
137 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
138 handler(view, event, cx);
139 }
140 },
141 ));
142 self
143 }
144
145 fn on_scroll_wheel(
146 mut self,
147 handler: impl Fn(&mut V, &ScrollWheelEvent, &mut ViewContext<V>) + 'static,
148 ) -> Self
149 where
150 Self: Sized,
151 {
152 self.interactivity().scroll_wheel_listeners.push(Box::new(
153 move |view, event, bounds, phase, cx| {
154 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
155 handler(view, event, cx);
156 }
157 },
158 ));
159 self
160 }
161
162 /// Capture the given action, fires during the capture phase
163 fn capture_action<A: Action>(
164 mut self,
165 listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
166 ) -> Self
167 where
168 Self: Sized,
169 {
170 self.interactivity().action_listeners.push((
171 TypeId::of::<A>(),
172 Box::new(move |view, action, phase, cx| {
173 let action = action.downcast_ref().unwrap();
174 if phase == DispatchPhase::Capture {
175 listener(view, action, cx)
176 }
177 }),
178 ));
179 self
180 }
181
182 /// Add a listener for the given action, fires during the bubble event phase
183 fn on_action<A: Action>(
184 mut self,
185 listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
186 ) -> Self
187 where
188 Self: Sized,
189 {
190 self.interactivity().action_listeners.push((
191 TypeId::of::<A>(),
192 Box::new(move |view, action, phase, cx| {
193 let action = action.downcast_ref().unwrap();
194 if phase == DispatchPhase::Bubble {
195 listener(view, action, cx)
196 }
197 }),
198 ));
199 self
200 }
201
202 fn on_key_down(
203 mut self,
204 listener: impl Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + 'static,
205 ) -> Self
206 where
207 Self: Sized,
208 {
209 self.interactivity()
210 .key_down_listeners
211 .push(Box::new(move |view, event, phase, cx| {
212 listener(view, event, phase, cx)
213 }));
214 self
215 }
216
217 fn on_key_up(
218 mut self,
219 listener: impl Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + 'static,
220 ) -> Self
221 where
222 Self: Sized,
223 {
224 self.interactivity()
225 .key_up_listeners
226 .push(Box::new(move |view, event, phase, cx| {
227 listener(view, event, phase, cx)
228 }));
229 self
230 }
231
232 fn drag_over<S: 'static>(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
233 where
234 Self: Sized,
235 {
236 self.interactivity()
237 .drag_over_styles
238 .push((TypeId::of::<S>(), f(StyleRefinement::default())));
239 self
240 }
241
242 fn group_drag_over<S: 'static>(
243 mut self,
244 group_name: impl Into<SharedString>,
245 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
246 ) -> Self
247 where
248 Self: Sized,
249 {
250 self.interactivity().group_drag_over_styles.push((
251 TypeId::of::<S>(),
252 GroupStyle {
253 group: group_name.into(),
254 style: f(StyleRefinement::default()),
255 },
256 ));
257 self
258 }
259
260 fn on_drop<W: 'static>(
261 mut self,
262 listener: impl Fn(&mut V, View<W>, &mut ViewContext<V>) + 'static,
263 ) -> Self
264 where
265 Self: Sized,
266 {
267 self.interactivity().drop_listeners.push((
268 TypeId::of::<W>(),
269 Box::new(move |view, dragged_view, cx| {
270 listener(view, dragged_view.downcast().unwrap(), cx);
271 }),
272 ));
273 self
274 }
275}
276
277pub trait StatefulInteractiveComponent<V: 'static>: InteractiveComponent<V> {
278 fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
279 where
280 Self: Sized,
281 {
282 self.interactivity().active_style = f(StyleRefinement::default());
283 self
284 }
285
286 fn group_active(
287 mut self,
288 group_name: impl Into<SharedString>,
289 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
290 ) -> Self
291 where
292 Self: Sized,
293 {
294 self.interactivity().group_active_style = Some(GroupStyle {
295 group: group_name.into(),
296 style: f(StyleRefinement::default()),
297 });
298 self
299 }
300
301 fn on_click(
302 mut self,
303 listener: impl Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static,
304 ) -> Self
305 where
306 Self: Sized,
307 {
308 self.interactivity()
309 .click_listeners
310 .push(Box::new(move |view, event, cx| listener(view, event, cx)));
311 self
312 }
313
314 fn on_drag<W>(
315 mut self,
316 listener: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
317 ) -> Self
318 where
319 Self: Sized,
320 W: 'static + Render,
321 {
322 debug_assert!(
323 self.interactivity().drag_listener.is_none(),
324 "calling on_drag more than once on the same element is not supported"
325 );
326 self.interactivity().drag_listener =
327 Some(Box::new(move |view_state, cursor_offset, cx| AnyDrag {
328 view: listener(view_state, cx).into(),
329 cursor_offset,
330 }));
331 self
332 }
333
334 fn on_hover(mut self, listener: impl 'static + Fn(&mut V, bool, &mut ViewContext<V>)) -> Self
335 where
336 Self: Sized,
337 {
338 debug_assert!(
339 self.interactivity().hover_listener.is_none(),
340 "calling on_hover more than once on the same element is not supported"
341 );
342 self.interactivity().hover_listener = Some(Box::new(listener));
343 self
344 }
345
346 fn tooltip<W>(
347 mut self,
348 build_tooltip: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
349 ) -> Self
350 where
351 Self: Sized,
352 W: 'static + Render,
353 {
354 debug_assert!(
355 self.interactivity().tooltip_builder.is_none(),
356 "calling tooltip more than once on the same element is not supported"
357 );
358 self.interactivity().tooltip_builder = Some(Arc::new(move |view_state, cx| {
359 build_tooltip(view_state, cx).into()
360 }));
361
362 self
363 }
364}
365
366pub trait FocusableComponent<V> {
367 fn focusability(&mut self) -> &mut Focusability<V>;
368
369 fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
370 where
371 Self: Sized,
372 {
373 self.focusability().focus_style = f(StyleRefinement::default());
374 self
375 }
376
377 fn focus_in(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
378 where
379 Self: Sized,
380 {
381 self.focusability().focus_in_style = f(StyleRefinement::default());
382 self
383 }
384
385 fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
386 where
387 Self: Sized,
388 {
389 self.focusability().in_focus_style = f(StyleRefinement::default());
390 self
391 }
392
393 fn on_focus(
394 mut self,
395 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
396 ) -> Self
397 where
398 Self: Sized,
399 {
400 self.focusability()
401 .focus_listeners
402 .push(Box::new(move |view, focus_handle, event, cx| {
403 if event.focused.as_ref() == Some(focus_handle) {
404 listener(view, event, cx)
405 }
406 }));
407 self
408 }
409
410 fn on_blur(
411 mut self,
412 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
413 ) -> Self
414 where
415 Self: Sized,
416 {
417 self.focusability()
418 .focus_listeners
419 .push(Box::new(move |view, focus_handle, event, cx| {
420 if event.blurred.as_ref() == Some(focus_handle) {
421 listener(view, event, cx)
422 }
423 }));
424 self
425 }
426
427 fn on_focus_in(
428 mut self,
429 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
430 ) -> Self
431 where
432 Self: Sized,
433 {
434 self.focusability()
435 .focus_listeners
436 .push(Box::new(move |view, focus_handle, event, cx| {
437 let descendant_blurred = event
438 .blurred
439 .as_ref()
440 .map_or(false, |blurred| focus_handle.contains(blurred, cx));
441 let descendant_focused = event
442 .focused
443 .as_ref()
444 .map_or(false, |focused| focus_handle.contains(focused, cx));
445
446 if !descendant_blurred && descendant_focused {
447 listener(view, event, cx)
448 }
449 }));
450 self
451 }
452
453 fn on_focus_out(
454 mut self,
455 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
456 ) -> Self
457 where
458 Self: Sized,
459 {
460 self.focusability()
461 .focus_listeners
462 .push(Box::new(move |view, focus_handle, event, cx| {
463 let descendant_blurred = event
464 .blurred
465 .as_ref()
466 .map_or(false, |blurred| focus_handle.contains(blurred, cx));
467 let descendant_focused = event
468 .focused
469 .as_ref()
470 .map_or(false, |focused| focus_handle.contains(focused, cx));
471 if descendant_blurred && !descendant_focused {
472 listener(view, event, cx)
473 }
474 }));
475 self
476 }
477}
478
479pub type FocusListeners<V> = SmallVec<[FocusListener<V>; 2]>;
480
481pub type FocusListener<V> =
482 Box<dyn Fn(&mut V, &FocusHandle, &FocusEvent, &mut ViewContext<V>) + 'static>;
483
484pub type MouseDownListener<V> = Box<
485 dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
486>;
487pub type MouseUpListener<V> = Box<
488 dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
489>;
490
491pub type MouseMoveListener<V> = Box<
492 dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
493>;
494
495pub type ScrollWheelListener<V> = Box<
496 dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
497 + 'static,
498>;
499
500pub type ClickListener<V> = Box<dyn Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static>;
501
502pub type DragListener<V> =
503 Box<dyn Fn(&mut V, Point<Pixels>, &mut ViewContext<V>) -> AnyDrag + 'static>;
504
505type DropListener<V> = dyn Fn(&mut V, AnyView, &mut ViewContext<V>) + 'static;
506
507pub type HoverListener<V> = Box<dyn Fn(&mut V, bool, &mut ViewContext<V>) + 'static>;
508
509pub type TooltipBuilder<V> = Arc<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static>;
510
511pub type KeyDownListener<V> =
512 Box<dyn Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + 'static>;
513
514pub type KeyUpListener<V> =
515 Box<dyn Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + 'static>;
516
517pub type ActionListener<V> =
518 Box<dyn Fn(&mut V, &dyn Any, DispatchPhase, &mut ViewContext<V>) + 'static>;
519
520pub struct FocusEvent {
521 pub blurred: Option<FocusHandle>,
522 pub focused: Option<FocusHandle>,
523}
524
525pub struct Node<V> {
526 interactivity: Interactivity<V>,
527 children: Vec<AnyElement<V>>,
528}
529
530impl<V> Styled for Node<V> {
531 fn style(&mut self) -> &mut StyleRefinement {
532 &mut self.interactivity.base_style
533 }
534}
535
536impl<V: 'static> InteractiveComponent<V> for Node<V> {
537 fn interactivity(&mut self) -> &mut Interactivity<V> {
538 &mut self.interactivity
539 }
540}
541
542pub struct NodeState {
543 child_layout_ids: SmallVec<[LayoutId; 4]>,
544}
545
546impl<V: 'static> Element<V> for Node<V> {
547 type ElementState = NodeState;
548
549 fn id(&self) -> Option<crate::ElementId> {
550 None
551 }
552
553 fn initialize(
554 &mut self,
555 view_state: &mut V,
556 _: Option<Self::ElementState>,
557 cx: &mut ViewContext<V>,
558 ) -> Self::ElementState {
559 for child in &mut self.children {
560 child.initialize(view_state, cx);
561 }
562 NodeState {
563 child_layout_ids: SmallVec::new(),
564 }
565 }
566
567 fn layout(
568 &mut self,
569 view_state: &mut V,
570 element_state: &mut Self::ElementState,
571 cx: &mut ViewContext<V>,
572 ) -> crate::LayoutId {
573 let style = self.interactivity().compute_style(None, cx);
574 style.with_text_style(cx, |cx| {
575 element_state.child_layout_ids = self
576 .children
577 .iter_mut()
578 .map(|child| child.layout(view_state, cx))
579 .collect::<SmallVec<_>>();
580 cx.request_layout(&style, element_state.child_layout_ids.iter().copied())
581 })
582 }
583
584 fn paint(
585 &mut self,
586 bounds: Bounds<Pixels>,
587 view_state: &mut V,
588 element_state: &mut Self::ElementState,
589 cx: &mut ViewContext<V>,
590 ) {
591 let style = self.interactivity.compute_style(Some(bounds), cx);
592 if style.visibility == Visibility::Hidden {
593 return;
594 }
595
596 if let Some(mouse_cursor) = style.mouse_cursor {
597 let hovered = bounds.contains_point(&cx.mouse_position());
598 if hovered {
599 cx.set_cursor_style(mouse_cursor);
600 }
601 }
602
603 if let Some(group) = self.interactivity.group.clone() {
604 GroupBounds::push(group, bounds, cx);
605 }
606
607 let z_index = style.z_index.unwrap_or(0);
608
609 let mut child_min = point(Pixels::MAX, Pixels::MAX);
610 let mut child_max = Point::default();
611
612 let content_size = if element_state.child_layout_ids.is_empty() {
613 bounds.size
614 } else {
615 for child_layout_id in &element_state.child_layout_ids {
616 let child_bounds = cx.layout_bounds(*child_layout_id);
617 child_min = child_min.min(&child_bounds.origin);
618 child_max = child_max.max(&child_bounds.lower_right());
619 }
620 (child_max - child_min).into()
621 };
622
623 let mut interactivity = mem::take(&mut self.interactivity);
624 interactivity.paint(bounds, cx, |cx| {
625 cx.with_z_index(z_index, |cx| {
626 cx.with_z_index(0, |cx| {
627 style.paint(bounds, cx);
628 });
629 cx.with_z_index(1, |cx| {
630 style.with_text_style(cx, |cx| {
631 style.apply_overflow(bounds, cx, |cx| {
632 let scroll_offset = self.interactivity.scroll_offset;
633 cx.with_element_offset2(scroll_offset, |cx| {
634 for child in &mut self.children {
635 child.paint(view_state, cx);
636 }
637 });
638 })
639 })
640 });
641 });
642 });
643 self.interactivity = interactivity;
644
645 if let Some(group) = self.interactivity.group.as_ref() {
646 GroupBounds::pop(group, cx);
647 }
648 }
649}
650
651pub enum FocusState {
652 /// The current element is not focused, and does not contain or descend from the focused element.
653 None,
654 /// The current element is focused.
655 Focus,
656 /// The current element contains the focused element
657 FocusIn,
658 /// The current element descends from the focused element
659 InFocus,
660}
661
662pub struct Interactivity<V> {
663 pub active: bool,
664 pub group_active: bool,
665 pub hovered: bool,
666 pub group_hovered: bool,
667 pub focus: FocusState,
668 pub key_context: KeyContext,
669 pub focus_handle: Option<FocusHandle>,
670 pub scroll_offset: Point<Pixels>,
671 pub base_style: StyleRefinement,
672 pub focus_style: StyleRefinement,
673 pub focus_in_style: StyleRefinement,
674 pub in_focus_style: StyleRefinement,
675 pub hover_style: StyleRefinement,
676 pub group_hover_style: Option<GroupStyle>,
677 pub active_style: StyleRefinement,
678 pub group_active_style: Option<GroupStyle>,
679 pub drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
680 pub group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
681 pub group: Option<SharedString>,
682 pub dispatch_context: KeyContext,
683 pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
684 pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
685 pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
686 pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
687 pub key_down_listeners: SmallVec<[KeyDownListener<V>; 2]>,
688 pub key_up_listeners: SmallVec<[KeyUpListener<V>; 2]>,
689 pub action_listeners: SmallVec<[(TypeId, ActionListener<V>); 8]>,
690 pub drop_listeners: SmallVec<[(TypeId, Box<DropListener<V>>); 2]>,
691 pub click_listeners: SmallVec<[ClickListener<V>; 2]>,
692 pub drag_listener: Option<DragListener<V>>,
693 pub hover_listener: Option<HoverListener<V>>,
694 pub tooltip_builder: Option<TooltipBuilder<V>>,
695}
696
697impl<V: 'static> Interactivity<V> {
698 fn compute_style(&self, bounds: Option<Bounds<Pixels>>, cx: &mut ViewContext<V>) -> Style {
699 let mut style = Style::default();
700 style.refine(&self.base_style);
701
702 match self.focus {
703 FocusState::None => {}
704 FocusState::Focus => {
705 style.refine(&self.focus_style);
706 style.refine(&self.focus_in_style);
707 style.refine(&self.in_focus_style);
708 }
709 FocusState::FocusIn => {
710 style.refine(&self.focus_in_style);
711 }
712 FocusState::InFocus => {
713 style.refine(&self.in_focus_style);
714 }
715 }
716
717 if let Some(bounds) = bounds {
718 let mouse_position = cx.mouse_position();
719 if let Some(group_hover) = self.group_hover_style.as_ref() {
720 if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
721 if group_bounds.contains_point(&mouse_position) {
722 style.refine(&group_hover.style);
723 }
724 }
725 }
726 if bounds.contains_point(&mouse_position) {
727 style.refine(&self.hover_style);
728 }
729
730 if let Some(drag) = cx.active_drag.take() {
731 for (state_type, group_drag_style) in &self.group_drag_over_styles {
732 if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) {
733 if *state_type == drag.view.entity_type()
734 && group_bounds.contains_point(&mouse_position)
735 {
736 style.refine(&group_drag_style.style);
737 }
738 }
739 }
740
741 for (state_type, drag_over_style) in &self.drag_over_styles {
742 if *state_type == drag.view.entity_type()
743 && bounds.contains_point(&mouse_position)
744 {
745 style.refine(drag_over_style);
746 }
747 }
748
749 cx.active_drag = Some(drag);
750 }
751 }
752
753 if self.group_active {
754 if let Some(group) = self.group_active_style.as_ref() {
755 style.refine(&group.style)
756 }
757 }
758
759 if self.active {
760 style.refine(&self.active_style)
761 }
762
763 style
764 }
765
766 fn paint(
767 &mut self,
768 bounds: Bounds<Pixels>,
769 cx: &mut ViewContext<V>,
770 f: impl FnOnce(&mut ViewContext<V>),
771 ) {
772 for listener in self.mouse_down_listeners.drain(..) {
773 cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
774 listener(state, event, &bounds, phase, cx);
775 })
776 }
777
778 for listener in self.mouse_up_listeners.drain(..) {
779 cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
780 listener(state, event, &bounds, phase, cx);
781 })
782 }
783
784 for listener in self.mouse_move_listeners.drain(..) {
785 cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
786 listener(state, event, &bounds, phase, cx);
787 })
788 }
789
790 for listener in self.scroll_wheel_listeners.drain(..) {
791 cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
792 listener(state, event, &bounds, phase, cx);
793 })
794 }
795
796 let hover_group_bounds = self
797 .group_hover_style
798 .as_ref()
799 .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
800
801 if let Some(group_bounds) = hover_group_bounds {
802 let hovered = group_bounds.contains_point(&cx.mouse_position());
803 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
804 if phase == DispatchPhase::Capture {
805 if group_bounds.contains_point(&event.position) != hovered {
806 cx.notify();
807 }
808 }
809 });
810 }
811
812 if self.hover_style.is_some()
813 || (cx.active_drag.is_some() && !self.drag_over_styles.is_empty())
814 {
815 let hovered = bounds.contains_point(&cx.mouse_position());
816 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
817 if phase == DispatchPhase::Capture {
818 if bounds.contains_point(&event.position) != hovered {
819 cx.notify();
820 }
821 }
822 });
823 }
824
825 if cx.active_drag.is_some() {
826 let drop_listeners = mem::take(&mut self.drop_listeners);
827 cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| {
828 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
829 if let Some(drag_state_type) =
830 cx.active_drag.as_ref().map(|drag| drag.view.entity_type())
831 {
832 for (drop_state_type, listener) in &drop_listeners {
833 if *drop_state_type == drag_state_type {
834 let drag = cx
835 .active_drag
836 .take()
837 .expect("checked for type drag state type above");
838 listener(view, drag.view.clone(), cx);
839 cx.notify();
840 cx.stop_propagation();
841 }
842 }
843 }
844 }
845 });
846 }
847
848 cx.with_key_dispatch(
849 self.key_context.clone(),
850 self.focus_handle.clone(),
851 |_, cx| f(cx),
852 );
853 }
854}
855
856impl<V: 'static> Default for Interactivity<V> {
857 fn default() -> Self {
858 Self {
859 active: false,
860 group_active: false,
861 hovered: false,
862 group_hovered: false,
863 focus: FocusState::None,
864 key_context: KeyContext::default(),
865 focus_handle: None,
866 scroll_offset: Point::default(),
867 base_style: StyleRefinement::default(),
868 focus_style: StyleRefinement::default(),
869 focus_in_style: StyleRefinement::default(),
870 in_focus_style: StyleRefinement::default(),
871 hover_style: StyleRefinement::default(),
872 group_hover_style: None,
873 active_style: StyleRefinement::default(),
874 group_active_style: None,
875 drag_over_styles: SmallVec::new(),
876 group_drag_over_styles: SmallVec::new(),
877 group: None,
878 dispatch_context: KeyContext::default(),
879 mouse_down_listeners: SmallVec::new(),
880 mouse_up_listeners: SmallVec::new(),
881 mouse_move_listeners: SmallVec::new(),
882 scroll_wheel_listeners: SmallVec::new(),
883 key_down_listeners: SmallVec::new(),
884 key_up_listeners: SmallVec::new(),
885 action_listeners: SmallVec::new(),
886 drop_listeners: SmallVec::new(),
887 click_listeners: SmallVec::new(),
888 drag_listener: None,
889 hover_listener: None,
890 tooltip_builder: None,
891 }
892 }
893}
894
895#[derive(Default)]
896pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
897
898impl GroupBounds {
899 pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
900 cx.default_global::<Self>()
901 .0
902 .get(name)
903 .and_then(|bounds_stack| bounds_stack.last())
904 .cloned()
905 }
906
907 pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
908 cx.default_global::<Self>()
909 .0
910 .entry(name)
911 .or_default()
912 .push(bounds);
913 }
914
915 pub fn pop(name: &SharedString, cx: &mut AppContext) {
916 cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
917 }
918}
919
920pub struct Focusable<V, E> {
921 focusability: Focusability<V>,
922 view_type: PhantomData<V>,
923 element: E,
924}
925
926pub struct Focusability<V> {
927 focus_handle: Option<FocusHandle>,
928 focus_listeners: FocusListeners<V>,
929 focus_style: StyleRefinement,
930 focus_in_style: StyleRefinement,
931 in_focus_style: StyleRefinement,
932}
933
934impl<V, E> FocusableComponent<V> for Focusable<V, E> {
935 fn focusability(&mut self) -> &mut Focusability<V> {
936 &mut self.focusability
937 }
938}
939
940impl<V: 'static, E: InteractiveComponent<V>> InteractiveComponent<V> for Focusable<V, E> {
941 fn interactivity(&mut self) -> &mut Interactivity<V> {
942 self.element.interactivity()
943 }
944}
945
946impl<V: 'static, E: StatefulInteractiveComponent<V>> StatefulInteractiveComponent<V>
947 for Focusable<V, E>
948{
949}
950
951pub struct Stateful<V, E> {
952 id: SharedString,
953 view_type: PhantomData<V>,
954 element: E,
955}
956
957impl<V: 'static, E: InteractiveComponent<V>> StatefulInteractiveComponent<V> for Stateful<V, E> {}
958
959impl<V: 'static, E: InteractiveComponent<V>> InteractiveComponent<V> for Stateful<V, E> {
960 fn interactivity(&mut self) -> &mut Interactivity<V> {
961 self.element.interactivity()
962 }
963}
964
965impl<V, E: FocusableComponent<V>> FocusableComponent<V> for Stateful<V, E> {
966 fn focusability(&mut self) -> &mut Focusability<V> {
967 self.element.focusability()
968 }
969}