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> {
278 fn interactivity(&mut self) -> &mut StatefulInteractivity<V>;
279
280 fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
281 where
282 Self: Sized,
283 {
284 self.interactivity().active_style = f(StyleRefinement::default());
285 self
286 }
287
288 fn group_active(
289 mut self,
290 group_name: impl Into<SharedString>,
291 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
292 ) -> Self
293 where
294 Self: Sized,
295 {
296 self.interactivity().group_active_style = Some(GroupStyle {
297 group: group_name.into(),
298 style: f(StyleRefinement::default()),
299 });
300 self
301 }
302
303 fn on_click(
304 mut self,
305 listener: impl Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static,
306 ) -> Self
307 where
308 Self: Sized,
309 {
310 self.interactivity()
311 .click_listeners
312 .push(Box::new(move |view, event, cx| listener(view, event, cx)));
313 self
314 }
315
316 fn on_drag<W>(
317 mut self,
318 listener: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
319 ) -> Self
320 where
321 Self: Sized,
322 W: 'static + Render,
323 {
324 debug_assert!(
325 self.interactivity().drag_listener.is_none(),
326 "calling on_drag more than once on the same element is not supported"
327 );
328 self.interactivity().drag_listener =
329 Some(Box::new(move |view_state, cursor_offset, cx| AnyDrag {
330 view: listener(view_state, cx).into(),
331 cursor_offset,
332 }));
333 self
334 }
335
336 fn on_hover(mut self, listener: impl 'static + Fn(&mut V, bool, &mut ViewContext<V>)) -> Self
337 where
338 Self: Sized,
339 {
340 debug_assert!(
341 self.interactivity().hover_listener.is_none(),
342 "calling on_hover more than once on the same element is not supported"
343 );
344 self.interactivity().hover_listener = Some(Box::new(listener));
345 self
346 }
347
348 fn tooltip<W>(
349 mut self,
350 build_tooltip: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
351 ) -> Self
352 where
353 Self: Sized,
354 W: 'static + Render,
355 {
356 debug_assert!(
357 self.interactivity().tooltip_builder.is_none(),
358 "calling tooltip more than once on the same element is not supported"
359 );
360 self.interactivity().tooltip_builder = Some(Arc::new(move |view_state, cx| {
361 build_tooltip(view_state, cx).into()
362 }));
363
364 self
365 }
366}
367
368pub trait FocusableComponent<V> {
369 fn focusability(&mut self) -> &mut Focusability<V>;
370
371 fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
372 where
373 Self: Sized,
374 {
375 self.focusability().focus_style = f(StyleRefinement::default());
376 self
377 }
378
379 fn focus_in(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
380 where
381 Self: Sized,
382 {
383 self.focusability().focus_in_style = f(StyleRefinement::default());
384 self
385 }
386
387 fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
388 where
389 Self: Sized,
390 {
391 // self.focusability(). (f(StyleRefinement::default()));
392 self
393 }
394
395 fn on_focus(
396 mut self,
397 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
398 ) -> Self
399 where
400 Self: Sized,
401 {
402 self.focusability()
403 .focus_listeners
404 .push(Box::new(move |view, focus_handle, event, cx| {
405 if event.focused.as_ref() == Some(focus_handle) {
406 listener(view, event, cx)
407 }
408 }));
409 self
410 }
411
412 fn on_blur(
413 mut self,
414 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
415 ) -> Self
416 where
417 Self: Sized,
418 {
419 self.focusability()
420 .focus_listeners
421 .push(Box::new(move |view, focus_handle, event, cx| {
422 if event.blurred.as_ref() == Some(focus_handle) {
423 listener(view, event, cx)
424 }
425 }));
426 self
427 }
428
429 fn on_focus_in(
430 mut self,
431 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
432 ) -> Self
433 where
434 Self: Sized,
435 {
436 self.focusability()
437 .focus_listeners
438 .push(Box::new(move |view, focus_handle, event, cx| {
439 let descendant_blurred = event
440 .blurred
441 .as_ref()
442 .map_or(false, |blurred| focus_handle.contains(blurred, cx));
443 let descendant_focused = event
444 .focused
445 .as_ref()
446 .map_or(false, |focused| focus_handle.contains(focused, cx));
447
448 if !descendant_blurred && descendant_focused {
449 listener(view, event, cx)
450 }
451 }));
452 self
453 }
454
455 fn on_focus_out(
456 mut self,
457 listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
458 ) -> Self
459 where
460 Self: Sized,
461 {
462 self.focusability()
463 .focus_listeners
464 .push(Box::new(move |view, focus_handle, event, cx| {
465 let descendant_blurred = event
466 .blurred
467 .as_ref()
468 .map_or(false, |blurred| focus_handle.contains(blurred, cx));
469 let descendant_focused = event
470 .focused
471 .as_ref()
472 .map_or(false, |focused| focus_handle.contains(focused, cx));
473 if descendant_blurred && !descendant_focused {
474 listener(view, event, cx)
475 }
476 }));
477 self
478 }
479}
480
481pub type FocusListeners<V> = SmallVec<[FocusListener<V>; 2]>;
482
483pub type FocusListener<V> =
484 Box<dyn Fn(&mut V, &FocusHandle, &FocusEvent, &mut ViewContext<V>) + 'static>;
485
486pub type MouseDownListener<V> = Box<
487 dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
488>;
489pub type MouseUpListener<V> = Box<
490 dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
491>;
492
493pub type MouseMoveListener<V> = Box<
494 dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
495>;
496
497pub type ScrollWheelListener<V> = Box<
498 dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
499 + 'static,
500>;
501
502pub type ClickListener<V> = Box<dyn Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static>;
503
504pub type DragListener<V> =
505 Box<dyn Fn(&mut V, Point<Pixels>, &mut ViewContext<V>) -> AnyDrag + 'static>;
506
507type DropListener<V> = dyn Fn(&mut V, AnyView, &mut ViewContext<V>) + 'static;
508
509pub type HoverListener<V> = Box<dyn Fn(&mut V, bool, &mut ViewContext<V>) + 'static>;
510
511pub type TooltipBuilder<V> = Arc<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static>;
512
513pub type KeyDownListener<V> =
514 Box<dyn Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + 'static>;
515
516pub type KeyUpListener<V> =
517 Box<dyn Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + 'static>;
518
519pub type ActionListener<V> =
520 Box<dyn Fn(&mut V, &dyn Any, DispatchPhase, &mut ViewContext<V>) + 'static>;
521
522pub struct FocusEvent {
523 pub blurred: Option<FocusHandle>,
524 pub focused: Option<FocusHandle>,
525}
526
527pub struct Node<V> {
528 style: StyleRefinement,
529 key_context: KeyContext,
530 interactivity: Interactivity<V>,
531 children: Vec<AnyElement<V>>,
532}
533
534impl<V> Node<V> {
535 fn compute_style(&self) -> Style {
536 let mut style = Style::default();
537 style.refine(&self.style);
538 style
539 }
540}
541
542impl<V> Styled for Node<V> {
543 fn style(&mut self) -> &mut StyleRefinement {
544 &mut self.style
545 }
546}
547
548impl<V: 'static> InteractiveComponent<V> for Node<V> {
549 fn interactivity(&mut self) -> &mut Interactivity<V> {
550 &mut self.interactivity
551 }
552}
553
554pub struct NodeState {
555 child_layout_ids: SmallVec<[LayoutId; 4]>,
556}
557
558impl<V: 'static> Element<V> for Node<V> {
559 type ElementState = NodeState;
560
561 fn id(&self) -> Option<crate::ElementId> {
562 None
563 }
564
565 fn initialize(
566 &mut self,
567 view_state: &mut V,
568 _: Option<Self::ElementState>,
569 cx: &mut ViewContext<V>,
570 ) -> Self::ElementState {
571 for child in &mut self.children {
572 child.initialize(view_state, cx);
573 }
574 NodeState {
575 child_layout_ids: SmallVec::new(),
576 }
577 }
578
579 fn layout(
580 &mut self,
581 view_state: &mut V,
582 element_state: &mut Self::ElementState,
583 cx: &mut ViewContext<V>,
584 ) -> crate::LayoutId {
585 let style = self.compute_style();
586 style.with_text_style(cx, |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
596 fn prepaint(
597 &mut self,
598 bounds: Bounds<Pixels>,
599 view_state: &mut V,
600 _: &mut Self::ElementState,
601 cx: &mut ViewContext<V>,
602 ) {
603 for child in &mut self.children {
604 child.prepaint(view_state, cx);
605 }
606 self.interactivity
607 .refine_style(&mut self.style, bounds, view_state, cx);
608 }
609
610 fn paint(
611 &mut self,
612 bounds: Bounds<Pixels>,
613 view_state: &mut V,
614 element_state: &mut Self::ElementState,
615 cx: &mut ViewContext<V>,
616 ) {
617 let style = self.compute_style();
618 if style.visibility == Visibility::Hidden {
619 return;
620 }
621
622 if let Some(mouse_cursor) = style.mouse_cursor {
623 let hovered = bounds.contains_point(&cx.mouse_position());
624 if hovered {
625 cx.set_cursor_style(mouse_cursor);
626 }
627 }
628
629 if let Some(group) = self.interactivity.group.clone() {
630 GroupBounds::push(group, bounds, cx);
631 }
632
633 let z_index = style.z_index.unwrap_or(0);
634
635 let mut child_min = point(Pixels::MAX, Pixels::MAX);
636 let mut child_max = Point::default();
637
638 let content_size = if element_state.child_layout_ids.is_empty() {
639 bounds.size
640 } else {
641 for child_layout_id in &element_state.child_layout_ids {
642 let child_bounds = cx.layout_bounds(*child_layout_id);
643 child_min = child_min.min(&child_bounds.origin);
644 child_max = child_max.max(&child_bounds.lower_right());
645 }
646 (child_max - child_min).into()
647 };
648
649 cx.with_z_index(z_index, |cx| {
650 cx.with_z_index(0, |cx| {
651 style.paint(bounds, cx);
652 self.interactivity.paint(bounds, cx);
653 });
654 cx.with_z_index(1, |cx| {
655 style.with_text_style(cx, |cx| {
656 style.apply_overflow(bounds, cx, |cx| {
657 let scroll_offset = self.interactivity.scroll_offset;
658 cx.with_element_offset2(scroll_offset, |cx| {
659 for child in &mut self.children {
660 child.paint(view_state, cx);
661 }
662 });
663 })
664 })
665 });
666 });
667
668 if let Some(group) = self.interactivity.group.as_ref() {
669 GroupBounds::pop(group, cx);
670 }
671 }
672}
673
674pub struct Interactivity<V> {
675 pub hover_style: StyleRefinement,
676 pub group_hover_style: Option<GroupStyle>,
677 pub drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
678 pub group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
679 group: Option<SharedString>,
680 pub dispatch_context: KeyContext,
681 pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
682 pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
683 pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
684 pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
685 pub key_down_listeners: SmallVec<[KeyDownListener<V>; 2]>,
686 pub key_up_listeners: SmallVec<[KeyUpListener<V>; 2]>,
687 pub action_listeners: SmallVec<[(TypeId, ActionListener<V>); 8]>,
688 drop_listeners: SmallVec<[(TypeId, Box<DropListener<V>>); 2]>,
689 scroll_offset: Point<Pixels>,
690}
691
692impl<V: 'static> Interactivity<V> {
693 fn refine_style(
694 &self,
695 style: &mut StyleRefinement,
696 bounds: Bounds<Pixels>,
697 cx: &mut ViewContext<V>,
698 ) {
699 let mouse_position = cx.mouse_position();
700 if let Some(group_hover) = self.group_hover_style.as_ref() {
701 if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
702 if group_bounds.contains_point(&mouse_position) {
703 style.refine(&group_hover.style);
704 }
705 }
706 }
707 if bounds.contains_point(&mouse_position) {
708 style.refine(&self.hover_style);
709 }
710
711 if let Some(drag) = cx.active_drag.take() {
712 for (state_type, group_drag_style) in &self.group_drag_over_styles {
713 if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) {
714 if *state_type == drag.view.entity_type()
715 && group_bounds.contains_point(&mouse_position)
716 {
717 style.refine(&group_drag_style.style);
718 }
719 }
720 }
721
722 for (state_type, drag_over_style) in &self.drag_over_styles {
723 if *state_type == drag.view.entity_type() && bounds.contains_point(&mouse_position)
724 {
725 style.refine(drag_over_style);
726 }
727 }
728
729 cx.active_drag = Some(drag);
730 }
731 }
732
733 fn paint(&mut self, bounds: Bounds<Pixels>, cx: &mut ViewContext<V>) {
734 for listener in self.mouse_down_listeners.drain(..) {
735 cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
736 listener(state, event, &bounds, phase, cx);
737 })
738 }
739
740 for listener in self.mouse_up_listeners.drain(..) {
741 cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
742 listener(state, event, &bounds, phase, cx);
743 })
744 }
745
746 for listener in self.mouse_move_listeners.drain(..) {
747 cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
748 listener(state, event, &bounds, phase, cx);
749 })
750 }
751
752 for listener in self.scroll_wheel_listeners.drain(..) {
753 cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
754 listener(state, event, &bounds, phase, cx);
755 })
756 }
757
758 let hover_group_bounds = self
759 .group_hover_style
760 .as_ref()
761 .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
762
763 if let Some(group_bounds) = hover_group_bounds {
764 let hovered = group_bounds.contains_point(&cx.mouse_position());
765 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
766 if phase == DispatchPhase::Capture {
767 if group_bounds.contains_point(&event.position) != hovered {
768 cx.notify();
769 }
770 }
771 });
772 }
773
774 if self.hover_style.is_some()
775 || (cx.active_drag.is_some() && !self.drag_over_styles.is_empty())
776 {
777 let hovered = bounds.contains_point(&cx.mouse_position());
778 cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
779 if phase == DispatchPhase::Capture {
780 if bounds.contains_point(&event.position) != hovered {
781 cx.notify();
782 }
783 }
784 });
785 }
786
787 if cx.active_drag.is_some() {
788 let drop_listeners = mem::take(&mut self.drop_listeners);
789 cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| {
790 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
791 if let Some(drag_state_type) =
792 cx.active_drag.as_ref().map(|drag| drag.view.entity_type())
793 {
794 for (drop_state_type, listener) in &drop_listeners {
795 if *drop_state_type == drag_state_type {
796 let drag = cx
797 .active_drag
798 .take()
799 .expect("checked for type drag state type above");
800 listener(view, drag.view.clone(), cx);
801 cx.notify();
802 cx.stop_propagation();
803 }
804 }
805 }
806 }
807 });
808 }
809 }
810}
811
812#[derive(Default)]
813pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
814
815impl GroupBounds {
816 pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
817 cx.default_global::<Self>()
818 .0
819 .get(name)
820 .and_then(|bounds_stack| bounds_stack.last())
821 .cloned()
822 }
823
824 pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
825 cx.default_global::<Self>()
826 .0
827 .entry(name)
828 .or_default()
829 .push(bounds);
830 }
831
832 pub fn pop(name: &SharedString, cx: &mut AppContext) {
833 cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
834 }
835}
836
837pub struct Focusable<V, E> {
838 focusability: Focusability<V>,
839 view_type: PhantomData<V>,
840 element: E,
841}
842
843pub struct Focusability<V> {
844 focus_handle: Option<FocusHandle>,
845 focus_listeners: FocusListeners<V>,
846 focus_style: StyleRefinement,
847 focus_in_style: StyleRefinement,
848 in_focus_style: StyleRefinement,
849}
850
851impl<V, E> FocusableComponent<V> for Focusable<V, E> {
852 fn focusability(&mut self) -> &mut Focusability<V> {
853 &mut self.focusability
854 }
855}
856
857impl<V: 'static, E: InteractiveComponent<V>> InteractiveComponent<V> for Focusable<V, E> {
858 fn interactivity(&mut self) -> &mut Interactivity<V> {
859 self.element.interactivity()
860 }
861}
862
863impl<V: 'static, E: StatefulInteractiveComponent<V>> StatefulInteractiveComponent<V>
864 for Focusable<V, E>
865{
866 fn interactivity(&mut self) -> &mut StatefulInteractivity<V> {
867 self.element.interactivity()
868 }
869}
870
871pub struct Stateful<V, E> {
872 id: SharedString,
873 interactivity: StatefulInteractivity<V>,
874 view_type: PhantomData<V>,
875 element: E,
876}
877
878pub struct StatefulInteractivity<V> {
879 click_listeners: SmallVec<[ClickListener<V>; 2]>,
880 active_style: StyleRefinement,
881 group_active_style: Option<GroupStyle>,
882 drag_listener: Option<DragListener<V>>,
883 hover_listener: Option<HoverListener<V>>,
884 tooltip_builder: Option<TooltipBuilder<V>>,
885}
886
887impl<V: 'static, E> StatefulInteractiveComponent<V> for Stateful<V, E> {
888 fn interactivity(&mut self) -> &mut StatefulInteractivity<V> {
889 &mut self.interactivity
890 }
891}
892
893impl<V: 'static, E: InteractiveComponent<V>> InteractiveComponent<V> for Stateful<V, E> {
894 fn interactivity(&mut self) -> &mut Interactivity<V> {
895 self.element.interactivity()
896 }
897}
898
899impl<V, E: FocusableComponent<V>> FocusableComponent<V> for Stateful<V, E> {
900 fn focusability(&mut self) -> &mut Focusability<V> {
901 self.element.focusability()
902 }
903}