1use crate::{
19 AbsoluteLength, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, App, Bounds, ClickEvent,
20 DispatchPhase, Element, ElementId, Entity, FocusHandle, Global, GlobalElementId, Hitbox,
21 HitboxBehavior, HitboxId, InspectorElementId, IntoElement, IsZero, KeyContext, KeyDownEvent,
22 KeyUpEvent, KeyboardButton, KeyboardClickEvent, LayoutId, ModifiersChangedEvent, MouseButton,
23 MouseClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Overflow, ParentElement, Pixels,
24 Point, Render, ScrollWheelEvent, SharedString, Size, Style, StyleRefinement, Styled, Task,
25 TooltipId, Visibility, Window, WindowControlArea, point, px, size,
26};
27use collections::HashMap;
28use refineable::Refineable;
29use smallvec::SmallVec;
30use stacksafe::{StackSafe, stacksafe};
31use std::{
32 any::{Any, TypeId},
33 cell::RefCell,
34 cmp::Ordering,
35 fmt::Debug,
36 marker::PhantomData,
37 mem,
38 rc::Rc,
39 sync::Arc,
40 time::Duration,
41};
42use util::ResultExt;
43
44use super::ImageCacheProvider;
45
46const DRAG_THRESHOLD: f64 = 2.;
47const TOOLTIP_SHOW_DELAY: Duration = Duration::from_millis(500);
48const HOVERABLE_TOOLTIP_HIDE_DELAY: Duration = Duration::from_millis(500);
49
50pub struct GroupStyle {
52 pub group: SharedString,
54
55 pub style: Box<StyleRefinement>,
58}
59
60pub struct DragMoveEvent<T> {
62 pub event: MouseMoveEvent,
64
65 pub bounds: Bounds<Pixels>,
67 drag: PhantomData<T>,
68 dragged_item: Arc<dyn Any>,
69}
70
71impl<T: 'static> DragMoveEvent<T> {
72 pub fn drag<'b>(&self, cx: &'b App) -> &'b T {
74 cx.active_drag
75 .as_ref()
76 .and_then(|drag| drag.value.downcast_ref::<T>())
77 .expect("DragMoveEvent is only valid when the stored active drag is of the same type.")
78 }
79
80 pub fn dragged_item(&self) -> &dyn Any {
82 self.dragged_item.as_ref()
83 }
84}
85
86#[derive(Clone, Debug)]
88pub struct ResizeEvent {
89 pub size: Size<Pixels>,
91 pub bounds: Bounds<Pixels>,
93}
94
95impl Interactivity {
96 #[cfg(any(feature = "inspector", debug_assertions))]
98 #[track_caller]
99 pub fn new() -> Interactivity {
100 Interactivity {
101 source_location: Some(core::panic::Location::caller()),
102 ..Default::default()
103 }
104 }
105
106 #[cfg(not(any(feature = "inspector", debug_assertions)))]
108 pub fn new() -> Interactivity {
109 Interactivity::default()
110 }
111
112 pub fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
114 #[cfg(any(feature = "inspector", debug_assertions))]
115 {
116 self.source_location
117 }
118
119 #[cfg(not(any(feature = "inspector", debug_assertions)))]
120 {
121 None
122 }
123 }
124
125 pub fn on_mouse_down(
130 &mut self,
131 button: MouseButton,
132 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
133 ) {
134 self.mouse_down_listeners
135 .push(Box::new(move |event, phase, hitbox, window, cx| {
136 if phase == DispatchPhase::Bubble
137 && event.button == button
138 && hitbox.is_hovered(window)
139 {
140 (listener)(event, window, cx)
141 }
142 }));
143 }
144
145 pub fn capture_any_mouse_down(
150 &mut self,
151 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
152 ) {
153 self.mouse_down_listeners
154 .push(Box::new(move |event, phase, hitbox, window, cx| {
155 if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {
156 (listener)(event, window, cx)
157 }
158 }));
159 }
160
161 pub fn on_any_mouse_down(
166 &mut self,
167 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
168 ) {
169 self.mouse_down_listeners
170 .push(Box::new(move |event, phase, hitbox, window, cx| {
171 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
172 (listener)(event, window, cx)
173 }
174 }));
175 }
176
177 pub fn on_mouse_up(
182 &mut self,
183 button: MouseButton,
184 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
185 ) {
186 self.mouse_up_listeners
187 .push(Box::new(move |event, phase, hitbox, window, cx| {
188 if phase == DispatchPhase::Bubble
189 && event.button == button
190 && hitbox.is_hovered(window)
191 {
192 (listener)(event, window, cx)
193 }
194 }));
195 }
196
197 pub fn capture_any_mouse_up(
202 &mut self,
203 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
204 ) {
205 self.mouse_up_listeners
206 .push(Box::new(move |event, phase, hitbox, window, cx| {
207 if phase == DispatchPhase::Capture && hitbox.is_hovered(window) {
208 (listener)(event, window, cx)
209 }
210 }));
211 }
212
213 pub fn on_any_mouse_up(
218 &mut self,
219 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
220 ) {
221 self.mouse_up_listeners
222 .push(Box::new(move |event, phase, hitbox, window, cx| {
223 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
224 (listener)(event, window, cx)
225 }
226 }));
227 }
228
229 pub fn on_mouse_down_out(
235 &mut self,
236 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
237 ) {
238 self.mouse_down_listeners
239 .push(Box::new(move |event, phase, hitbox, window, cx| {
240 if phase == DispatchPhase::Capture && !hitbox.contains(&window.mouse_position()) {
241 (listener)(event, window, cx)
242 }
243 }));
244 }
245
246 pub fn on_mouse_up_out(
252 &mut self,
253 button: MouseButton,
254 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
255 ) {
256 self.mouse_up_listeners
257 .push(Box::new(move |event, phase, hitbox, window, cx| {
258 if phase == DispatchPhase::Capture
259 && event.button == button
260 && !hitbox.is_hovered(window)
261 {
262 (listener)(event, window, cx);
263 }
264 }));
265 }
266
267 pub fn on_mouse_move(
272 &mut self,
273 listener: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static,
274 ) {
275 self.mouse_move_listeners
276 .push(Box::new(move |event, phase, hitbox, window, cx| {
277 if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
278 (listener)(event, window, cx);
279 }
280 }));
281 }
282
283 pub fn on_drag_move<T>(
291 &mut self,
292 listener: impl Fn(&DragMoveEvent<T>, &mut Window, &mut App) + 'static,
293 ) where
294 T: 'static,
295 {
296 self.mouse_move_listeners
297 .push(Box::new(move |event, phase, hitbox, window, cx| {
298 if phase == DispatchPhase::Capture
299 && let Some(drag) = &cx.active_drag
300 && drag.value.as_ref().type_id() == TypeId::of::<T>()
301 {
302 (listener)(
303 &DragMoveEvent {
304 event: event.clone(),
305 bounds: hitbox.bounds,
306 drag: PhantomData,
307 dragged_item: Arc::clone(&drag.value),
308 },
309 window,
310 cx,
311 );
312 }
313 }));
314 }
315
316 pub fn on_scroll_wheel(
321 &mut self,
322 listener: impl Fn(&ScrollWheelEvent, &mut Window, &mut App) + 'static,
323 ) {
324 self.scroll_wheel_listeners
325 .push(Box::new(move |event, phase, hitbox, window, cx| {
326 if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) {
327 (listener)(event, window, cx);
328 }
329 }));
330 }
331
332 pub fn capture_action<A: Action>(
337 &mut self,
338 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
339 ) {
340 self.action_listeners.push((
341 TypeId::of::<A>(),
342 Box::new(move |action, phase, window, cx| {
343 let action = action.downcast_ref().unwrap();
344 if phase == DispatchPhase::Capture {
345 (listener)(action, window, cx)
346 } else {
347 cx.propagate();
348 }
349 }),
350 ));
351 }
352
353 pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut Window, &mut App) + 'static) {
358 self.action_listeners.push((
359 TypeId::of::<A>(),
360 Box::new(move |action, phase, window, cx| {
361 let action = action.downcast_ref().unwrap();
362 if phase == DispatchPhase::Bubble {
363 (listener)(action, window, cx)
364 }
365 }),
366 ));
367 }
368
369 pub fn on_boxed_action(
376 &mut self,
377 action: &dyn Action,
378 listener: impl Fn(&dyn Action, &mut Window, &mut App) + 'static,
379 ) {
380 let action = action.boxed_clone();
381 self.action_listeners.push((
382 (*action).type_id(),
383 Box::new(move |_, phase, window, cx| {
384 if phase == DispatchPhase::Bubble {
385 (listener)(&*action, window, cx)
386 }
387 }),
388 ));
389 }
390
391 pub fn on_key_down(
396 &mut self,
397 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
398 ) {
399 self.key_down_listeners
400 .push(Box::new(move |event, phase, window, cx| {
401 if phase == DispatchPhase::Bubble {
402 (listener)(event, window, cx)
403 }
404 }));
405 }
406
407 pub fn capture_key_down(
412 &mut self,
413 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
414 ) {
415 self.key_down_listeners
416 .push(Box::new(move |event, phase, window, cx| {
417 if phase == DispatchPhase::Capture {
418 listener(event, window, cx)
419 }
420 }));
421 }
422
423 pub fn on_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static) {
428 self.key_up_listeners
429 .push(Box::new(move |event, phase, window, cx| {
430 if phase == DispatchPhase::Bubble {
431 listener(event, window, cx)
432 }
433 }));
434 }
435
436 pub fn capture_key_up(
441 &mut self,
442 listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
443 ) {
444 self.key_up_listeners
445 .push(Box::new(move |event, phase, window, cx| {
446 if phase == DispatchPhase::Capture {
447 listener(event, window, cx)
448 }
449 }));
450 }
451
452 pub fn on_modifiers_changed(
457 &mut self,
458 listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,
459 ) {
460 self.modifiers_changed_listeners
461 .push(Box::new(move |event, window, cx| {
462 listener(event, window, cx)
463 }));
464 }
465
466 pub fn on_drop<T: 'static>(&mut self, listener: impl Fn(&T, &mut Window, &mut App) + 'static) {
471 self.drop_listeners.push((
472 TypeId::of::<T>(),
473 Box::new(move |dragged_value, window, cx| {
474 listener(dragged_value.downcast_ref().unwrap(), window, cx);
475 }),
476 ));
477 }
478
479 pub fn can_drop(
482 &mut self,
483 predicate: impl Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static,
484 ) {
485 self.can_drop_predicate = Some(Box::new(predicate));
486 }
487
488 pub fn on_click(&mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static)
493 where
494 Self: Sized,
495 {
496 self.click_listeners.push(Rc::new(move |event, window, cx| {
497 listener(event, window, cx)
498 }));
499 }
500
501 pub fn on_drag<T, W>(
508 &mut self,
509 value: T,
510 constructor: impl Fn(&T, Point<Pixels>, &mut Window, &mut App) -> Entity<W> + 'static,
511 ) where
512 Self: Sized,
513 T: 'static,
514 W: 'static + Render,
515 {
516 debug_assert!(
517 self.drag_listener.is_none(),
518 "calling on_drag more than once on the same element is not supported"
519 );
520 self.drag_listener = Some((
521 Arc::new(value),
522 Box::new(move |value, offset, window, cx| {
523 constructor(value.downcast_ref().unwrap(), offset, window, cx).into()
524 }),
525 ));
526 }
527
528 pub fn on_hover(&mut self, listener: impl Fn(&bool, &mut Window, &mut App) + 'static)
534 where
535 Self: Sized,
536 {
537 debug_assert!(
538 self.hover_listener.is_none(),
539 "calling on_hover more than once on the same element is not supported"
540 );
541 self.hover_listener = Some(Box::new(listener));
542 }
543
544 pub fn on_resize(&mut self, listener: impl Fn(&ResizeEvent, &mut Window, &mut App) + 'static)
549 where
550 Self: Sized,
551 {
552 self.resize_listeners.push(Box::new(listener));
553 }
554
555 pub fn tooltip(&mut self, build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static)
558 where
559 Self: Sized,
560 {
561 debug_assert!(
562 self.tooltip_builder.is_none(),
563 "calling tooltip more than once on the same element is not supported"
564 );
565 self.tooltip_builder = Some(TooltipBuilder {
566 build: Rc::new(build_tooltip),
567 hoverable: false,
568 });
569 }
570
571 pub fn hoverable_tooltip(
575 &mut self,
576 build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
577 ) where
578 Self: Sized,
579 {
580 debug_assert!(
581 self.tooltip_builder.is_none(),
582 "calling tooltip more than once on the same element is not supported"
583 );
584 self.tooltip_builder = Some(TooltipBuilder {
585 build: Rc::new(build_tooltip),
586 hoverable: true,
587 });
588 }
589
590 pub fn occlude_mouse(&mut self) {
595 self.hitbox_behavior = HitboxBehavior::BlockMouse;
596 }
597
598 pub fn window_control_area(&mut self, area: WindowControlArea) {
601 self.window_control = Some(area);
602 }
603
604 pub fn block_mouse_except_scroll(&mut self) {
609 self.hitbox_behavior = HitboxBehavior::BlockMouseExceptScroll;
610 }
611}
612
613pub trait InteractiveElement: Sized {
616 fn interactivity(&mut self) -> &mut Interactivity;
618
619 fn group(mut self, group: impl Into<SharedString>) -> Self {
621 self.interactivity().group = Some(group.into());
622 self
623 }
624
625 fn id(mut self, id: impl Into<ElementId>) -> Stateful<Self> {
627 self.interactivity().element_id = Some(id.into());
628
629 Stateful { element: self }
630 }
631
632 fn track_focus(mut self, focus_handle: &FocusHandle) -> Self {
636 self.interactivity().focusable = true;
637 self.interactivity().tracked_focus_handle = Some(focus_handle.clone());
638 self
639 }
640
641 fn tab_stop(mut self, tab_stop: bool) -> Self {
648 self.interactivity().tab_stop = tab_stop;
649 self
650 }
651
652 fn tab_index(mut self, index: isize) -> Self {
657 self.interactivity().focusable = true;
658 self.interactivity().tab_index = Some(index);
659 self.interactivity().tab_stop = true;
660 self
661 }
662
663 fn tab_group(mut self) -> Self {
668 self.interactivity().tab_group = true;
669 if self.interactivity().tab_index.is_none() {
670 self.interactivity().tab_index = Some(0);
671 }
672 self
673 }
674
675 fn key_context<C, E>(mut self, key_context: C) -> Self
678 where
679 C: TryInto<KeyContext, Error = E>,
680 E: Debug,
681 {
682 if let Some(key_context) = key_context.try_into().log_err() {
683 self.interactivity().key_context = Some(key_context);
684 }
685 self
686 }
687
688 fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
690 debug_assert!(
691 self.interactivity().hover_style.is_none(),
692 "hover style already set"
693 );
694 self.interactivity().hover_style = Some(Box::new(f(StyleRefinement::default())));
695 self
696 }
697
698 fn group_hover(
700 mut self,
701 group_name: impl Into<SharedString>,
702 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
703 ) -> Self {
704 self.interactivity().group_hover_style = Some(GroupStyle {
705 group: group_name.into(),
706 style: Box::new(f(StyleRefinement::default())),
707 });
708 self
709 }
710
711 fn on_mouse_down(
716 mut self,
717 button: MouseButton,
718 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
719 ) -> Self {
720 self.interactivity().on_mouse_down(button, listener);
721 self
722 }
723
724 #[cfg(any(test, feature = "test-support"))]
725 fn debug_selector(mut self, f: impl FnOnce() -> String) -> Self {
729 self.interactivity().debug_selector = Some(f());
730 self
731 }
732
733 #[cfg(not(any(test, feature = "test-support")))]
734 #[inline]
738 fn debug_selector(self, _: impl FnOnce() -> String) -> Self {
739 self
740 }
741
742 fn capture_any_mouse_down(
747 mut self,
748 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
749 ) -> Self {
750 self.interactivity().capture_any_mouse_down(listener);
751 self
752 }
753
754 fn on_any_mouse_down(
759 mut self,
760 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
761 ) -> Self {
762 self.interactivity().on_any_mouse_down(listener);
763 self
764 }
765
766 fn on_mouse_up(
771 mut self,
772 button: MouseButton,
773 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
774 ) -> Self {
775 self.interactivity().on_mouse_up(button, listener);
776 self
777 }
778
779 fn capture_any_mouse_up(
784 mut self,
785 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
786 ) -> Self {
787 self.interactivity().capture_any_mouse_up(listener);
788 self
789 }
790
791 fn on_mouse_down_out(
797 mut self,
798 listener: impl Fn(&MouseDownEvent, &mut Window, &mut App) + 'static,
799 ) -> Self {
800 self.interactivity().on_mouse_down_out(listener);
801 self
802 }
803
804 fn on_mouse_up_out(
810 mut self,
811 button: MouseButton,
812 listener: impl Fn(&MouseUpEvent, &mut Window, &mut App) + 'static,
813 ) -> Self {
814 self.interactivity().on_mouse_up_out(button, listener);
815 self
816 }
817
818 fn on_mouse_move(
823 mut self,
824 listener: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static,
825 ) -> Self {
826 self.interactivity().on_mouse_move(listener);
827 self
828 }
829
830 fn on_drag_move<T: 'static>(
838 mut self,
839 listener: impl Fn(&DragMoveEvent<T>, &mut Window, &mut App) + 'static,
840 ) -> Self {
841 self.interactivity().on_drag_move(listener);
842 self
843 }
844
845 fn on_scroll_wheel(
850 mut self,
851 listener: impl Fn(&ScrollWheelEvent, &mut Window, &mut App) + 'static,
852 ) -> Self {
853 self.interactivity().on_scroll_wheel(listener);
854 self
855 }
856
857 fn capture_action<A: Action>(
862 mut self,
863 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
864 ) -> Self {
865 self.interactivity().capture_action(listener);
866 self
867 }
868
869 fn on_action<A: Action>(
874 mut self,
875 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
876 ) -> Self {
877 self.interactivity().on_action(listener);
878 self
879 }
880
881 fn on_boxed_action(
888 mut self,
889 action: &dyn Action,
890 listener: impl Fn(&dyn Action, &mut Window, &mut App) + 'static,
891 ) -> Self {
892 self.interactivity().on_boxed_action(action, listener);
893 self
894 }
895
896 fn on_key_down(
901 mut self,
902 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
903 ) -> Self {
904 self.interactivity().on_key_down(listener);
905 self
906 }
907
908 fn capture_key_down(
913 mut self,
914 listener: impl Fn(&KeyDownEvent, &mut Window, &mut App) + 'static,
915 ) -> Self {
916 self.interactivity().capture_key_down(listener);
917 self
918 }
919
920 fn on_key_up(
925 mut self,
926 listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
927 ) -> Self {
928 self.interactivity().on_key_up(listener);
929 self
930 }
931
932 fn capture_key_up(
937 mut self,
938 listener: impl Fn(&KeyUpEvent, &mut Window, &mut App) + 'static,
939 ) -> Self {
940 self.interactivity().capture_key_up(listener);
941 self
942 }
943
944 fn on_modifiers_changed(
949 mut self,
950 listener: impl Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static,
951 ) -> Self {
952 self.interactivity().on_modifiers_changed(listener);
953 self
954 }
955
956 fn drag_over<S: 'static>(
958 mut self,
959 f: impl 'static + Fn(StyleRefinement, &S, &mut Window, &mut App) -> StyleRefinement,
960 ) -> Self {
961 self.interactivity().drag_over_styles.push((
962 TypeId::of::<S>(),
963 Box::new(move |currently_dragged: &dyn Any, window, cx| {
964 f(
965 StyleRefinement::default(),
966 currently_dragged.downcast_ref::<S>().unwrap(),
967 window,
968 cx,
969 )
970 }),
971 ));
972 self
973 }
974
975 fn group_drag_over<S: 'static>(
977 mut self,
978 group_name: impl Into<SharedString>,
979 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
980 ) -> Self {
981 self.interactivity().group_drag_over_styles.push((
982 TypeId::of::<S>(),
983 GroupStyle {
984 group: group_name.into(),
985 style: Box::new(f(StyleRefinement::default())),
986 },
987 ));
988 self
989 }
990
991 fn on_drop<T: 'static>(
996 mut self,
997 listener: impl Fn(&T, &mut Window, &mut App) + 'static,
998 ) -> Self {
999 self.interactivity().on_drop(listener);
1000 self
1001 }
1002
1003 fn can_drop(
1006 mut self,
1007 predicate: impl Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static,
1008 ) -> Self {
1009 self.interactivity().can_drop(predicate);
1010 self
1011 }
1012
1013 fn occlude(mut self) -> Self {
1017 self.interactivity().occlude_mouse();
1018 self
1019 }
1020
1021 fn window_control_area(mut self, area: WindowControlArea) -> Self {
1024 self.interactivity().window_control_area(area);
1025 self
1026 }
1027
1028 fn block_mouse_except_scroll(mut self) -> Self {
1033 self.interactivity().block_mouse_except_scroll();
1034 self
1035 }
1036
1037 fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1040 where
1041 Self: Sized,
1042 {
1043 self.interactivity().focus_style = Some(Box::new(f(StyleRefinement::default())));
1044 self
1045 }
1046
1047 fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1050 where
1051 Self: Sized,
1052 {
1053 self.interactivity().in_focus_style = Some(Box::new(f(StyleRefinement::default())));
1054 self
1055 }
1056}
1057
1058pub trait StatefulInteractiveElement: InteractiveElement {
1061 fn focusable(mut self) -> Self {
1063 self.interactivity().focusable = true;
1064 self
1065 }
1066
1067 fn overflow_scroll(mut self) -> Self {
1069 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
1070 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
1071 self
1072 }
1073
1074 fn overflow_x_scroll(mut self) -> Self {
1076 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
1077 self
1078 }
1079
1080 fn overflow_y_scroll(mut self) -> Self {
1082 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
1083 self
1084 }
1085
1086 fn scrollbar_width(mut self, width: impl Into<AbsoluteLength>) -> Self {
1091 self.interactivity().base_style.scrollbar_width = Some(width.into());
1092 self
1093 }
1094
1095 fn track_scroll(mut self, scroll_handle: &ScrollHandle) -> Self {
1097 self.interactivity().tracked_scroll_handle = Some(scroll_handle.clone());
1098 self
1099 }
1100
1101 fn anchor_scroll(mut self, scroll_anchor: Option<ScrollAnchor>) -> Self {
1103 self.interactivity().scroll_anchor = scroll_anchor;
1104 self
1105 }
1106
1107 fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
1109 where
1110 Self: Sized,
1111 {
1112 self.interactivity().active_style = Some(Box::new(f(StyleRefinement::default())));
1113 self
1114 }
1115
1116 fn group_active(
1118 mut self,
1119 group_name: impl Into<SharedString>,
1120 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
1121 ) -> Self
1122 where
1123 Self: Sized,
1124 {
1125 self.interactivity().group_active_style = Some(GroupStyle {
1126 group: group_name.into(),
1127 style: Box::new(f(StyleRefinement::default())),
1128 });
1129 self
1130 }
1131
1132 fn on_click(mut self, listener: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self
1137 where
1138 Self: Sized,
1139 {
1140 self.interactivity().on_click(listener);
1141 self
1142 }
1143
1144 fn on_drag<T, W>(
1152 mut self,
1153 value: T,
1154 constructor: impl Fn(&T, Point<Pixels>, &mut Window, &mut App) -> Entity<W> + 'static,
1155 ) -> Self
1156 where
1157 Self: Sized,
1158 T: 'static,
1159 W: 'static + Render,
1160 {
1161 self.interactivity().on_drag(value, constructor);
1162 self
1163 }
1164
1165 fn on_hover(mut self, listener: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self
1171 where
1172 Self: Sized,
1173 {
1174 self.interactivity().on_hover(listener);
1175 self
1176 }
1177
1178 fn tooltip(mut self, build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self
1181 where
1182 Self: Sized,
1183 {
1184 self.interactivity().tooltip(build_tooltip);
1185 self
1186 }
1187
1188 fn hoverable_tooltip(
1192 mut self,
1193 build_tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
1194 ) -> Self
1195 where
1196 Self: Sized,
1197 {
1198 self.interactivity().hoverable_tooltip(build_tooltip);
1199 self
1200 }
1201
1202 fn on_resize(mut self, listener: impl Fn(&ResizeEvent, &mut Window, &mut App) + 'static) -> Self
1207 where
1208 Self: Sized,
1209 {
1210 self.interactivity().on_resize(listener);
1211 self
1212 }
1213}
1214
1215pub(crate) type MouseDownListener =
1216 Box<dyn Fn(&MouseDownEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1217pub(crate) type MouseUpListener =
1218 Box<dyn Fn(&MouseUpEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1219
1220pub(crate) type MouseMoveListener =
1221 Box<dyn Fn(&MouseMoveEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1222
1223pub(crate) type ScrollWheelListener =
1224 Box<dyn Fn(&ScrollWheelEvent, DispatchPhase, &Hitbox, &mut Window, &mut App) + 'static>;
1225
1226pub(crate) type ClickListener = Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>;
1227
1228pub(crate) type DragListener =
1229 Box<dyn Fn(&dyn Any, Point<Pixels>, &mut Window, &mut App) -> AnyView + 'static>;
1230
1231type DropListener = Box<dyn Fn(&dyn Any, &mut Window, &mut App) + 'static>;
1232
1233type CanDropPredicate = Box<dyn Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static>;
1234
1235pub(crate) struct TooltipBuilder {
1236 build: Rc<dyn Fn(&mut Window, &mut App) -> AnyView + 'static>,
1237 hoverable: bool,
1238}
1239
1240pub(crate) type KeyDownListener =
1241 Box<dyn Fn(&KeyDownEvent, DispatchPhase, &mut Window, &mut App) + 'static>;
1242
1243pub(crate) type KeyUpListener =
1244 Box<dyn Fn(&KeyUpEvent, DispatchPhase, &mut Window, &mut App) + 'static>;
1245
1246pub(crate) type ModifiersChangedListener =
1247 Box<dyn Fn(&ModifiersChangedEvent, &mut Window, &mut App) + 'static>;
1248
1249pub(crate) type ActionListener =
1250 Box<dyn Fn(&dyn Any, DispatchPhase, &mut Window, &mut App) + 'static>;
1251
1252pub(crate) type ResizeListener = Box<dyn Fn(&ResizeEvent, &mut Window, &mut App) + 'static>;
1253
1254#[track_caller]
1256pub fn div() -> Div {
1257 Div {
1258 interactivity: Interactivity::new(),
1259 children: SmallVec::default(),
1260 prepaint_listener: None,
1261 image_cache: None,
1262 }
1263}
1264
1265pub struct Div {
1267 interactivity: Interactivity,
1268 children: SmallVec<[StackSafe<AnyElement>; 2]>,
1269 prepaint_listener: Option<Box<dyn Fn(Vec<Bounds<Pixels>>, &mut Window, &mut App) + 'static>>,
1270 image_cache: Option<Box<dyn ImageCacheProvider>>,
1271}
1272
1273impl Div {
1274 pub fn on_children_prepainted(
1277 mut self,
1278 listener: impl Fn(Vec<Bounds<Pixels>>, &mut Window, &mut App) + 'static,
1279 ) -> Self {
1280 self.prepaint_listener = Some(Box::new(listener));
1281 self
1282 }
1283
1284 pub fn image_cache(mut self, cache: impl ImageCacheProvider) -> Self {
1286 self.image_cache = Some(Box::new(cache));
1287 self
1288 }
1289}
1290
1291pub struct DivFrameState {
1298 child_layout_ids: SmallVec<[LayoutId; 2]>,
1299}
1300
1301#[derive(Clone)]
1303pub struct DivInspectorState {
1304 #[cfg(any(feature = "inspector", debug_assertions))]
1308 pub base_style: Box<StyleRefinement>,
1309 pub bounds: Bounds<Pixels>,
1311 pub content_size: Size<Pixels>,
1313}
1314
1315impl Styled for Div {
1316 fn style(&mut self) -> &mut StyleRefinement {
1317 &mut self.interactivity.base_style
1318 }
1319}
1320
1321impl InteractiveElement for Div {
1322 fn interactivity(&mut self) -> &mut Interactivity {
1323 &mut self.interactivity
1324 }
1325}
1326
1327impl ParentElement for Div {
1328 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
1329 self.children
1330 .extend(elements.into_iter().map(StackSafe::new))
1331 }
1332}
1333
1334impl Element for Div {
1335 type RequestLayoutState = DivFrameState;
1336 type PrepaintState = Option<Hitbox>;
1337
1338 fn id(&self) -> Option<ElementId> {
1339 self.interactivity.element_id.clone()
1340 }
1341
1342 fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
1343 self.interactivity.source_location()
1344 }
1345
1346 #[stacksafe]
1347 fn request_layout(
1348 &mut self,
1349 global_id: Option<&GlobalElementId>,
1350 inspector_id: Option<&InspectorElementId>,
1351 window: &mut Window,
1352 cx: &mut App,
1353 ) -> (LayoutId, Self::RequestLayoutState) {
1354 let mut child_layout_ids = SmallVec::new();
1355 let image_cache = self
1356 .image_cache
1357 .as_mut()
1358 .map(|provider| provider.provide(window, cx));
1359
1360 let layout_id = window.with_image_cache(image_cache, |window| {
1361 self.interactivity.request_layout(
1362 global_id,
1363 inspector_id,
1364 window,
1365 cx,
1366 |style, window, cx| {
1367 window.with_text_style(style.text_style().cloned(), |window| {
1368 child_layout_ids = self
1369 .children
1370 .iter_mut()
1371 .map(|child| child.request_layout(window, cx))
1372 .collect::<SmallVec<_>>();
1373 window.request_layout(style, child_layout_ids.iter().copied(), cx)
1374 })
1375 },
1376 )
1377 });
1378
1379 (layout_id, DivFrameState { child_layout_ids })
1380 }
1381
1382 #[stacksafe]
1383 fn prepaint(
1384 &mut self,
1385 global_id: Option<&GlobalElementId>,
1386 inspector_id: Option<&InspectorElementId>,
1387 bounds: Bounds<Pixels>,
1388 request_layout: &mut Self::RequestLayoutState,
1389 window: &mut Window,
1390 cx: &mut App,
1391 ) -> Option<Hitbox> {
1392 let has_prepaint_listener = self.prepaint_listener.is_some();
1393 let mut children_bounds = Vec::with_capacity(if has_prepaint_listener {
1394 request_layout.child_layout_ids.len()
1395 } else {
1396 0
1397 });
1398
1399 let mut child_min = point(Pixels::MAX, Pixels::MAX);
1400 let mut child_max = Point::default();
1401 if let Some(handle) = self.interactivity.scroll_anchor.as_ref() {
1402 *handle.last_origin.borrow_mut() = bounds.origin - window.element_offset();
1403 }
1404 let content_size = if request_layout.child_layout_ids.is_empty() {
1405 bounds.size
1406 } else if let Some(scroll_handle) = self.interactivity.tracked_scroll_handle.as_ref() {
1407 let mut state = scroll_handle.0.borrow_mut();
1408 state.child_bounds = Vec::with_capacity(request_layout.child_layout_ids.len());
1409 for child_layout_id in &request_layout.child_layout_ids {
1410 let child_bounds = window.layout_bounds(*child_layout_id);
1411 child_min = child_min.min(&child_bounds.origin);
1412 child_max = child_max.max(&child_bounds.bottom_right());
1413 state.child_bounds.push(child_bounds);
1414 }
1415 (child_max - child_min).into()
1416 } else {
1417 for child_layout_id in &request_layout.child_layout_ids {
1418 let child_bounds = window.layout_bounds(*child_layout_id);
1419 child_min = child_min.min(&child_bounds.origin);
1420 child_max = child_max.max(&child_bounds.bottom_right());
1421
1422 if has_prepaint_listener {
1423 children_bounds.push(child_bounds);
1424 }
1425 }
1426 (child_max - child_min).into()
1427 };
1428
1429 if let Some(scroll_handle) = self.interactivity.tracked_scroll_handle.as_ref() {
1430 scroll_handle.scroll_to_active_item();
1431 }
1432
1433 self.interactivity.prepaint(
1434 global_id,
1435 inspector_id,
1436 bounds,
1437 content_size,
1438 window,
1439 cx,
1440 |_style, scroll_offset, hitbox, window, cx| {
1441 window.with_element_offset(scroll_offset, |window| {
1442 for child in &mut self.children {
1443 child.prepaint(window, cx);
1444 }
1445 });
1446
1447 if let Some(listener) = self.prepaint_listener.as_ref() {
1448 listener(children_bounds, window, cx);
1449 }
1450
1451 hitbox
1452 },
1453 )
1454 }
1455
1456 #[stacksafe]
1457 fn paint(
1458 &mut self,
1459 global_id: Option<&GlobalElementId>,
1460 inspector_id: Option<&InspectorElementId>,
1461 bounds: Bounds<Pixels>,
1462 _request_layout: &mut Self::RequestLayoutState,
1463 hitbox: &mut Option<Hitbox>,
1464 window: &mut Window,
1465 cx: &mut App,
1466 ) {
1467 let image_cache = self
1468 .image_cache
1469 .as_mut()
1470 .map(|provider| provider.provide(window, cx));
1471
1472 window.with_image_cache(image_cache, |window| {
1473 self.interactivity.paint(
1474 global_id,
1475 inspector_id,
1476 bounds,
1477 hitbox.as_ref(),
1478 window,
1479 cx,
1480 |_style, window, cx| {
1481 for child in &mut self.children {
1482 child.paint(window, cx);
1483 }
1484 },
1485 )
1486 });
1487 }
1488}
1489
1490impl IntoElement for Div {
1491 type Element = Self;
1492
1493 fn into_element(self) -> Self::Element {
1494 self
1495 }
1496}
1497
1498#[derive(Default)]
1501pub struct Interactivity {
1502 pub element_id: Option<ElementId>,
1504 pub active: Option<bool>,
1506 pub hovered: Option<bool>,
1509 pub(crate) tooltip_id: Option<TooltipId>,
1510 pub(crate) content_size: Size<Pixels>,
1511 pub(crate) key_context: Option<KeyContext>,
1512 pub(crate) focusable: bool,
1513 pub(crate) tracked_focus_handle: Option<FocusHandle>,
1514 pub(crate) tracked_scroll_handle: Option<ScrollHandle>,
1515 pub(crate) scroll_anchor: Option<ScrollAnchor>,
1516 pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
1517 pub(crate) group: Option<SharedString>,
1518 pub base_style: Box<StyleRefinement>,
1521 pub(crate) focus_style: Option<Box<StyleRefinement>>,
1522 pub(crate) in_focus_style: Option<Box<StyleRefinement>>,
1523 pub(crate) hover_style: Option<Box<StyleRefinement>>,
1524 pub(crate) group_hover_style: Option<GroupStyle>,
1525 pub(crate) active_style: Option<Box<StyleRefinement>>,
1526 pub(crate) group_active_style: Option<GroupStyle>,
1527 pub(crate) drag_over_styles: Vec<(
1528 TypeId,
1529 Box<dyn Fn(&dyn Any, &mut Window, &mut App) -> StyleRefinement>,
1530 )>,
1531 pub(crate) group_drag_over_styles: Vec<(TypeId, GroupStyle)>,
1532 pub(crate) mouse_down_listeners: Vec<MouseDownListener>,
1533 pub(crate) mouse_up_listeners: Vec<MouseUpListener>,
1534 pub(crate) mouse_move_listeners: Vec<MouseMoveListener>,
1535 pub(crate) scroll_wheel_listeners: Vec<ScrollWheelListener>,
1536 pub(crate) key_down_listeners: Vec<KeyDownListener>,
1537 pub(crate) key_up_listeners: Vec<KeyUpListener>,
1538 pub(crate) modifiers_changed_listeners: Vec<ModifiersChangedListener>,
1539 pub(crate) action_listeners: Vec<(TypeId, ActionListener)>,
1540 pub(crate) drop_listeners: Vec<(TypeId, DropListener)>,
1541 pub(crate) can_drop_predicate: Option<CanDropPredicate>,
1542 pub(crate) click_listeners: Vec<ClickListener>,
1543 pub(crate) drag_listener: Option<(Arc<dyn Any>, DragListener)>,
1544 pub(crate) hover_listener: Option<Box<dyn Fn(&bool, &mut Window, &mut App)>>,
1545 pub(crate) resize_listeners: Vec<ResizeListener>,
1546 pub(crate) tooltip_builder: Option<TooltipBuilder>,
1547 pub(crate) window_control: Option<WindowControlArea>,
1548 pub(crate) hitbox_behavior: HitboxBehavior,
1549 pub(crate) tab_index: Option<isize>,
1550 pub(crate) tab_group: bool,
1551 pub(crate) tab_stop: bool,
1552
1553 #[cfg(any(feature = "inspector", debug_assertions))]
1554 pub(crate) source_location: Option<&'static core::panic::Location<'static>>,
1555
1556 #[cfg(any(test, feature = "test-support"))]
1557 pub(crate) debug_selector: Option<String>,
1558}
1559
1560impl Interactivity {
1561 pub fn request_layout(
1563 &mut self,
1564 global_id: Option<&GlobalElementId>,
1565 _inspector_id: Option<&InspectorElementId>,
1566 window: &mut Window,
1567 cx: &mut App,
1568 f: impl FnOnce(Style, &mut Window, &mut App) -> LayoutId,
1569 ) -> LayoutId {
1570 #[cfg(any(feature = "inspector", debug_assertions))]
1571 window.with_inspector_state(
1572 _inspector_id,
1573 cx,
1574 |inspector_state: &mut Option<DivInspectorState>, _window| {
1575 if let Some(inspector_state) = inspector_state {
1576 self.base_style = inspector_state.base_style.clone();
1577 } else {
1578 *inspector_state = Some(DivInspectorState {
1579 base_style: self.base_style.clone(),
1580 bounds: Default::default(),
1581 content_size: Default::default(),
1582 })
1583 }
1584 },
1585 );
1586
1587 window.with_optional_element_state::<InteractiveElementState, _>(
1588 global_id,
1589 |element_state, window| {
1590 let mut element_state =
1591 element_state.map(|element_state| element_state.unwrap_or_default());
1592
1593 if let Some(element_state) = element_state.as_ref()
1594 && cx.has_active_drag()
1595 {
1596 if let Some(pending_mouse_down) = element_state.pending_mouse_down.as_ref() {
1597 *pending_mouse_down.borrow_mut() = None;
1598 }
1599 if let Some(clicked_state) = element_state.clicked_state.as_ref() {
1600 *clicked_state.borrow_mut() = ElementClickedState::default();
1601 }
1602 }
1603
1604 if self.focusable
1609 && self.tracked_focus_handle.is_none()
1610 && let Some(element_state) = element_state.as_mut()
1611 {
1612 let mut handle = element_state
1613 .focus_handle
1614 .get_or_insert_with(|| cx.focus_handle())
1615 .clone()
1616 .tab_stop(self.tab_stop);
1617
1618 if let Some(index) = self.tab_index {
1619 handle = handle.tab_index(index);
1620 }
1621
1622 self.tracked_focus_handle = Some(handle);
1623 }
1624
1625 if let Some(scroll_handle) = self.tracked_scroll_handle.as_ref() {
1626 self.scroll_offset = Some(scroll_handle.0.borrow().offset.clone());
1627 } else if (self.base_style.overflow.x == Some(Overflow::Scroll)
1628 || self.base_style.overflow.y == Some(Overflow::Scroll))
1629 && let Some(element_state) = element_state.as_mut()
1630 {
1631 self.scroll_offset = Some(
1632 element_state
1633 .scroll_offset
1634 .get_or_insert_with(Rc::default)
1635 .clone(),
1636 );
1637 }
1638
1639 let style = self.compute_style_internal(None, element_state.as_mut(), window, cx);
1640 let layout_id = f(style, window, cx);
1641 (layout_id, element_state)
1642 },
1643 )
1644 }
1645
1646 pub fn prepaint<R>(
1648 &mut self,
1649 global_id: Option<&GlobalElementId>,
1650 _inspector_id: Option<&InspectorElementId>,
1651 bounds: Bounds<Pixels>,
1652 content_size: Size<Pixels>,
1653 window: &mut Window,
1654 cx: &mut App,
1655 f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut Window, &mut App) -> R,
1656 ) -> R {
1657 self.content_size = content_size;
1658
1659 #[cfg(any(feature = "inspector", debug_assertions))]
1660 window.with_inspector_state(
1661 _inspector_id,
1662 cx,
1663 |inspector_state: &mut Option<DivInspectorState>, _window| {
1664 if let Some(inspector_state) = inspector_state {
1665 inspector_state.bounds = bounds;
1666 inspector_state.content_size = content_size;
1667 }
1668 },
1669 );
1670
1671 if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
1672 window.set_focus_handle(focus_handle, cx);
1673 }
1674 window.with_optional_element_state::<InteractiveElementState, _>(
1675 global_id,
1676 |element_state, window| {
1677 let mut element_state =
1678 element_state.map(|element_state| element_state.unwrap_or_default());
1679 let style = self.compute_style_internal(None, element_state.as_mut(), window, cx);
1680
1681 if let Some(element_state) = element_state.as_mut() {
1682 if let Some(clicked_state) = element_state.clicked_state.as_ref() {
1683 let clicked_state = clicked_state.borrow();
1684 self.active = Some(clicked_state.element);
1685 }
1686 if let Some(active_tooltip) = element_state.active_tooltip.as_ref() {
1687 if self.tooltip_builder.is_some() {
1688 self.tooltip_id = set_tooltip_on_window(active_tooltip, window);
1689 } else {
1690 element_state.active_tooltip.take();
1692 }
1693 }
1694 }
1695
1696 window.with_text_style(style.text_style().cloned(), |window| {
1697 window.with_content_mask(
1698 style.overflow_mask(bounds, window.rem_size()),
1699 |window| {
1700 let hitbox = if self.should_insert_hitbox(&style, window, cx) {
1701 Some(window.insert_hitbox(bounds, self.hitbox_behavior))
1702 } else {
1703 None
1704 };
1705
1706 let scroll_offset =
1707 self.clamp_scroll_position(bounds, &style, window, cx);
1708 let result = f(&style, scroll_offset, hitbox, window, cx);
1709 (result, element_state)
1710 },
1711 )
1712 })
1713 },
1714 )
1715 }
1716
1717 fn should_insert_hitbox(&self, style: &Style, window: &Window, cx: &App) -> bool {
1718 self.hitbox_behavior != HitboxBehavior::Normal
1719 || self.window_control.is_some()
1720 || style.mouse_cursor.is_some()
1721 || self.group.is_some()
1722 || self.scroll_offset.is_some()
1723 || self.tracked_focus_handle.is_some()
1724 || self.hover_style.is_some()
1725 || self.group_hover_style.is_some()
1726 || self.hover_listener.is_some()
1727 || !self.mouse_up_listeners.is_empty()
1728 || !self.mouse_down_listeners.is_empty()
1729 || !self.mouse_move_listeners.is_empty()
1730 || !self.click_listeners.is_empty()
1731 || !self.scroll_wheel_listeners.is_empty()
1732 || self.drag_listener.is_some()
1733 || !self.drop_listeners.is_empty()
1734 || self.tooltip_builder.is_some()
1735 || window.is_inspector_picking(cx)
1736 }
1737
1738 fn clamp_scroll_position(
1739 &self,
1740 bounds: Bounds<Pixels>,
1741 style: &Style,
1742 window: &mut Window,
1743 _cx: &mut App,
1744 ) -> Point<Pixels> {
1745 fn round_to_two_decimals(pixels: Pixels) -> Pixels {
1746 const ROUNDING_FACTOR: f32 = 100.0;
1747 (pixels * ROUNDING_FACTOR).round() / ROUNDING_FACTOR
1748 }
1749
1750 if let Some(scroll_offset) = self.scroll_offset.as_ref() {
1751 let mut scroll_to_bottom = false;
1752 let mut tracked_scroll_handle = self
1753 .tracked_scroll_handle
1754 .as_ref()
1755 .map(|handle| handle.0.borrow_mut());
1756 if let Some(mut scroll_handle_state) = tracked_scroll_handle.as_deref_mut() {
1757 scroll_handle_state.overflow = style.overflow;
1758 scroll_to_bottom = mem::take(&mut scroll_handle_state.scroll_to_bottom);
1759 }
1760
1761 let rem_size = window.rem_size();
1762 let padding = style.padding.to_pixels(bounds.size.into(), rem_size);
1763 let padding_size = size(padding.left + padding.right, padding.top + padding.bottom);
1764 let padded_content_size = self.content_size + padding_size;
1771 let scroll_max = (padded_content_size - bounds.size)
1772 .map(round_to_two_decimals)
1773 .max(&Default::default());
1774 let mut scroll_offset = scroll_offset.borrow_mut();
1777
1778 scroll_offset.x = scroll_offset.x.clamp(-scroll_max.width, px(0.));
1779 if scroll_to_bottom {
1780 scroll_offset.y = -scroll_max.height;
1781 } else {
1782 scroll_offset.y = scroll_offset.y.clamp(-scroll_max.height, px(0.));
1783 }
1784
1785 if let Some(mut scroll_handle_state) = tracked_scroll_handle {
1786 scroll_handle_state.max_offset = scroll_max;
1787 scroll_handle_state.bounds = bounds;
1788 }
1789
1790 *scroll_offset
1791 } else {
1792 Point::default()
1793 }
1794 }
1795
1796 pub fn paint(
1805 &mut self,
1806 global_id: Option<&GlobalElementId>,
1807 _inspector_id: Option<&InspectorElementId>,
1808 bounds: Bounds<Pixels>,
1809 hitbox: Option<&Hitbox>,
1810 window: &mut Window,
1811 cx: &mut App,
1812 f: impl FnOnce(&Style, &mut Window, &mut App),
1813 ) {
1814 self.hovered = hitbox.map(|hitbox| hitbox.is_hovered(window));
1815 window.with_optional_element_state::<InteractiveElementState, _>(
1816 global_id,
1817 |element_state, window| {
1818 let mut element_state =
1819 element_state.map(|element_state| element_state.unwrap_or_default());
1820
1821 let style = self.compute_style_internal(hitbox, element_state.as_mut(), window, cx);
1822
1823 #[cfg(any(feature = "test-support", test))]
1824 if let Some(debug_selector) = &self.debug_selector {
1825 window
1826 .next_frame
1827 .debug_bounds
1828 .insert(debug_selector.clone(), bounds);
1829 }
1830
1831 self.paint_hover_group_handler(window, cx);
1832
1833 if !self.resize_listeners.is_empty() {
1834 if let Some(element_state) = element_state.as_mut() {
1835 let size_changed = element_state
1836 .prev_bounds
1837 .map(|prev| prev.size != bounds.size)
1838 .unwrap_or(true);
1839
1840 if size_changed {
1841 element_state.prev_bounds = Some(bounds);
1842 let resize_event = ResizeEvent {
1843 size: bounds.size,
1844 bounds,
1845 };
1846 for listener in &self.resize_listeners {
1847 listener(&resize_event, window, cx);
1848 }
1849 }
1850 }
1851 }
1852
1853 if style.visibility == Visibility::Hidden {
1854 return ((), element_state);
1855 }
1856
1857 let mut tab_group = None;
1858 if self.tab_group {
1859 tab_group = self.tab_index;
1860 }
1861 if let Some(focus_handle) = &self.tracked_focus_handle {
1862 window.next_frame.tab_stops.insert(focus_handle);
1863 }
1864
1865 window.with_element_opacity(style.opacity, |window| {
1866 style.paint(bounds, window, cx, |window: &mut Window, cx: &mut App| {
1867 window.with_text_style(style.text_style().cloned(), |window| {
1868 window.with_content_mask(
1869 style.overflow_mask(bounds, window.rem_size()),
1870 |window| {
1871 window.with_tab_group(tab_group, |window| {
1872 if let Some(hitbox) = hitbox {
1873 #[cfg(debug_assertions)]
1874 self.paint_debug_info(
1875 global_id, hitbox, &style, window, cx,
1876 );
1877
1878 if let Some(drag) = cx.active_drag.as_ref() {
1879 if let Some(mouse_cursor) = drag.cursor_style {
1880 window.set_window_cursor_style(mouse_cursor);
1881 }
1882 } else {
1883 if let Some(mouse_cursor) = style.mouse_cursor {
1884 window.set_cursor_style(mouse_cursor, hitbox);
1885 }
1886 }
1887
1888 if let Some(group) = self.group.clone() {
1889 GroupHitboxes::push(group, hitbox.id, cx);
1890 }
1891
1892 if let Some(area) = self.window_control {
1893 window.insert_window_control_hitbox(
1894 area,
1895 hitbox.clone(),
1896 );
1897 }
1898
1899 self.paint_mouse_listeners(
1900 hitbox,
1901 element_state.as_mut(),
1902 window,
1903 cx,
1904 );
1905 self.paint_scroll_listener(hitbox, &style, window, cx);
1906 }
1907
1908 self.paint_keyboard_listeners(window, cx);
1909 f(&style, window, cx);
1910
1911 if let Some(_hitbox) = hitbox {
1912 #[cfg(any(feature = "inspector", debug_assertions))]
1913 window.insert_inspector_hitbox(
1914 _hitbox.id,
1915 _inspector_id,
1916 cx,
1917 );
1918
1919 if let Some(group) = self.group.as_ref() {
1920 GroupHitboxes::pop(group, cx);
1921 }
1922 }
1923 })
1924 },
1925 );
1926 });
1927 });
1928 });
1929
1930 ((), element_state)
1931 },
1932 );
1933 }
1934
1935 #[cfg(debug_assertions)]
1936 fn paint_debug_info(
1937 &self,
1938 global_id: Option<&GlobalElementId>,
1939 hitbox: &Hitbox,
1940 style: &Style,
1941 window: &mut Window,
1942 cx: &mut App,
1943 ) {
1944 use crate::{BorderStyle, TextAlign};
1945
1946 if let Some(global_id) = global_id.as_ref().filter(|_| {
1947 (style.debug || style.debug_below || cx.has_global::<crate::DebugBelow>())
1948 && hitbox.is_hovered(window)
1949 }) {
1950 const FONT_SIZE: crate::Pixels = crate::Pixels(10.);
1951 let element_id = format!("{:?}", global_id);
1952 let str_len = element_id.len();
1953
1954 let render_debug_text = |window: &mut Window| {
1955 if let Some(text) = window
1956 .text_system()
1957 .shape_text(
1958 element_id.into(),
1959 FONT_SIZE,
1960 &[window.text_style().to_run(str_len)],
1961 None,
1962 None,
1963 )
1964 .ok()
1965 .and_then(|mut text| text.pop())
1966 {
1967 text.paint(hitbox.origin, FONT_SIZE, TextAlign::Left, None, window, cx)
1968 .ok();
1969
1970 let text_bounds = crate::Bounds {
1971 origin: hitbox.origin,
1972 size: text.size(FONT_SIZE),
1973 };
1974 if self.source_location.is_some()
1975 && text_bounds.contains(&window.mouse_position())
1976 && window.modifiers().secondary()
1977 {
1978 let secondary_held = window.modifiers().secondary();
1979 window.on_key_event({
1980 move |e: &crate::ModifiersChangedEvent, _phase, window, _cx| {
1981 if e.modifiers.secondary() != secondary_held
1982 && text_bounds.contains(&window.mouse_position())
1983 {
1984 window.refresh();
1985 }
1986 }
1987 });
1988
1989 let was_hovered = hitbox.is_hovered(window);
1990 let current_view = window.current_view();
1991 window.on_mouse_event({
1992 let hitbox = hitbox.clone();
1993 move |_: &MouseMoveEvent, phase, window, cx| {
1994 if phase == DispatchPhase::Capture {
1995 let hovered = hitbox.is_hovered(window);
1996 if hovered != was_hovered {
1997 cx.notify(current_view)
1998 }
1999 }
2000 }
2001 });
2002
2003 window.on_mouse_event({
2004 let hitbox = hitbox.clone();
2005 let location = self.source_location.unwrap();
2006 move |e: &crate::MouseDownEvent, phase, window, cx| {
2007 if text_bounds.contains(&e.position)
2008 && phase.capture()
2009 && hitbox.is_hovered(window)
2010 {
2011 cx.stop_propagation();
2012 let Ok(dir) = std::env::current_dir() else {
2013 return;
2014 };
2015
2016 eprintln!(
2017 "This element was created at:\n{}:{}:{}",
2018 dir.join(location.file()).to_string_lossy(),
2019 location.line(),
2020 location.column()
2021 );
2022 }
2023 }
2024 });
2025 window.paint_quad(crate::outline(
2026 crate::Bounds {
2027 origin: hitbox.origin
2028 + crate::point(crate::px(0.), FONT_SIZE - px(2.)),
2029 size: crate::Size {
2030 width: text_bounds.size.width,
2031 height: crate::px(1.),
2032 },
2033 },
2034 crate::red(),
2035 BorderStyle::default(),
2036 ))
2037 }
2038 }
2039 };
2040
2041 window.with_text_style(
2042 Some(crate::TextStyleRefinement {
2043 color: Some(crate::red()),
2044 line_height: Some(FONT_SIZE.into()),
2045 background_color: Some(crate::white()),
2046 ..Default::default()
2047 }),
2048 render_debug_text,
2049 )
2050 }
2051 }
2052
2053 fn paint_mouse_listeners(
2054 &mut self,
2055 hitbox: &Hitbox,
2056 element_state: Option<&mut InteractiveElementState>,
2057 window: &mut Window,
2058 cx: &mut App,
2059 ) {
2060 let is_focused = self
2061 .tracked_focus_handle
2062 .as_ref()
2063 .map(|handle| handle.is_focused(window))
2064 .unwrap_or(false);
2065
2066 if let Some(focus_handle) = self.tracked_focus_handle.clone() {
2070 let hitbox = hitbox.clone();
2071 window.on_mouse_event(move |_: &MouseDownEvent, phase, window, _| {
2072 if phase == DispatchPhase::Bubble
2073 && hitbox.is_hovered(window)
2074 && !window.default_prevented()
2075 {
2076 window.focus(&focus_handle);
2077 window.prevent_default();
2080 }
2081 });
2082 }
2083
2084 for listener in self.mouse_down_listeners.drain(..) {
2085 let hitbox = hitbox.clone();
2086 window.on_mouse_event(move |event: &MouseDownEvent, phase, window, cx| {
2087 listener(event, phase, &hitbox, window, cx);
2088 })
2089 }
2090
2091 for listener in self.mouse_up_listeners.drain(..) {
2092 let hitbox = hitbox.clone();
2093 window.on_mouse_event(move |event: &MouseUpEvent, phase, window, cx| {
2094 listener(event, phase, &hitbox, window, cx);
2095 })
2096 }
2097
2098 for listener in self.mouse_move_listeners.drain(..) {
2099 let hitbox = hitbox.clone();
2100 window.on_mouse_event(move |event: &MouseMoveEvent, phase, window, cx| {
2101 listener(event, phase, &hitbox, window, cx);
2102 })
2103 }
2104
2105 for listener in self.scroll_wheel_listeners.drain(..) {
2106 let hitbox = hitbox.clone();
2107 window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
2108 listener(event, phase, &hitbox, window, cx);
2109 })
2110 }
2111
2112 if self.hover_style.is_some()
2113 || self.base_style.mouse_cursor.is_some()
2114 || cx.active_drag.is_some() && !self.drag_over_styles.is_empty()
2115 {
2116 let hitbox = hitbox.clone();
2117 let was_hovered = hitbox.is_hovered(window);
2118 let current_view = window.current_view();
2119 window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
2120 let hovered = hitbox.is_hovered(window);
2121 if phase == DispatchPhase::Capture && hovered != was_hovered {
2122 cx.notify(current_view);
2123 }
2124 });
2125 }
2126 let drag_cursor_style = self.base_style.as_ref().mouse_cursor;
2127
2128 let mut drag_listener = mem::take(&mut self.drag_listener);
2129 let drop_listeners = mem::take(&mut self.drop_listeners);
2130 let click_listeners = mem::take(&mut self.click_listeners);
2131 let can_drop_predicate = mem::take(&mut self.can_drop_predicate);
2132
2133 if !drop_listeners.is_empty() {
2134 let hitbox = hitbox.clone();
2135 window.on_mouse_event({
2136 move |_: &MouseUpEvent, phase, window, cx| {
2137 if let Some(drag) = &cx.active_drag
2138 && phase == DispatchPhase::Bubble
2139 && hitbox.is_hovered(window)
2140 {
2141 let drag_state_type = drag.value.as_ref().type_id();
2142 for (drop_state_type, listener) in &drop_listeners {
2143 if *drop_state_type == drag_state_type {
2144 let drag = cx
2145 .active_drag
2146 .take()
2147 .expect("checked for type drag state type above");
2148
2149 let mut can_drop = true;
2150 if let Some(predicate) = &can_drop_predicate {
2151 can_drop = predicate(drag.value.as_ref(), window, cx);
2152 }
2153
2154 if can_drop {
2155 listener(drag.value.as_ref(), window, cx);
2156 window.refresh();
2157 cx.stop_propagation();
2158 }
2159 }
2160 }
2161 }
2162 }
2163 });
2164 }
2165
2166 if let Some(element_state) = element_state {
2167 if !click_listeners.is_empty() || drag_listener.is_some() {
2168 let pending_mouse_down = element_state
2169 .pending_mouse_down
2170 .get_or_insert_with(Default::default)
2171 .clone();
2172
2173 let clicked_state = element_state
2174 .clicked_state
2175 .get_or_insert_with(Default::default)
2176 .clone();
2177
2178 window.on_mouse_event({
2179 let pending_mouse_down = pending_mouse_down.clone();
2180 let hitbox = hitbox.clone();
2181 move |event: &MouseDownEvent, phase, window, _cx| {
2182 if phase == DispatchPhase::Bubble
2183 && event.button == MouseButton::Left
2184 && hitbox.is_hovered(window)
2185 {
2186 *pending_mouse_down.borrow_mut() = Some(event.clone());
2187 window.refresh();
2188 }
2189 }
2190 });
2191
2192 window.on_mouse_event({
2193 let pending_mouse_down = pending_mouse_down.clone();
2194 let hitbox = hitbox.clone();
2195 move |event: &MouseMoveEvent, phase, window, cx| {
2196 if phase == DispatchPhase::Capture {
2197 return;
2198 }
2199
2200 let mut pending_mouse_down = pending_mouse_down.borrow_mut();
2201 if let Some(mouse_down) = pending_mouse_down.clone()
2202 && !cx.has_active_drag()
2203 && (event.position - mouse_down.position).magnitude() > DRAG_THRESHOLD
2204 && let Some((drag_value, drag_listener)) = drag_listener.take()
2205 {
2206 *clicked_state.borrow_mut() = ElementClickedState::default();
2207 let cursor_offset = event.position - hitbox.origin;
2208 let drag =
2209 (drag_listener)(drag_value.as_ref(), cursor_offset, window, cx);
2210 cx.active_drag = Some(AnyDrag {
2211 view: drag,
2212 value: drag_value,
2213 cursor_offset,
2214 cursor_style: drag_cursor_style,
2215 });
2216 pending_mouse_down.take();
2217 window.refresh();
2218 cx.stop_propagation();
2219 }
2220 }
2221 });
2222
2223 if is_focused {
2224 window.on_key_event({
2226 let click_listeners = click_listeners.clone();
2227 let hitbox = hitbox.clone();
2228 move |event: &KeyUpEvent, phase, window, cx| {
2229 if phase.bubble() && !window.default_prevented() {
2230 let stroke = &event.keystroke;
2231 let keyboard_button = if stroke.key.eq("enter") {
2232 Some(KeyboardButton::Enter)
2233 } else if stroke.key.eq("space") {
2234 Some(KeyboardButton::Space)
2235 } else {
2236 None
2237 };
2238
2239 if let Some(button) = keyboard_button
2240 && !stroke.modifiers.modified()
2241 {
2242 let click_event = ClickEvent::Keyboard(KeyboardClickEvent {
2243 button,
2244 bounds: hitbox.bounds,
2245 });
2246
2247 for listener in &click_listeners {
2248 listener(&click_event, window, cx);
2249 }
2250 }
2251 }
2252 }
2253 });
2254 }
2255
2256 window.on_mouse_event({
2257 let mut captured_mouse_down = None;
2258 let hitbox = hitbox.clone();
2259 move |event: &MouseUpEvent, phase, window, cx| match phase {
2260 DispatchPhase::Capture => {
2264 let mut pending_mouse_down = pending_mouse_down.borrow_mut();
2265 if pending_mouse_down.is_some() && hitbox.is_hovered(window) {
2266 captured_mouse_down = pending_mouse_down.take();
2267 window.refresh();
2268 } else if pending_mouse_down.is_some() {
2269 pending_mouse_down.take();
2275 window.refresh();
2276 }
2277 }
2278 DispatchPhase::Bubble => {
2280 if let Some(mouse_down) = captured_mouse_down.take() {
2281 let mouse_click = ClickEvent::Mouse(MouseClickEvent {
2282 down: mouse_down,
2283 up: event.clone(),
2284 });
2285 for listener in &click_listeners {
2286 listener(&mouse_click, window, cx);
2287 }
2288 }
2289 }
2290 }
2291 });
2292 }
2293
2294 if let Some(hover_listener) = self.hover_listener.take() {
2295 let hitbox = hitbox.clone();
2296 let was_hovered = element_state
2297 .hover_state
2298 .get_or_insert_with(Default::default)
2299 .clone();
2300 let has_mouse_down = element_state
2301 .pending_mouse_down
2302 .get_or_insert_with(Default::default)
2303 .clone();
2304
2305 window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
2306 if phase != DispatchPhase::Bubble {
2307 return;
2308 }
2309 let is_hovered = has_mouse_down.borrow().is_none()
2310 && !cx.has_active_drag()
2311 && hitbox.is_hovered(window);
2312 let mut was_hovered = was_hovered.borrow_mut();
2313
2314 if is_hovered != *was_hovered {
2315 *was_hovered = is_hovered;
2316 drop(was_hovered);
2317
2318 hover_listener(&is_hovered, window, cx);
2319 }
2320 });
2321 }
2322
2323 if let Some(tooltip_builder) = self.tooltip_builder.take() {
2324 let active_tooltip = element_state
2325 .active_tooltip
2326 .get_or_insert_with(Default::default)
2327 .clone();
2328 let pending_mouse_down = element_state
2329 .pending_mouse_down
2330 .get_or_insert_with(Default::default)
2331 .clone();
2332
2333 let tooltip_is_hoverable = tooltip_builder.hoverable;
2334 let build_tooltip = Rc::new(move |window: &mut Window, cx: &mut App| {
2335 Some(((tooltip_builder.build)(window, cx), tooltip_is_hoverable))
2336 });
2337 let check_is_hovered_during_prepaint = Rc::new({
2339 let pending_mouse_down = pending_mouse_down.clone();
2340 let source_bounds = hitbox.bounds;
2341 move |window: &Window| {
2342 pending_mouse_down.borrow().is_none()
2343 && source_bounds.contains(&window.mouse_position())
2344 }
2345 });
2346 let check_is_hovered = Rc::new({
2347 let hitbox = hitbox.clone();
2348 move |window: &Window| {
2349 pending_mouse_down.borrow().is_none() && hitbox.is_hovered(window)
2350 }
2351 });
2352 register_tooltip_mouse_handlers(
2353 &active_tooltip,
2354 self.tooltip_id,
2355 build_tooltip,
2356 check_is_hovered,
2357 check_is_hovered_during_prepaint,
2358 window,
2359 );
2360 }
2361
2362 let active_state = element_state
2363 .clicked_state
2364 .get_or_insert_with(Default::default)
2365 .clone();
2366 if active_state.borrow().is_clicked() {
2367 window.on_mouse_event(move |_: &MouseUpEvent, phase, window, _cx| {
2368 if phase == DispatchPhase::Capture {
2369 *active_state.borrow_mut() = ElementClickedState::default();
2370 window.refresh();
2371 }
2372 });
2373 } else {
2374 let active_group_hitbox = self
2375 .group_active_style
2376 .as_ref()
2377 .and_then(|group_active| GroupHitboxes::get(&group_active.group, cx));
2378 let hitbox = hitbox.clone();
2379 window.on_mouse_event(move |_: &MouseDownEvent, phase, window, _cx| {
2380 if phase == DispatchPhase::Bubble && !window.default_prevented() {
2381 let group_hovered = active_group_hitbox
2382 .is_some_and(|group_hitbox_id| group_hitbox_id.is_hovered(window));
2383 let element_hovered = hitbox.is_hovered(window);
2384 if group_hovered || element_hovered {
2385 *active_state.borrow_mut() = ElementClickedState {
2386 group: group_hovered,
2387 element: element_hovered,
2388 };
2389 window.refresh();
2390 }
2391 }
2392 });
2393 }
2394 }
2395 }
2396
2397 fn paint_keyboard_listeners(&mut self, window: &mut Window, _cx: &mut App) {
2398 let key_down_listeners = mem::take(&mut self.key_down_listeners);
2399 let key_up_listeners = mem::take(&mut self.key_up_listeners);
2400 let modifiers_changed_listeners = mem::take(&mut self.modifiers_changed_listeners);
2401 let action_listeners = mem::take(&mut self.action_listeners);
2402 if let Some(context) = self.key_context.clone() {
2403 window.set_key_context(context);
2404 }
2405
2406 for listener in key_down_listeners {
2407 window.on_key_event(move |event: &KeyDownEvent, phase, window, cx| {
2408 listener(event, phase, window, cx);
2409 })
2410 }
2411
2412 for listener in key_up_listeners {
2413 window.on_key_event(move |event: &KeyUpEvent, phase, window, cx| {
2414 listener(event, phase, window, cx);
2415 })
2416 }
2417
2418 for listener in modifiers_changed_listeners {
2419 window.on_modifiers_changed(move |event: &ModifiersChangedEvent, window, cx| {
2420 listener(event, window, cx);
2421 })
2422 }
2423
2424 for (action_type, listener) in action_listeners {
2425 window.on_action(action_type, listener)
2426 }
2427 }
2428
2429 fn paint_hover_group_handler(&self, window: &mut Window, cx: &mut App) {
2430 let group_hitbox = self
2431 .group_hover_style
2432 .as_ref()
2433 .and_then(|group_hover| GroupHitboxes::get(&group_hover.group, cx));
2434
2435 if let Some(group_hitbox) = group_hitbox {
2436 let was_hovered = group_hitbox.is_hovered(window);
2437 let current_view = window.current_view();
2438 window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
2439 let hovered = group_hitbox.is_hovered(window);
2440 if phase == DispatchPhase::Capture && hovered != was_hovered {
2441 cx.notify(current_view);
2442 }
2443 });
2444 }
2445 }
2446
2447 fn paint_scroll_listener(
2448 &self,
2449 hitbox: &Hitbox,
2450 style: &Style,
2451 window: &mut Window,
2452 _cx: &mut App,
2453 ) {
2454 if let Some(scroll_offset) = self.scroll_offset.clone() {
2455 let overflow = style.overflow;
2456 let allow_concurrent_scroll = style.allow_concurrent_scroll;
2457 let restrict_scroll_to_axis = style.restrict_scroll_to_axis;
2458 let line_height = window.line_height();
2459 let hitbox = hitbox.clone();
2460 let current_view = window.current_view();
2461 window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
2462 if phase == DispatchPhase::Bubble && hitbox.should_handle_scroll(window) {
2463 let mut scroll_offset = scroll_offset.borrow_mut();
2464 let old_scroll_offset = *scroll_offset;
2465 let delta = event.delta.pixel_delta(line_height);
2466
2467 let mut delta_x = Pixels::ZERO;
2468 if overflow.x == Overflow::Scroll {
2469 if !delta.x.is_zero() {
2470 delta_x = delta.x;
2471 } else if !restrict_scroll_to_axis && overflow.y != Overflow::Scroll {
2472 delta_x = delta.y;
2473 }
2474 }
2475 let mut delta_y = Pixels::ZERO;
2476 if overflow.y == Overflow::Scroll {
2477 if !delta.y.is_zero() {
2478 delta_y = delta.y;
2479 } else if !restrict_scroll_to_axis && overflow.x != Overflow::Scroll {
2480 delta_y = delta.x;
2481 }
2482 }
2483 if !allow_concurrent_scroll && !delta_x.is_zero() && !delta_y.is_zero() {
2484 if delta_x.abs() > delta_y.abs() {
2485 delta_y = Pixels::ZERO;
2486 } else {
2487 delta_x = Pixels::ZERO;
2488 }
2489 }
2490 scroll_offset.y += delta_y;
2491 scroll_offset.x += delta_x;
2492 if *scroll_offset != old_scroll_offset {
2493 cx.notify(current_view);
2494 }
2495 }
2496 });
2497 }
2498 }
2499
2500 pub fn compute_style(
2502 &self,
2503 global_id: Option<&GlobalElementId>,
2504 hitbox: Option<&Hitbox>,
2505 window: &mut Window,
2506 cx: &mut App,
2507 ) -> Style {
2508 window.with_optional_element_state(global_id, |element_state, window| {
2509 let mut element_state =
2510 element_state.map(|element_state| element_state.unwrap_or_default());
2511 let style = self.compute_style_internal(hitbox, element_state.as_mut(), window, cx);
2512 (style, element_state)
2513 })
2514 }
2515
2516 fn compute_style_internal(
2518 &self,
2519 hitbox: Option<&Hitbox>,
2520 element_state: Option<&mut InteractiveElementState>,
2521 window: &mut Window,
2522 cx: &mut App,
2523 ) -> Style {
2524 let mut style = Style::default();
2525 style.refine(&self.base_style);
2526
2527 if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
2528 if let Some(in_focus_style) = self.in_focus_style.as_ref()
2529 && focus_handle.within_focused(window, cx)
2530 {
2531 style.refine(in_focus_style);
2532 }
2533
2534 if let Some(focus_style) = self.focus_style.as_ref()
2535 && focus_handle.is_focused(window)
2536 {
2537 style.refine(focus_style);
2538 }
2539 }
2540
2541 if let Some(hitbox) = hitbox {
2542 if !cx.has_active_drag() {
2543 if let Some(group_hover) = self.group_hover_style.as_ref()
2544 && let Some(group_hitbox_id) = GroupHitboxes::get(&group_hover.group, cx)
2545 && group_hitbox_id.is_hovered(window)
2546 {
2547 style.refine(&group_hover.style);
2548 }
2549
2550 if let Some(hover_style) = self.hover_style.as_ref()
2551 && hitbox.is_hovered(window)
2552 {
2553 style.refine(hover_style);
2554 }
2555 }
2556
2557 if let Some(drag) = cx.active_drag.take() {
2558 let mut can_drop = true;
2559 if let Some(can_drop_predicate) = &self.can_drop_predicate {
2560 can_drop = can_drop_predicate(drag.value.as_ref(), window, cx);
2561 }
2562
2563 if can_drop {
2564 for (state_type, group_drag_style) in &self.group_drag_over_styles {
2565 if let Some(group_hitbox_id) =
2566 GroupHitboxes::get(&group_drag_style.group, cx)
2567 && *state_type == drag.value.as_ref().type_id()
2568 && group_hitbox_id.is_hovered(window)
2569 {
2570 style.refine(&group_drag_style.style);
2571 }
2572 }
2573
2574 for (state_type, build_drag_over_style) in &self.drag_over_styles {
2575 if *state_type == drag.value.as_ref().type_id() && hitbox.is_hovered(window)
2576 {
2577 style.refine(&build_drag_over_style(drag.value.as_ref(), window, cx));
2578 }
2579 }
2580 }
2581
2582 style.mouse_cursor = drag.cursor_style;
2583 cx.active_drag = Some(drag);
2584 }
2585 }
2586
2587 if let Some(element_state) = element_state {
2588 let clicked_state = element_state
2589 .clicked_state
2590 .get_or_insert_with(Default::default)
2591 .borrow();
2592 if clicked_state.group
2593 && let Some(group) = self.group_active_style.as_ref()
2594 {
2595 style.refine(&group.style)
2596 }
2597
2598 if let Some(active_style) = self.active_style.as_ref()
2599 && clicked_state.element
2600 {
2601 style.refine(active_style)
2602 }
2603 }
2604
2605 style
2606 }
2607}
2608
2609#[derive(Default)]
2612pub struct InteractiveElementState {
2613 pub(crate) focus_handle: Option<FocusHandle>,
2614 pub(crate) clicked_state: Option<Rc<RefCell<ElementClickedState>>>,
2615 pub(crate) hover_state: Option<Rc<RefCell<bool>>>,
2616 pub(crate) pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
2617 pub(crate) scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
2618 pub(crate) active_tooltip: Option<Rc<RefCell<Option<ActiveTooltip>>>>,
2619 pub(crate) prev_bounds: Option<Bounds<Pixels>>,
2620}
2621
2622#[derive(Copy, Clone, Default, Eq, PartialEq)]
2624pub struct ElementClickedState {
2625 pub group: bool,
2627
2628 pub element: bool,
2630}
2631
2632impl ElementClickedState {
2633 fn is_clicked(&self) -> bool {
2634 self.group || self.element
2635 }
2636}
2637
2638pub(crate) enum ActiveTooltip {
2639 WaitingForShow { _task: Task<()> },
2641 Visible {
2643 tooltip: AnyTooltip,
2644 is_hoverable: bool,
2645 },
2646 WaitingForHide {
2649 tooltip: AnyTooltip,
2650 _task: Task<()>,
2651 },
2652}
2653
2654pub(crate) fn clear_active_tooltip(
2655 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2656 window: &mut Window,
2657) {
2658 match active_tooltip.borrow_mut().take() {
2659 None => {}
2660 Some(ActiveTooltip::WaitingForShow { .. }) => {}
2661 Some(ActiveTooltip::Visible { .. }) => window.refresh(),
2662 Some(ActiveTooltip::WaitingForHide { .. }) => window.refresh(),
2663 }
2664}
2665
2666pub(crate) fn clear_active_tooltip_if_not_hoverable(
2667 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2668 window: &mut Window,
2669) {
2670 let should_clear = match active_tooltip.borrow().as_ref() {
2671 None => false,
2672 Some(ActiveTooltip::WaitingForShow { .. }) => false,
2673 Some(ActiveTooltip::Visible { is_hoverable, .. }) => !is_hoverable,
2674 Some(ActiveTooltip::WaitingForHide { .. }) => false,
2675 };
2676 if should_clear {
2677 active_tooltip.borrow_mut().take();
2678 window.refresh();
2679 }
2680}
2681
2682pub(crate) fn set_tooltip_on_window(
2683 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2684 window: &mut Window,
2685) -> Option<TooltipId> {
2686 let tooltip = match active_tooltip.borrow().as_ref() {
2687 None => return None,
2688 Some(ActiveTooltip::WaitingForShow { .. }) => return None,
2689 Some(ActiveTooltip::Visible { tooltip, .. }) => tooltip.clone(),
2690 Some(ActiveTooltip::WaitingForHide { tooltip, .. }) => tooltip.clone(),
2691 };
2692 Some(window.set_tooltip(tooltip))
2693}
2694
2695pub(crate) fn register_tooltip_mouse_handlers(
2696 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2697 tooltip_id: Option<TooltipId>,
2698 build_tooltip: Rc<dyn Fn(&mut Window, &mut App) -> Option<(AnyView, bool)>>,
2699 check_is_hovered: Rc<dyn Fn(&Window) -> bool>,
2700 check_is_hovered_during_prepaint: Rc<dyn Fn(&Window) -> bool>,
2701 window: &mut Window,
2702) {
2703 window.on_mouse_event({
2704 let active_tooltip = active_tooltip.clone();
2705 let build_tooltip = build_tooltip.clone();
2706 let check_is_hovered = check_is_hovered.clone();
2707 move |_: &MouseMoveEvent, phase, window, cx| {
2708 handle_tooltip_mouse_move(
2709 &active_tooltip,
2710 &build_tooltip,
2711 &check_is_hovered,
2712 &check_is_hovered_during_prepaint,
2713 phase,
2714 window,
2715 cx,
2716 )
2717 }
2718 });
2719
2720 window.on_mouse_event({
2721 let active_tooltip = active_tooltip.clone();
2722 move |_: &MouseDownEvent, _phase, window: &mut Window, _cx| {
2723 if !tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window)) {
2724 clear_active_tooltip_if_not_hoverable(&active_tooltip, window);
2725 }
2726 }
2727 });
2728
2729 window.on_mouse_event({
2730 let active_tooltip = active_tooltip.clone();
2731 move |_: &ScrollWheelEvent, _phase, window: &mut Window, _cx| {
2732 if !tooltip_id.is_some_and(|tooltip_id| tooltip_id.is_hovered(window)) {
2733 clear_active_tooltip_if_not_hoverable(&active_tooltip, window);
2734 }
2735 }
2736 });
2737}
2738
2739fn handle_tooltip_mouse_move(
2751 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2752 build_tooltip: &Rc<dyn Fn(&mut Window, &mut App) -> Option<(AnyView, bool)>>,
2753 check_is_hovered: &Rc<dyn Fn(&Window) -> bool>,
2754 check_is_hovered_during_prepaint: &Rc<dyn Fn(&Window) -> bool>,
2755 phase: DispatchPhase,
2756 window: &mut Window,
2757 cx: &mut App,
2758) {
2759 enum Action {
2762 None,
2763 CancelShow,
2764 ScheduleShow,
2765 }
2766
2767 let action = match active_tooltip.borrow().as_ref() {
2768 None => {
2769 let is_hovered = check_is_hovered(window);
2770 if is_hovered && phase.bubble() {
2771 Action::ScheduleShow
2772 } else {
2773 Action::None
2774 }
2775 }
2776 Some(ActiveTooltip::WaitingForShow { .. }) => {
2777 let is_hovered = check_is_hovered(window);
2778 if is_hovered {
2779 Action::None
2780 } else {
2781 Action::CancelShow
2782 }
2783 }
2784 Some(ActiveTooltip::Visible { .. }) | Some(ActiveTooltip::WaitingForHide { .. }) => {
2786 Action::None
2787 }
2788 };
2789
2790 match action {
2791 Action::None => {}
2792 Action::CancelShow => {
2793 active_tooltip.borrow_mut().take();
2795 }
2796 Action::ScheduleShow => {
2797 let delayed_show_task = window.spawn(cx, {
2798 let active_tooltip = active_tooltip.clone();
2799 let build_tooltip = build_tooltip.clone();
2800 let check_is_hovered_during_prepaint = check_is_hovered_during_prepaint.clone();
2801 async move |cx| {
2802 cx.background_executor().timer(TOOLTIP_SHOW_DELAY).await;
2803 cx.update(|window, cx| {
2804 let new_tooltip =
2805 build_tooltip(window, cx).map(|(view, tooltip_is_hoverable)| {
2806 let active_tooltip = active_tooltip.clone();
2807 ActiveTooltip::Visible {
2808 tooltip: AnyTooltip {
2809 view,
2810 mouse_position: window.mouse_position(),
2811 check_visible_and_update: Rc::new(
2812 move |tooltip_bounds, window, cx| {
2813 handle_tooltip_check_visible_and_update(
2814 &active_tooltip,
2815 tooltip_is_hoverable,
2816 &check_is_hovered_during_prepaint,
2817 tooltip_bounds,
2818 window,
2819 cx,
2820 )
2821 },
2822 ),
2823 },
2824 is_hoverable: tooltip_is_hoverable,
2825 }
2826 });
2827 *active_tooltip.borrow_mut() = new_tooltip;
2828 window.refresh();
2829 })
2830 .ok();
2831 }
2832 });
2833 active_tooltip
2834 .borrow_mut()
2835 .replace(ActiveTooltip::WaitingForShow {
2836 _task: delayed_show_task,
2837 });
2838 }
2839 }
2840}
2841
2842fn handle_tooltip_check_visible_and_update(
2846 active_tooltip: &Rc<RefCell<Option<ActiveTooltip>>>,
2847 tooltip_is_hoverable: bool,
2848 check_is_hovered: &Rc<dyn Fn(&Window) -> bool>,
2849 tooltip_bounds: Bounds<Pixels>,
2850 window: &mut Window,
2851 cx: &mut App,
2852) -> bool {
2853 enum Action {
2856 None,
2857 Hide,
2858 ScheduleHide(AnyTooltip),
2859 CancelHide(AnyTooltip),
2860 }
2861
2862 let is_hovered = check_is_hovered(window)
2863 || (tooltip_is_hoverable && tooltip_bounds.contains(&window.mouse_position()));
2864 let action = match active_tooltip.borrow().as_ref() {
2865 Some(ActiveTooltip::Visible { tooltip, .. }) => {
2866 if is_hovered {
2867 Action::None
2868 } else {
2869 if tooltip_is_hoverable {
2870 Action::ScheduleHide(tooltip.clone())
2871 } else {
2872 Action::Hide
2873 }
2874 }
2875 }
2876 Some(ActiveTooltip::WaitingForHide { tooltip, .. }) => {
2877 if is_hovered {
2878 Action::CancelHide(tooltip.clone())
2879 } else {
2880 Action::None
2881 }
2882 }
2883 None | Some(ActiveTooltip::WaitingForShow { .. }) => Action::None,
2884 };
2885
2886 match action {
2887 Action::None => {}
2888 Action::Hide => clear_active_tooltip(active_tooltip, window),
2889 Action::ScheduleHide(tooltip) => {
2890 let delayed_hide_task = window.spawn(cx, {
2891 let active_tooltip = active_tooltip.clone();
2892 async move |cx| {
2893 cx.background_executor()
2894 .timer(HOVERABLE_TOOLTIP_HIDE_DELAY)
2895 .await;
2896 if active_tooltip.borrow_mut().take().is_some() {
2897 cx.update(|window, _cx| window.refresh()).ok();
2898 }
2899 }
2900 });
2901 active_tooltip
2902 .borrow_mut()
2903 .replace(ActiveTooltip::WaitingForHide {
2904 tooltip,
2905 _task: delayed_hide_task,
2906 });
2907 }
2908 Action::CancelHide(tooltip) => {
2909 active_tooltip.borrow_mut().replace(ActiveTooltip::Visible {
2911 tooltip,
2912 is_hoverable: true,
2913 });
2914 }
2915 }
2916
2917 active_tooltip.borrow().is_some()
2918}
2919
2920#[derive(Default)]
2921pub(crate) struct GroupHitboxes(HashMap<SharedString, SmallVec<[HitboxId; 1]>>);
2922
2923impl Global for GroupHitboxes {}
2924
2925impl GroupHitboxes {
2926 pub fn get(name: &SharedString, cx: &mut App) -> Option<HitboxId> {
2927 cx.default_global::<Self>()
2928 .0
2929 .get(name)
2930 .and_then(|bounds_stack| bounds_stack.last())
2931 .cloned()
2932 }
2933
2934 pub fn push(name: SharedString, hitbox_id: HitboxId, cx: &mut App) {
2935 cx.default_global::<Self>()
2936 .0
2937 .entry(name)
2938 .or_default()
2939 .push(hitbox_id);
2940 }
2941
2942 pub fn pop(name: &SharedString, cx: &mut App) {
2943 cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
2944 }
2945}
2946
2947pub struct Stateful<E> {
2949 pub(crate) element: E,
2950}
2951
2952impl<E> Styled for Stateful<E>
2953where
2954 E: Styled,
2955{
2956 fn style(&mut self) -> &mut StyleRefinement {
2957 self.element.style()
2958 }
2959}
2960
2961impl<E> StatefulInteractiveElement for Stateful<E>
2962where
2963 E: Element,
2964 Self: InteractiveElement,
2965{
2966}
2967
2968impl<E> InteractiveElement for Stateful<E>
2969where
2970 E: InteractiveElement,
2971{
2972 fn interactivity(&mut self) -> &mut Interactivity {
2973 self.element.interactivity()
2974 }
2975}
2976
2977impl<E> Element for Stateful<E>
2978where
2979 E: Element,
2980{
2981 type RequestLayoutState = E::RequestLayoutState;
2982 type PrepaintState = E::PrepaintState;
2983
2984 fn id(&self) -> Option<ElementId> {
2985 self.element.id()
2986 }
2987
2988 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
2989 self.element.source_location()
2990 }
2991
2992 fn request_layout(
2993 &mut self,
2994 id: Option<&GlobalElementId>,
2995 inspector_id: Option<&InspectorElementId>,
2996 window: &mut Window,
2997 cx: &mut App,
2998 ) -> (LayoutId, Self::RequestLayoutState) {
2999 self.element.request_layout(id, inspector_id, window, cx)
3000 }
3001
3002 fn prepaint(
3003 &mut self,
3004 id: Option<&GlobalElementId>,
3005 inspector_id: Option<&InspectorElementId>,
3006 bounds: Bounds<Pixels>,
3007 state: &mut Self::RequestLayoutState,
3008 window: &mut Window,
3009 cx: &mut App,
3010 ) -> E::PrepaintState {
3011 self.element
3012 .prepaint(id, inspector_id, bounds, state, window, cx)
3013 }
3014
3015 fn paint(
3016 &mut self,
3017 id: Option<&GlobalElementId>,
3018 inspector_id: Option<&InspectorElementId>,
3019 bounds: Bounds<Pixels>,
3020 request_layout: &mut Self::RequestLayoutState,
3021 prepaint: &mut Self::PrepaintState,
3022 window: &mut Window,
3023 cx: &mut App,
3024 ) {
3025 self.element.paint(
3026 id,
3027 inspector_id,
3028 bounds,
3029 request_layout,
3030 prepaint,
3031 window,
3032 cx,
3033 );
3034 }
3035}
3036
3037impl<E> IntoElement for Stateful<E>
3038where
3039 E: Element,
3040{
3041 type Element = Self;
3042
3043 fn into_element(self) -> Self::Element {
3044 self
3045 }
3046}
3047
3048impl<E> ParentElement for Stateful<E>
3049where
3050 E: ParentElement,
3051{
3052 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
3053 self.element.extend(elements)
3054 }
3055}
3056
3057#[derive(Clone)]
3060pub struct ScrollAnchor {
3061 handle: ScrollHandle,
3062 last_origin: Rc<RefCell<Point<Pixels>>>,
3063}
3064
3065impl ScrollAnchor {
3066 pub fn for_handle(handle: ScrollHandle) -> Self {
3068 Self {
3069 handle,
3070 last_origin: Default::default(),
3071 }
3072 }
3073 pub fn scroll_to(&self, window: &mut Window, _cx: &mut App) {
3075 let this = self.clone();
3076
3077 window.on_next_frame(move |_, _| {
3078 let viewport_bounds = this.handle.bounds();
3079 let self_bounds = *this.last_origin.borrow();
3080 this.handle.set_offset(viewport_bounds.origin - self_bounds);
3081 });
3082 }
3083}
3084
3085#[derive(Default, Debug)]
3086struct ScrollHandleState {
3087 offset: Rc<RefCell<Point<Pixels>>>,
3088 bounds: Bounds<Pixels>,
3089 max_offset: Size<Pixels>,
3090 child_bounds: Vec<Bounds<Pixels>>,
3091 scroll_to_bottom: bool,
3092 overflow: Point<Overflow>,
3093 active_item: Option<ScrollActiveItem>,
3094}
3095
3096#[derive(Default, Debug, Clone, Copy)]
3097struct ScrollActiveItem {
3098 index: usize,
3099 strategy: ScrollStrategy,
3100}
3101
3102#[derive(Default, Debug, Clone, Copy)]
3103enum ScrollStrategy {
3104 #[default]
3105 FirstVisible,
3106 Top,
3107}
3108
3109#[derive(Clone, Debug)]
3113pub struct ScrollHandle(Rc<RefCell<ScrollHandleState>>);
3114
3115impl Default for ScrollHandle {
3116 fn default() -> Self {
3117 Self::new()
3118 }
3119}
3120
3121impl ScrollHandle {
3122 pub fn new() -> Self {
3124 Self(Rc::default())
3125 }
3126
3127 pub fn offset(&self) -> Point<Pixels> {
3129 *self.0.borrow().offset.borrow()
3130 }
3131
3132 pub fn max_offset(&self) -> Size<Pixels> {
3134 self.0.borrow().max_offset
3135 }
3136
3137 pub fn top_item(&self) -> usize {
3139 let state = self.0.borrow();
3140 let top = state.bounds.top() - state.offset.borrow().y;
3141
3142 match state.child_bounds.binary_search_by(|bounds| {
3143 if top < bounds.top() {
3144 Ordering::Greater
3145 } else if top > bounds.bottom() {
3146 Ordering::Less
3147 } else {
3148 Ordering::Equal
3149 }
3150 }) {
3151 Ok(ix) => ix,
3152 Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)),
3153 }
3154 }
3155
3156 pub fn bottom_item(&self) -> usize {
3158 let state = self.0.borrow();
3159 let bottom = state.bounds.bottom() - state.offset.borrow().y;
3160
3161 match state.child_bounds.binary_search_by(|bounds| {
3162 if bottom < bounds.top() {
3163 Ordering::Greater
3164 } else if bottom > bounds.bottom() {
3165 Ordering::Less
3166 } else {
3167 Ordering::Equal
3168 }
3169 }) {
3170 Ok(ix) => ix,
3171 Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)),
3172 }
3173 }
3174
3175 pub fn bounds(&self) -> Bounds<Pixels> {
3177 self.0.borrow().bounds
3178 }
3179
3180 pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
3182 self.0.borrow().child_bounds.get(ix).cloned()
3183 }
3184
3185 pub fn scroll_to_item(&self, ix: usize) {
3187 let mut state = self.0.borrow_mut();
3188 state.active_item = Some(ScrollActiveItem {
3189 index: ix,
3190 strategy: ScrollStrategy::default(),
3191 });
3192 }
3193
3194 pub fn scroll_to_top_of_item(&self, ix: usize) {
3197 let mut state = self.0.borrow_mut();
3198 state.active_item = Some(ScrollActiveItem {
3199 index: ix,
3200 strategy: ScrollStrategy::Top,
3201 });
3202 }
3203
3204 fn scroll_to_active_item(&self) {
3208 let mut state = self.0.borrow_mut();
3209
3210 let Some(active_item) = state.active_item else {
3211 return;
3212 };
3213
3214 let active_item = match state.child_bounds.get(active_item.index) {
3215 Some(bounds) => {
3216 let mut scroll_offset = state.offset.borrow_mut();
3217
3218 match active_item.strategy {
3219 ScrollStrategy::FirstVisible => {
3220 if state.overflow.y == Overflow::Scroll {
3221 if bounds.top() + scroll_offset.y < state.bounds.top() {
3222 scroll_offset.y = state.bounds.top() - bounds.top();
3223 } else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() {
3224 scroll_offset.y = state.bounds.bottom() - bounds.bottom();
3225 }
3226 }
3227 }
3228 ScrollStrategy::Top => {
3229 scroll_offset.y = state.bounds.top() - bounds.top();
3230 }
3231 }
3232
3233 if state.overflow.x == Overflow::Scroll {
3234 if bounds.left() + scroll_offset.x < state.bounds.left() {
3235 scroll_offset.x = state.bounds.left() - bounds.left();
3236 } else if bounds.right() + scroll_offset.x > state.bounds.right() {
3237 scroll_offset.x = state.bounds.right() - bounds.right();
3238 }
3239 }
3240 None
3241 }
3242 None => Some(active_item),
3243 };
3244 state.active_item = active_item;
3245 }
3246
3247 pub fn scroll_to_bottom(&self) {
3249 let mut state = self.0.borrow_mut();
3250 state.scroll_to_bottom = true;
3251 }
3252
3253 pub fn set_offset(&self, mut position: Point<Pixels>) {
3257 let state = self.0.borrow();
3258 *state.offset.borrow_mut() = position;
3259 }
3260
3261 pub fn logical_scroll_top(&self) -> (usize, Pixels) {
3263 let ix = self.top_item();
3264 let state = self.0.borrow();
3265
3266 if let Some(child_bounds) = state.child_bounds.get(ix) {
3267 (
3268 ix,
3269 child_bounds.top() + state.offset.borrow().y - state.bounds.top(),
3270 )
3271 } else {
3272 (ix, px(0.))
3273 }
3274 }
3275
3276 pub fn logical_scroll_bottom(&self) -> (usize, Pixels) {
3278 let ix = self.bottom_item();
3279 let state = self.0.borrow();
3280
3281 if let Some(child_bounds) = state.child_bounds.get(ix) {
3282 (
3283 ix,
3284 child_bounds.bottom() + state.offset.borrow().y - state.bounds.bottom(),
3285 )
3286 } else {
3287 (ix, px(0.))
3288 }
3289 }
3290
3291 pub fn children_count(&self) -> usize {
3293 self.0.borrow().child_bounds.len()
3294 }
3295}