1use crate::{
2 point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext,
3 BorrowWindow, Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusHandle, IntoElement,
4 IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent,
5 MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent,
6 SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility,
7 WindowContext,
8};
9
10use collections::HashMap;
11use refineable::Refineable;
12use smallvec::SmallVec;
13use std::{
14 any::{Any, TypeId},
15 cell::RefCell,
16 cmp::Ordering,
17 fmt::Debug,
18 marker::PhantomData,
19 mem,
20 rc::Rc,
21 time::Duration,
22};
23use taffy::style::Overflow;
24use util::ResultExt;
25
26const DRAG_THRESHOLD: f64 = 2.;
27const TOOLTIP_DELAY: Duration = Duration::from_millis(500);
28
29pub struct GroupStyle {
30 pub group: SharedString,
31 pub style: Box<StyleRefinement>,
32}
33
34pub struct DragMoveEvent<T> {
35 pub event: MouseMoveEvent,
36 pub bounds: Bounds<Pixels>,
37 drag: PhantomData<T>,
38}
39
40impl<T: 'static> DragMoveEvent<T> {
41 pub fn drag<'b>(&self, cx: &'b AppContext) -> &'b T {
42 cx.active_drag
43 .as_ref()
44 .and_then(|drag| drag.value.downcast_ref::<T>())
45 .expect("DragMoveEvent is only valid when the stored active drag is of the same type.")
46 }
47}
48
49impl Interactivity {
50 pub fn on_mouse_down(
51 &mut self,
52 button: MouseButton,
53 listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
54 ) {
55 self.mouse_down_listeners
56 .push(Box::new(move |event, bounds, phase, cx| {
57 if phase == DispatchPhase::Bubble
58 && event.button == button
59 && bounds.visibly_contains(&event.position, cx)
60 {
61 (listener)(event, cx)
62 }
63 }));
64 }
65
66 pub fn capture_any_mouse_down(
67 &mut self,
68 listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
69 ) {
70 self.mouse_down_listeners
71 .push(Box::new(move |event, bounds, phase, cx| {
72 if phase == DispatchPhase::Capture && bounds.visibly_contains(&event.position, cx) {
73 (listener)(event, cx)
74 }
75 }));
76 }
77
78 pub fn on_any_mouse_down(
79 &mut self,
80 listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
81 ) {
82 self.mouse_down_listeners
83 .push(Box::new(move |event, bounds, phase, cx| {
84 if phase == DispatchPhase::Bubble && bounds.visibly_contains(&event.position, cx) {
85 (listener)(event, cx)
86 }
87 }));
88 }
89
90 pub fn on_mouse_up(
91 &mut self,
92 button: MouseButton,
93 listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
94 ) {
95 self.mouse_up_listeners
96 .push(Box::new(move |event, bounds, phase, cx| {
97 if phase == DispatchPhase::Bubble
98 && event.button == button
99 && bounds.visibly_contains(&event.position, cx)
100 {
101 (listener)(event, cx)
102 }
103 }));
104 }
105
106 pub fn capture_any_mouse_up(
107 &mut self,
108 listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
109 ) {
110 self.mouse_up_listeners
111 .push(Box::new(move |event, bounds, phase, cx| {
112 if phase == DispatchPhase::Capture && bounds.visibly_contains(&event.position, cx) {
113 (listener)(event, cx)
114 }
115 }));
116 }
117
118 pub fn on_any_mouse_up(
119 &mut self,
120 listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
121 ) {
122 self.mouse_up_listeners
123 .push(Box::new(move |event, bounds, phase, cx| {
124 if phase == DispatchPhase::Bubble && bounds.visibly_contains(&event.position, cx) {
125 (listener)(event, cx)
126 }
127 }));
128 }
129
130 pub fn on_mouse_down_out(
131 &mut self,
132 listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
133 ) {
134 self.mouse_down_listeners
135 .push(Box::new(move |event, bounds, phase, cx| {
136 if phase == DispatchPhase::Capture && !bounds.visibly_contains(&event.position, cx)
137 {
138 (listener)(event, cx)
139 }
140 }));
141 }
142
143 pub fn on_mouse_up_out(
144 &mut self,
145 button: MouseButton,
146 listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
147 ) {
148 self.mouse_up_listeners
149 .push(Box::new(move |event, bounds, phase, cx| {
150 if phase == DispatchPhase::Capture
151 && event.button == button
152 && !bounds.visibly_contains(&event.position, cx)
153 {
154 (listener)(event, cx);
155 }
156 }));
157 }
158
159 pub fn on_mouse_move(
160 &mut self,
161 listener: impl Fn(&MouseMoveEvent, &mut WindowContext) + 'static,
162 ) {
163 self.mouse_move_listeners
164 .push(Box::new(move |event, bounds, phase, cx| {
165 if phase == DispatchPhase::Bubble && bounds.visibly_contains(&event.position, cx) {
166 (listener)(event, cx);
167 }
168 }));
169 }
170
171 pub fn on_drag_move<T>(
172 &mut self,
173 listener: impl Fn(&DragMoveEvent<T>, &mut WindowContext) + 'static,
174 ) where
175 T: 'static,
176 {
177 self.mouse_move_listeners
178 .push(Box::new(move |event, bounds, phase, cx| {
179 if phase == DispatchPhase::Capture {
180 if cx
181 .active_drag
182 .as_ref()
183 .is_some_and(|drag| drag.value.as_ref().type_id() == TypeId::of::<T>())
184 {
185 (listener)(
186 &DragMoveEvent {
187 event: event.clone(),
188 bounds: bounds.bounds,
189 drag: PhantomData,
190 },
191 cx,
192 );
193 }
194 }
195 }));
196 }
197
198 pub fn on_scroll_wheel(
199 &mut self,
200 listener: impl Fn(&ScrollWheelEvent, &mut WindowContext) + 'static,
201 ) {
202 self.scroll_wheel_listeners
203 .push(Box::new(move |event, bounds, phase, cx| {
204 if phase == DispatchPhase::Bubble && bounds.visibly_contains(&event.position, cx) {
205 (listener)(event, cx);
206 }
207 }));
208 }
209
210 pub fn capture_action<A: Action>(
211 &mut self,
212 listener: impl Fn(&A, &mut WindowContext) + 'static,
213 ) {
214 self.action_listeners.push((
215 TypeId::of::<A>(),
216 Box::new(move |action, phase, cx| {
217 let action = action.downcast_ref().unwrap();
218 if phase == DispatchPhase::Capture {
219 (listener)(action, cx)
220 }
221 }),
222 ));
223 }
224
225 pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut WindowContext) + 'static) {
226 self.action_listeners.push((
227 TypeId::of::<A>(),
228 Box::new(move |action, phase, cx| {
229 let action = action.downcast_ref().unwrap();
230 if phase == DispatchPhase::Bubble {
231 (listener)(action, cx)
232 }
233 }),
234 ));
235 }
236
237 pub fn on_boxed_action(
238 &mut self,
239 action: &Box<dyn Action>,
240 listener: impl Fn(&Box<dyn Action>, &mut WindowContext) + 'static,
241 ) {
242 let action = action.boxed_clone();
243 self.action_listeners.push((
244 (*action).type_id(),
245 Box::new(move |_, phase, cx| {
246 if phase == DispatchPhase::Bubble {
247 (listener)(&action, cx)
248 }
249 }),
250 ));
251 }
252
253 pub fn on_key_down(&mut self, listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static) {
254 self.key_down_listeners
255 .push(Box::new(move |event, phase, cx| {
256 if phase == DispatchPhase::Bubble {
257 (listener)(event, cx)
258 }
259 }));
260 }
261
262 pub fn capture_key_down(
263 &mut self,
264 listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
265 ) {
266 self.key_down_listeners
267 .push(Box::new(move |event, phase, cx| {
268 if phase == DispatchPhase::Capture {
269 listener(event, cx)
270 }
271 }));
272 }
273
274 pub fn on_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) {
275 self.key_up_listeners
276 .push(Box::new(move |event, phase, cx| {
277 if phase == DispatchPhase::Bubble {
278 listener(event, cx)
279 }
280 }));
281 }
282
283 pub fn capture_key_up(&mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) {
284 self.key_up_listeners
285 .push(Box::new(move |event, phase, cx| {
286 if phase == DispatchPhase::Capture {
287 listener(event, cx)
288 }
289 }));
290 }
291
292 pub fn on_drop<T: 'static>(&mut self, listener: impl Fn(&T, &mut WindowContext) + 'static) {
293 self.drop_listeners.push((
294 TypeId::of::<T>(),
295 Box::new(move |dragged_value, cx| {
296 listener(dragged_value.downcast_ref().unwrap(), cx);
297 }),
298 ));
299 }
300
301 pub fn can_drop(&mut self, predicate: impl Fn(&dyn Any, &mut WindowContext) -> bool + 'static) {
302 self.can_drop_predicate = Some(Box::new(predicate));
303 }
304
305 pub fn on_click(&mut self, listener: impl Fn(&ClickEvent, &mut WindowContext) + 'static)
306 where
307 Self: Sized,
308 {
309 self.click_listeners
310 .push(Box::new(move |event, cx| listener(event, cx)));
311 }
312
313 pub fn on_drag<T, W>(
314 &mut self,
315 value: T,
316 constructor: impl Fn(&T, &mut WindowContext) -> View<W> + 'static,
317 ) where
318 Self: Sized,
319 T: 'static,
320 W: 'static + Render,
321 {
322 debug_assert!(
323 self.drag_listener.is_none(),
324 "calling on_drag more than once on the same element is not supported"
325 );
326 self.drag_listener = Some((
327 Box::new(value),
328 Box::new(move |value, cx| constructor(value.downcast_ref().unwrap(), cx).into()),
329 ));
330 }
331
332 pub fn on_hover(&mut self, listener: impl Fn(&bool, &mut WindowContext) + 'static)
333 where
334 Self: Sized,
335 {
336 debug_assert!(
337 self.hover_listener.is_none(),
338 "calling on_hover more than once on the same element is not supported"
339 );
340 self.hover_listener = Some(Box::new(listener));
341 }
342
343 pub fn tooltip(&mut self, build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static)
344 where
345 Self: Sized,
346 {
347 debug_assert!(
348 self.tooltip_builder.is_none(),
349 "calling tooltip more than once on the same element is not supported"
350 );
351 self.tooltip_builder = Some(Rc::new(build_tooltip));
352 }
353
354 pub fn block_mouse(&mut self) {
355 self.block_mouse = true;
356 }
357}
358
359pub trait InteractiveElement: Sized {
360 fn interactivity(&mut self) -> &mut Interactivity;
361
362 fn group(mut self, group: impl Into<SharedString>) -> Self {
363 self.interactivity().group = Some(group.into());
364 self
365 }
366
367 fn id(mut self, id: impl Into<ElementId>) -> Stateful<Self> {
368 self.interactivity().element_id = Some(id.into());
369
370 Stateful { element: self }
371 }
372
373 fn track_focus(mut self, focus_handle: &FocusHandle) -> Focusable<Self> {
374 self.interactivity().focusable = true;
375 self.interactivity().tracked_focus_handle = Some(focus_handle.clone());
376 Focusable { element: self }
377 }
378
379 fn key_context<C, E>(mut self, key_context: C) -> Self
380 where
381 C: TryInto<KeyContext, Error = E>,
382 E: Debug,
383 {
384 if let Some(key_context) = key_context.try_into().log_err() {
385 self.interactivity().key_context = Some(key_context);
386 }
387 self
388 }
389
390 fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
391 debug_assert!(
392 self.interactivity().hover_style.is_none(),
393 "hover style already set"
394 );
395 self.interactivity().hover_style = Some(Box::new(f(StyleRefinement::default())));
396 self
397 }
398
399 fn group_hover(
400 mut self,
401 group_name: impl Into<SharedString>,
402 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
403 ) -> Self {
404 self.interactivity().group_hover_style = Some(GroupStyle {
405 group: group_name.into(),
406 style: Box::new(f(StyleRefinement::default())),
407 });
408 self
409 }
410
411 fn on_mouse_down(
412 mut self,
413 button: MouseButton,
414 listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
415 ) -> Self {
416 self.interactivity().on_mouse_down(button, listener);
417 self
418 }
419
420 fn capture_any_mouse_down(
421 mut self,
422 listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
423 ) -> Self {
424 self.interactivity().capture_any_mouse_down(listener);
425 self
426 }
427
428 fn on_any_mouse_down(
429 mut self,
430 listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
431 ) -> Self {
432 self.interactivity().on_any_mouse_down(listener);
433 self
434 }
435
436 fn on_mouse_up(
437 mut self,
438 button: MouseButton,
439 listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
440 ) -> Self {
441 self.interactivity().on_mouse_up(button, listener);
442 self
443 }
444
445 fn capture_any_mouse_up(
446 mut self,
447 listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
448 ) -> Self {
449 self.interactivity().capture_any_mouse_up(listener);
450 self
451 }
452
453 fn on_mouse_down_out(
454 mut self,
455 listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
456 ) -> Self {
457 self.interactivity().on_mouse_down_out(listener);
458 self
459 }
460
461 fn on_mouse_up_out(
462 mut self,
463 button: MouseButton,
464 listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
465 ) -> Self {
466 self.interactivity().on_mouse_up_out(button, listener);
467 self
468 }
469
470 fn on_mouse_move(
471 mut self,
472 listener: impl Fn(&MouseMoveEvent, &mut WindowContext) + 'static,
473 ) -> Self {
474 self.interactivity().on_mouse_move(listener);
475 self
476 }
477
478 fn on_drag_move<T: 'static>(
479 mut self,
480 listener: impl Fn(&DragMoveEvent<T>, &mut WindowContext) + 'static,
481 ) -> Self
482 where
483 T: 'static,
484 {
485 self.interactivity().on_drag_move(listener);
486 self
487 }
488
489 fn on_scroll_wheel(
490 mut self,
491 listener: impl Fn(&ScrollWheelEvent, &mut WindowContext) + 'static,
492 ) -> Self {
493 self.interactivity().on_scroll_wheel(listener);
494 self
495 }
496
497 /// Capture the given action, before normal action dispatch can fire
498 fn capture_action<A: Action>(
499 mut self,
500 listener: impl Fn(&A, &mut WindowContext) + 'static,
501 ) -> Self {
502 self.interactivity().capture_action(listener);
503 self
504 }
505
506 /// Add a listener for the given action, fires during the bubble event phase
507 fn on_action<A: Action>(mut self, listener: impl Fn(&A, &mut WindowContext) + 'static) -> Self {
508 self.interactivity().on_action(listener);
509 self
510 }
511
512 fn on_boxed_action(
513 mut self,
514 action: &Box<dyn Action>,
515 listener: impl Fn(&Box<dyn Action>, &mut WindowContext) + 'static,
516 ) -> Self {
517 self.interactivity().on_boxed_action(action, listener);
518 self
519 }
520
521 fn on_key_down(
522 mut self,
523 listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
524 ) -> Self {
525 self.interactivity().on_key_down(listener);
526 self
527 }
528
529 fn capture_key_down(
530 mut self,
531 listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
532 ) -> Self {
533 self.interactivity().capture_key_down(listener);
534 self
535 }
536
537 fn on_key_up(mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) -> Self {
538 self.interactivity().on_key_up(listener);
539 self
540 }
541
542 fn capture_key_up(
543 mut self,
544 listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static,
545 ) -> Self {
546 self.interactivity().capture_key_up(listener);
547 self
548 }
549
550 fn drag_over<S: 'static>(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
551 self.interactivity()
552 .drag_over_styles
553 .push((TypeId::of::<S>(), f(StyleRefinement::default())));
554 self
555 }
556
557 fn group_drag_over<S: 'static>(
558 mut self,
559 group_name: impl Into<SharedString>,
560 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
561 ) -> Self {
562 self.interactivity().group_drag_over_styles.push((
563 TypeId::of::<S>(),
564 GroupStyle {
565 group: group_name.into(),
566 style: Box::new(f(StyleRefinement::default())),
567 },
568 ));
569 self
570 }
571
572 fn on_drop<T: 'static>(mut self, listener: impl Fn(&T, &mut WindowContext) + 'static) -> Self {
573 self.interactivity().on_drop(listener);
574 self
575 }
576
577 fn can_drop(
578 mut self,
579 predicate: impl Fn(&dyn Any, &mut WindowContext) -> bool + 'static,
580 ) -> Self {
581 self.interactivity().can_drop(predicate);
582 self
583 }
584
585 fn block_mouse(mut self) -> Self {
586 self.interactivity().block_mouse();
587 self
588 }
589}
590
591pub trait StatefulInteractiveElement: InteractiveElement {
592 fn focusable(mut self) -> Focusable<Self> {
593 self.interactivity().focusable = true;
594 Focusable { element: self }
595 }
596
597 fn overflow_scroll(mut self) -> Self {
598 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
599 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
600 self
601 }
602
603 fn overflow_x_scroll(mut self) -> Self {
604 self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
605 self
606 }
607
608 fn overflow_y_scroll(mut self) -> Self {
609 self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
610 self
611 }
612
613 fn track_scroll(mut self, scroll_handle: &ScrollHandle) -> Self {
614 self.interactivity().scroll_handle = Some(scroll_handle.clone());
615 self
616 }
617
618 fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
619 where
620 Self: Sized,
621 {
622 self.interactivity().active_style = Some(Box::new(f(StyleRefinement::default())));
623 self
624 }
625
626 fn group_active(
627 mut self,
628 group_name: impl Into<SharedString>,
629 f: impl FnOnce(StyleRefinement) -> StyleRefinement,
630 ) -> Self
631 where
632 Self: Sized,
633 {
634 self.interactivity().group_active_style = Some(GroupStyle {
635 group: group_name.into(),
636 style: Box::new(f(StyleRefinement::default())),
637 });
638 self
639 }
640
641 fn on_click(mut self, listener: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self
642 where
643 Self: Sized,
644 {
645 self.interactivity().on_click(listener);
646 self
647 }
648
649 fn on_drag<T, W>(
650 mut self,
651 value: T,
652 constructor: impl Fn(&T, &mut WindowContext) -> View<W> + 'static,
653 ) -> Self
654 where
655 Self: Sized,
656 T: 'static,
657 W: 'static + Render,
658 {
659 self.interactivity().on_drag(value, constructor);
660 self
661 }
662
663 fn on_hover(mut self, listener: impl Fn(&bool, &mut WindowContext) + 'static) -> Self
664 where
665 Self: Sized,
666 {
667 self.interactivity().on_hover(listener);
668 self
669 }
670
671 fn tooltip(mut self, build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self
672 where
673 Self: Sized,
674 {
675 self.interactivity().tooltip(build_tooltip);
676 self
677 }
678}
679
680pub trait FocusableElement: InteractiveElement {
681 fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
682 where
683 Self: Sized,
684 {
685 self.interactivity().focus_style = Some(Box::new(f(StyleRefinement::default())));
686 self
687 }
688
689 fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
690 where
691 Self: Sized,
692 {
693 self.interactivity().in_focus_style = Some(Box::new(f(StyleRefinement::default())));
694 self
695 }
696}
697
698pub type MouseDownListener =
699 Box<dyn Fn(&MouseDownEvent, &InteractiveBounds, DispatchPhase, &mut WindowContext) + 'static>;
700pub type MouseUpListener =
701 Box<dyn Fn(&MouseUpEvent, &InteractiveBounds, DispatchPhase, &mut WindowContext) + 'static>;
702
703pub type MouseMoveListener =
704 Box<dyn Fn(&MouseMoveEvent, &InteractiveBounds, DispatchPhase, &mut WindowContext) + 'static>;
705
706pub type ScrollWheelListener =
707 Box<dyn Fn(&ScrollWheelEvent, &InteractiveBounds, DispatchPhase, &mut WindowContext) + 'static>;
708
709pub type ClickListener = Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>;
710
711pub type DragListener = Box<dyn Fn(&dyn Any, &mut WindowContext) -> AnyView + 'static>;
712
713type DropListener = Box<dyn Fn(&dyn Any, &mut WindowContext) + 'static>;
714
715type CanDropPredicate = Box<dyn Fn(&dyn Any, &mut WindowContext) -> bool + 'static>;
716
717pub type TooltipBuilder = Rc<dyn Fn(&mut WindowContext) -> AnyView + 'static>;
718
719pub type KeyDownListener = Box<dyn Fn(&KeyDownEvent, DispatchPhase, &mut WindowContext) + 'static>;
720
721pub type KeyUpListener = Box<dyn Fn(&KeyUpEvent, DispatchPhase, &mut WindowContext) + 'static>;
722
723pub type DragEventListener = Box<dyn Fn(&MouseMoveEvent, &mut WindowContext) + 'static>;
724
725pub type ActionListener = Box<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
726
727#[track_caller]
728pub fn div() -> Div {
729 #[cfg(debug_assertions)]
730 let interactivity = {
731 let mut interactivity = Interactivity::default();
732 interactivity.location = Some(*core::panic::Location::caller());
733 interactivity
734 };
735
736 #[cfg(not(debug_assertions))]
737 let interactivity = Interactivity::default();
738
739 Div {
740 interactivity,
741 children: SmallVec::default(),
742 }
743}
744
745pub struct Div {
746 interactivity: Interactivity,
747 children: SmallVec<[AnyElement; 2]>,
748}
749
750impl Styled for Div {
751 fn style(&mut self) -> &mut StyleRefinement {
752 &mut self.interactivity.base_style
753 }
754}
755
756impl InteractiveElement for Div {
757 fn interactivity(&mut self) -> &mut Interactivity {
758 &mut self.interactivity
759 }
760}
761
762impl ParentElement for Div {
763 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
764 &mut self.children
765 }
766}
767
768impl Element for Div {
769 type State = DivState;
770
771 fn layout(
772 &mut self,
773 element_state: Option<Self::State>,
774 cx: &mut WindowContext,
775 ) -> (LayoutId, Self::State) {
776 let mut child_layout_ids = SmallVec::new();
777 let (layout_id, interactive_state) = self.interactivity.layout(
778 element_state.map(|s| s.interactive_state),
779 cx,
780 |style, cx| {
781 cx.with_text_style(style.text_style().cloned(), |cx| {
782 child_layout_ids = self
783 .children
784 .iter_mut()
785 .map(|child| child.layout(cx))
786 .collect::<SmallVec<_>>();
787 cx.request_layout(&style, child_layout_ids.iter().copied())
788 })
789 },
790 );
791 (
792 layout_id,
793 DivState {
794 interactive_state,
795 child_layout_ids,
796 },
797 )
798 }
799
800 fn paint(
801 &mut self,
802 bounds: Bounds<Pixels>,
803 element_state: &mut Self::State,
804 cx: &mut WindowContext,
805 ) {
806 let mut child_min = point(Pixels::MAX, Pixels::MAX);
807 let mut child_max = Point::default();
808 let content_size = if element_state.child_layout_ids.is_empty() {
809 bounds.size
810 } else if let Some(scroll_handle) = self.interactivity.scroll_handle.as_ref() {
811 let mut state = scroll_handle.0.borrow_mut();
812 state.child_bounds = Vec::with_capacity(element_state.child_layout_ids.len());
813 state.bounds = bounds;
814 let requested = state.requested_scroll_top.take();
815
816 for (ix, child_layout_id) in element_state.child_layout_ids.iter().enumerate() {
817 let child_bounds = cx.layout_bounds(*child_layout_id);
818 child_min = child_min.min(&child_bounds.origin);
819 child_max = child_max.max(&child_bounds.lower_right());
820 state.child_bounds.push(child_bounds);
821
822 if let Some(requested) = requested.as_ref() {
823 if requested.0 == ix {
824 *state.offset.borrow_mut() =
825 bounds.origin - (child_bounds.origin - point(px(0.), requested.1));
826 }
827 }
828 }
829 (child_max - child_min).into()
830 } else {
831 for child_layout_id in &element_state.child_layout_ids {
832 let child_bounds = cx.layout_bounds(*child_layout_id);
833 child_min = child_min.min(&child_bounds.origin);
834 child_max = child_max.max(&child_bounds.lower_right());
835 }
836 (child_max - child_min).into()
837 };
838
839 self.interactivity.paint(
840 bounds,
841 content_size,
842 &mut element_state.interactive_state,
843 cx,
844 |_style, scroll_offset, cx| {
845 cx.with_element_offset(scroll_offset, |cx| {
846 for child in &mut self.children {
847 child.paint(cx);
848 }
849 })
850 },
851 );
852 }
853}
854
855impl IntoElement for Div {
856 type Element = Self;
857
858 fn element_id(&self) -> Option<ElementId> {
859 self.interactivity.element_id.clone()
860 }
861
862 fn into_element(self) -> Self::Element {
863 self
864 }
865}
866
867pub struct DivState {
868 child_layout_ids: SmallVec<[LayoutId; 2]>,
869 interactive_state: InteractiveElementState,
870}
871
872impl DivState {
873 pub fn is_active(&self) -> bool {
874 self.interactive_state
875 .pending_mouse_down
876 .as_ref()
877 .map_or(false, |pending| pending.borrow().is_some())
878 }
879}
880
881pub struct Interactivity {
882 pub element_id: Option<ElementId>,
883 pub key_context: Option<KeyContext>,
884 pub focusable: bool,
885 pub tracked_focus_handle: Option<FocusHandle>,
886 pub scroll_handle: Option<ScrollHandle>,
887 pub group: Option<SharedString>,
888 pub base_style: Box<StyleRefinement>,
889 pub focus_style: Option<Box<StyleRefinement>>,
890 pub in_focus_style: Option<Box<StyleRefinement>>,
891 pub hover_style: Option<Box<StyleRefinement>>,
892 pub group_hover_style: Option<GroupStyle>,
893 pub active_style: Option<Box<StyleRefinement>>,
894 pub group_active_style: Option<GroupStyle>,
895 pub drag_over_styles: Vec<(TypeId, StyleRefinement)>,
896 pub group_drag_over_styles: Vec<(TypeId, GroupStyle)>,
897 pub mouse_down_listeners: Vec<MouseDownListener>,
898 pub mouse_up_listeners: Vec<MouseUpListener>,
899 pub mouse_move_listeners: Vec<MouseMoveListener>,
900 pub scroll_wheel_listeners: Vec<ScrollWheelListener>,
901 pub key_down_listeners: Vec<KeyDownListener>,
902 pub key_up_listeners: Vec<KeyUpListener>,
903 pub action_listeners: Vec<(TypeId, ActionListener)>,
904 pub drop_listeners: Vec<(TypeId, DropListener)>,
905 pub can_drop_predicate: Option<CanDropPredicate>,
906 pub click_listeners: Vec<ClickListener>,
907 pub drag_listener: Option<(Box<dyn Any>, DragListener)>,
908 pub hover_listener: Option<Box<dyn Fn(&bool, &mut WindowContext)>>,
909 pub tooltip_builder: Option<TooltipBuilder>,
910 pub block_mouse: bool,
911
912 #[cfg(debug_assertions)]
913 pub location: Option<core::panic::Location<'static>>,
914}
915
916#[derive(Clone, Debug)]
917pub struct InteractiveBounds {
918 pub bounds: Bounds<Pixels>,
919 pub stacking_order: StackingOrder,
920}
921
922impl InteractiveBounds {
923 pub fn visibly_contains(&self, point: &Point<Pixels>, cx: &WindowContext) -> bool {
924 self.bounds.contains(point) && cx.was_top_layer(&point, &self.stacking_order)
925 }
926
927 pub fn drag_target_contains(&self, point: &Point<Pixels>, cx: &WindowContext) -> bool {
928 self.bounds.contains(point)
929 && cx.was_top_layer_under_active_drag(&point, &self.stacking_order)
930 }
931}
932
933impl Interactivity {
934 pub fn layout(
935 &mut self,
936 element_state: Option<InteractiveElementState>,
937 cx: &mut WindowContext,
938 f: impl FnOnce(Style, &mut WindowContext) -> LayoutId,
939 ) -> (LayoutId, InteractiveElementState) {
940 let mut element_state = element_state.unwrap_or_default();
941
942 if cx.has_active_drag() {
943 if let Some(pending_mouse_down) = element_state.pending_mouse_down.as_ref() {
944 *pending_mouse_down.borrow_mut() = None;
945 }
946 if let Some(clicked_state) = element_state.clicked_state.as_ref() {
947 *clicked_state.borrow_mut() = ElementClickedState::default();
948 }
949 }
950
951 // Ensure we store a focus handle in our element state if we're focusable.
952 // If there's an explicit focus handle we're tracking, use that. Otherwise
953 // create a new handle and store it in the element state, which lives for as
954 // as frames contain an element with this id.
955 if self.focusable {
956 element_state.focus_handle.get_or_insert_with(|| {
957 self.tracked_focus_handle
958 .clone()
959 .unwrap_or_else(|| cx.focus_handle())
960 });
961 }
962
963 if let Some(scroll_handle) = self.scroll_handle.as_ref() {
964 element_state.scroll_offset = Some(scroll_handle.0.borrow().offset.clone());
965 }
966
967 let style = self.compute_style(None, &mut element_state, cx);
968 let layout_id = f(style, cx);
969 (layout_id, element_state)
970 }
971
972 pub fn paint(
973 &mut self,
974 bounds: Bounds<Pixels>,
975 content_size: Size<Pixels>,
976 element_state: &mut InteractiveElementState,
977 cx: &mut WindowContext,
978 f: impl FnOnce(&Style, Point<Pixels>, &mut WindowContext),
979 ) {
980 let style = self.compute_style(Some(bounds), element_state, cx);
981
982 if style.visibility == Visibility::Hidden {
983 return;
984 }
985
986 let z_index = style.z_index.unwrap_or(0);
987 cx.with_z_index(z_index, |cx| {
988 style.paint(bounds, cx, |cx| {
989 cx.with_text_style(style.text_style().cloned(), |cx| {
990 cx.with_content_mask(style.overflow_mask(bounds, cx.rem_size()), |cx| {
991 #[cfg(debug_assertions)]
992 if self.element_id.is_some()
993 && (style.debug
994 || style.debug_below
995 || cx.has_global::<crate::DebugBelow>())
996 && bounds.contains(&cx.mouse_position())
997 {
998 const FONT_SIZE: crate::Pixels = crate::Pixels(10.);
999 let element_id = format!("{:?}", self.element_id.as_ref().unwrap());
1000 let str_len = element_id.len();
1001
1002 let render_debug_text = |cx: &mut WindowContext| {
1003 if let Some(text) = cx
1004 .text_system()
1005 .shape_text(
1006 &element_id,
1007 FONT_SIZE,
1008 &[cx.text_style().to_run(str_len)],
1009 None,
1010 )
1011 .ok()
1012 .map(|mut text| text.pop())
1013 .flatten()
1014 {
1015 text.paint(bounds.origin, FONT_SIZE, cx).ok();
1016
1017 let text_bounds = crate::Bounds {
1018 origin: bounds.origin,
1019 size: text.size(FONT_SIZE),
1020 };
1021 if self.location.is_some()
1022 && text_bounds.contains(&cx.mouse_position())
1023 && cx.modifiers().command
1024 {
1025 let command_held = cx.modifiers().command;
1026 cx.on_key_event({
1027 let text_bounds = text_bounds.clone();
1028 move |e: &crate::ModifiersChangedEvent, _phase, cx| {
1029 if e.modifiers.command != command_held
1030 && text_bounds.contains(&cx.mouse_position())
1031 {
1032 cx.notify();
1033 }
1034 }
1035 });
1036
1037 let hovered = bounds.contains(&cx.mouse_position());
1038 cx.on_mouse_event(
1039 move |event: &MouseMoveEvent, phase, cx| {
1040 if phase == DispatchPhase::Capture {
1041 if bounds.contains(&event.position) != hovered {
1042 cx.notify();
1043 }
1044 }
1045 },
1046 );
1047
1048 cx.on_mouse_event({
1049 let location = self.location.clone().unwrap();
1050 let text_bounds = text_bounds.clone();
1051 move |e: &crate::MouseDownEvent, phase, cx| {
1052 if text_bounds.contains(&e.position)
1053 && phase.capture()
1054 {
1055 cx.stop_propagation();
1056 let Ok(dir) = std::env::current_dir() else {
1057 return;
1058 };
1059
1060 eprintln!(
1061 "This element is created at:\n{}:{}:{}",
1062 location.file(),
1063 location.line(),
1064 location.column()
1065 );
1066
1067 std::process::Command::new("zed")
1068 .arg(format!(
1069 "{}/{}:{}:{}",
1070 dir.to_string_lossy(),
1071 location.file(),
1072 location.line(),
1073 location.column()
1074 ))
1075 .spawn()
1076 .ok();
1077 }
1078 }
1079 });
1080 cx.paint_quad(crate::outline(
1081 crate::Bounds {
1082 origin: bounds.origin
1083 + crate::point(
1084 crate::px(0.),
1085 FONT_SIZE - px(2.),
1086 ),
1087 size: crate::Size {
1088 width: text_bounds.size.width,
1089 height: crate::px(1.),
1090 },
1091 },
1092 crate::red(),
1093 ))
1094 }
1095 }
1096 };
1097
1098 cx.with_z_index(1, |cx| {
1099 cx.with_text_style(
1100 Some(crate::TextStyleRefinement {
1101 color: Some(crate::red()),
1102 line_height: Some(FONT_SIZE.into()),
1103 background_color: Some(crate::white()),
1104 ..Default::default()
1105 }),
1106 render_debug_text,
1107 )
1108 });
1109 }
1110
1111 let interactive_bounds = InteractiveBounds {
1112 bounds: bounds.intersect(&cx.content_mask().bounds),
1113 stacking_order: cx.stacking_order().clone(),
1114 };
1115
1116 if self.block_mouse
1117 || style.background.as_ref().is_some_and(|fill| {
1118 fill.color().is_some_and(|color| !color.is_transparent())
1119 })
1120 {
1121 cx.add_opaque_layer(interactive_bounds.bounds);
1122 }
1123
1124 if !cx.has_active_drag() {
1125 if let Some(mouse_cursor) = style.mouse_cursor {
1126 let mouse_position = &cx.mouse_position();
1127 let hovered =
1128 interactive_bounds.visibly_contains(mouse_position, cx);
1129 if hovered {
1130 cx.set_cursor_style(mouse_cursor);
1131 }
1132 }
1133 }
1134
1135 // If this element can be focused, register a mouse down listener
1136 // that will automatically transfer focus when hitting the element.
1137 // This behavior can be suppressed by using `cx.prevent_default()`.
1138 if let Some(focus_handle) = element_state.focus_handle.clone() {
1139 cx.on_mouse_event({
1140 let interactive_bounds = interactive_bounds.clone();
1141 move |event: &MouseDownEvent, phase, cx| {
1142 if phase == DispatchPhase::Bubble
1143 && !cx.default_prevented()
1144 && interactive_bounds.visibly_contains(&event.position, cx)
1145 {
1146 cx.focus(&focus_handle);
1147 // If there is a parent that is also focusable, prevent it
1148 // from transferring focus because we already did so.
1149 cx.prevent_default();
1150 }
1151 }
1152 });
1153 }
1154
1155 for listener in self.mouse_down_listeners.drain(..) {
1156 let interactive_bounds = interactive_bounds.clone();
1157 cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
1158 listener(event, &interactive_bounds, phase, cx);
1159 })
1160 }
1161
1162 for listener in self.mouse_up_listeners.drain(..) {
1163 let interactive_bounds = interactive_bounds.clone();
1164 cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
1165 listener(event, &interactive_bounds, phase, cx);
1166 })
1167 }
1168
1169 for listener in self.mouse_move_listeners.drain(..) {
1170 let interactive_bounds = interactive_bounds.clone();
1171 cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
1172 listener(event, &interactive_bounds, phase, cx);
1173 })
1174 }
1175
1176 for listener in self.scroll_wheel_listeners.drain(..) {
1177 let interactive_bounds = interactive_bounds.clone();
1178 cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
1179 listener(event, &interactive_bounds, phase, cx);
1180 })
1181 }
1182
1183 let hover_group_bounds = self
1184 .group_hover_style
1185 .as_ref()
1186 .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
1187
1188 if let Some(group_bounds) = hover_group_bounds {
1189 let hovered = group_bounds.contains(&cx.mouse_position());
1190 cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
1191 if phase == DispatchPhase::Capture {
1192 if group_bounds.contains(&event.position) != hovered {
1193 cx.notify();
1194 }
1195 }
1196 });
1197 }
1198
1199 if self.hover_style.is_some()
1200 || self.base_style.mouse_cursor.is_some()
1201 || cx.active_drag.is_some() && !self.drag_over_styles.is_empty()
1202 {
1203 let bounds = bounds.intersect(&cx.content_mask().bounds);
1204 let hovered = bounds.contains(&cx.mouse_position());
1205 cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
1206 if phase == DispatchPhase::Capture {
1207 if bounds.contains(&event.position) != hovered {
1208 cx.notify();
1209 }
1210 }
1211 });
1212 }
1213
1214 let mut drag_listener = mem::take(&mut self.drag_listener);
1215 let drop_listeners = mem::take(&mut self.drop_listeners);
1216 let click_listeners = mem::take(&mut self.click_listeners);
1217 let can_drop_predicate = mem::take(&mut self.can_drop_predicate);
1218
1219 if !drop_listeners.is_empty() {
1220 cx.on_mouse_event({
1221 let interactive_bounds = interactive_bounds.clone();
1222 move |event: &MouseUpEvent, phase, cx| {
1223 if let Some(drag) = &cx.active_drag {
1224 if phase == DispatchPhase::Bubble
1225 && interactive_bounds
1226 .drag_target_contains(&event.position, cx)
1227 {
1228 let drag_state_type = drag.value.as_ref().type_id();
1229 for (drop_state_type, listener) in &drop_listeners {
1230 if *drop_state_type == drag_state_type {
1231 let drag = cx.active_drag.take().expect(
1232 "checked for type drag state type above",
1233 );
1234
1235 let mut can_drop = true;
1236 if let Some(predicate) = &can_drop_predicate {
1237 can_drop =
1238 predicate(drag.value.as_ref(), cx);
1239 }
1240
1241 if can_drop {
1242 listener(drag.value.as_ref(), cx);
1243 cx.notify();
1244 cx.stop_propagation();
1245 }
1246 }
1247 }
1248 }
1249 }
1250 }
1251 });
1252 }
1253
1254 if !click_listeners.is_empty() || drag_listener.is_some() {
1255 let pending_mouse_down = element_state
1256 .pending_mouse_down
1257 .get_or_insert_with(Default::default)
1258 .clone();
1259
1260 let clicked_state = element_state
1261 .clicked_state
1262 .get_or_insert_with(Default::default)
1263 .clone();
1264
1265 cx.on_mouse_event({
1266 let interactive_bounds = interactive_bounds.clone();
1267 let pending_mouse_down = pending_mouse_down.clone();
1268 move |event: &MouseDownEvent, phase, cx| {
1269 if phase == DispatchPhase::Bubble
1270 && event.button == MouseButton::Left
1271 && interactive_bounds.visibly_contains(&event.position, cx)
1272 {
1273 *pending_mouse_down.borrow_mut() = Some(event.clone());
1274 cx.notify();
1275 }
1276 }
1277 });
1278
1279 cx.on_mouse_event({
1280 let pending_mouse_down = pending_mouse_down.clone();
1281 move |event: &MouseMoveEvent, phase, cx| {
1282 if phase == DispatchPhase::Capture {
1283 return;
1284 }
1285
1286 let mut pending_mouse_down = pending_mouse_down.borrow_mut();
1287 if let Some(mouse_down) = pending_mouse_down.clone() {
1288 if !cx.has_active_drag()
1289 && (event.position - mouse_down.position).magnitude()
1290 > DRAG_THRESHOLD
1291 {
1292 if let Some((drag_value, drag_listener)) =
1293 drag_listener.take()
1294 {
1295 *clicked_state.borrow_mut() =
1296 ElementClickedState::default();
1297 let cursor_offset = event.position - bounds.origin;
1298 let drag = (drag_listener)(drag_value.as_ref(), cx);
1299 cx.active_drag = Some(AnyDrag {
1300 view: drag,
1301 value: drag_value,
1302 cursor_offset,
1303 });
1304 pending_mouse_down.take();
1305 cx.notify();
1306 cx.stop_propagation();
1307 }
1308 }
1309 }
1310 }
1311 });
1312
1313 cx.on_mouse_event({
1314 let interactive_bounds = interactive_bounds.clone();
1315 let mut captured_mouse_down = None;
1316 move |event: &MouseUpEvent, phase, cx| match phase {
1317 // Clear the pending mouse down during the capture phase,
1318 // so that it happens even if another event handler stops
1319 // propagation.
1320 DispatchPhase::Capture => {
1321 let mut pending_mouse_down =
1322 pending_mouse_down.borrow_mut();
1323 if pending_mouse_down.is_some() {
1324 captured_mouse_down = pending_mouse_down.take();
1325 cx.notify();
1326 }
1327 }
1328 // Fire click handlers during the bubble phase.
1329 DispatchPhase::Bubble => {
1330 if let Some(mouse_down) = captured_mouse_down.take() {
1331 if interactive_bounds
1332 .visibly_contains(&event.position, cx)
1333 {
1334 let mouse_click = ClickEvent {
1335 down: mouse_down,
1336 up: event.clone(),
1337 };
1338 for listener in &click_listeners {
1339 listener(&mouse_click, cx);
1340 }
1341 }
1342 }
1343 }
1344 }
1345 });
1346 }
1347
1348 if let Some(hover_listener) = self.hover_listener.take() {
1349 let was_hovered = element_state
1350 .hover_state
1351 .get_or_insert_with(Default::default)
1352 .clone();
1353 let has_mouse_down = element_state
1354 .pending_mouse_down
1355 .get_or_insert_with(Default::default)
1356 .clone();
1357 let interactive_bounds = interactive_bounds.clone();
1358
1359 cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
1360 if phase != DispatchPhase::Bubble {
1361 return;
1362 }
1363 let is_hovered = interactive_bounds
1364 .visibly_contains(&event.position, cx)
1365 && has_mouse_down.borrow().is_none()
1366 && !cx.has_active_drag();
1367 let mut was_hovered = was_hovered.borrow_mut();
1368
1369 if is_hovered != was_hovered.clone() {
1370 *was_hovered = is_hovered;
1371 drop(was_hovered);
1372
1373 hover_listener(&is_hovered, cx);
1374 }
1375 });
1376 }
1377
1378 if let Some(tooltip_builder) = self.tooltip_builder.take() {
1379 let active_tooltip = element_state
1380 .active_tooltip
1381 .get_or_insert_with(Default::default)
1382 .clone();
1383 let pending_mouse_down = element_state
1384 .pending_mouse_down
1385 .get_or_insert_with(Default::default)
1386 .clone();
1387 let interactive_bounds = interactive_bounds.clone();
1388
1389 cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
1390 let is_hovered = interactive_bounds
1391 .visibly_contains(&event.position, cx)
1392 && pending_mouse_down.borrow().is_none();
1393 if !is_hovered {
1394 active_tooltip.borrow_mut().take();
1395 return;
1396 }
1397
1398 if phase != DispatchPhase::Bubble {
1399 return;
1400 }
1401
1402 if active_tooltip.borrow().is_none() {
1403 let task = cx.spawn({
1404 let active_tooltip = active_tooltip.clone();
1405 let tooltip_builder = tooltip_builder.clone();
1406
1407 move |mut cx| async move {
1408 cx.background_executor().timer(TOOLTIP_DELAY).await;
1409 cx.update(|_, cx| {
1410 active_tooltip.borrow_mut().replace(
1411 ActiveTooltip {
1412 tooltip: Some(AnyTooltip {
1413 view: tooltip_builder(cx),
1414 cursor_offset: cx.mouse_position(),
1415 }),
1416 _task: None,
1417 },
1418 );
1419 cx.notify();
1420 })
1421 .ok();
1422 }
1423 });
1424 active_tooltip.borrow_mut().replace(ActiveTooltip {
1425 tooltip: None,
1426 _task: Some(task),
1427 });
1428 }
1429 });
1430
1431 let active_tooltip = element_state
1432 .active_tooltip
1433 .get_or_insert_with(Default::default)
1434 .clone();
1435 cx.on_mouse_event(move |_: &MouseDownEvent, _, _| {
1436 active_tooltip.borrow_mut().take();
1437 });
1438
1439 if let Some(active_tooltip) = element_state
1440 .active_tooltip
1441 .get_or_insert_with(Default::default)
1442 .borrow()
1443 .as_ref()
1444 {
1445 if active_tooltip.tooltip.is_some() {
1446 cx.active_tooltip = active_tooltip.tooltip.clone()
1447 }
1448 }
1449 }
1450
1451 let active_state = element_state
1452 .clicked_state
1453 .get_or_insert_with(Default::default)
1454 .clone();
1455 if active_state.borrow().is_clicked() {
1456 cx.on_mouse_event(move |_: &MouseUpEvent, phase, cx| {
1457 if phase == DispatchPhase::Capture {
1458 *active_state.borrow_mut() = ElementClickedState::default();
1459 cx.notify();
1460 }
1461 });
1462 } else {
1463 let active_group_bounds = self
1464 .group_active_style
1465 .as_ref()
1466 .and_then(|group_active| GroupBounds::get(&group_active.group, cx));
1467 let interactive_bounds = interactive_bounds.clone();
1468 cx.on_mouse_event(move |down: &MouseDownEvent, phase, cx| {
1469 if phase == DispatchPhase::Bubble && !cx.default_prevented() {
1470 let group = active_group_bounds
1471 .map_or(false, |bounds| bounds.contains(&down.position));
1472 let element =
1473 interactive_bounds.visibly_contains(&down.position, cx);
1474 if group || element {
1475 *active_state.borrow_mut() =
1476 ElementClickedState { group, element };
1477 cx.notify();
1478 }
1479 }
1480 });
1481 }
1482
1483 let overflow = style.overflow;
1484 if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
1485 if let Some(scroll_handle) = &self.scroll_handle {
1486 scroll_handle.0.borrow_mut().overflow = overflow;
1487 }
1488
1489 let scroll_offset = element_state
1490 .scroll_offset
1491 .get_or_insert_with(Rc::default)
1492 .clone();
1493 let line_height = cx.line_height();
1494 let scroll_max = (content_size - bounds.size).max(&Size::default());
1495 // Clamp scroll offset in case scroll max is smaller now (e.g., if children
1496 // were removed or the bounds became larger).
1497 {
1498 let mut scroll_offset = scroll_offset.borrow_mut();
1499 scroll_offset.x = scroll_offset.x.clamp(-scroll_max.width, px(0.));
1500 scroll_offset.y = scroll_offset.y.clamp(-scroll_max.height, px(0.));
1501 }
1502
1503 let interactive_bounds = interactive_bounds.clone();
1504 cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
1505 if phase == DispatchPhase::Bubble
1506 && interactive_bounds.visibly_contains(&event.position, cx)
1507 {
1508 let mut scroll_offset = scroll_offset.borrow_mut();
1509 let old_scroll_offset = *scroll_offset;
1510 let delta = event.delta.pixel_delta(line_height);
1511
1512 if overflow.x == Overflow::Scroll {
1513 let mut delta_x = Pixels::ZERO;
1514 if !delta.x.is_zero() {
1515 delta_x = delta.x;
1516 } else if overflow.y != Overflow::Scroll {
1517 delta_x = delta.y;
1518 }
1519
1520 scroll_offset.x = (scroll_offset.x + delta_x)
1521 .clamp(-scroll_max.width, px(0.));
1522 }
1523
1524 if overflow.y == Overflow::Scroll {
1525 let mut delta_y = Pixels::ZERO;
1526 if !delta.y.is_zero() {
1527 delta_y = delta.y;
1528 } else if overflow.x != Overflow::Scroll {
1529 delta_y = delta.x;
1530 }
1531
1532 scroll_offset.y = (scroll_offset.y + delta_y)
1533 .clamp(-scroll_max.height, px(0.));
1534 }
1535
1536 if *scroll_offset != old_scroll_offset {
1537 cx.notify();
1538 cx.stop_propagation();
1539 }
1540 }
1541 });
1542 }
1543
1544 if let Some(group) = self.group.clone() {
1545 GroupBounds::push(group, bounds, cx);
1546 }
1547
1548 let scroll_offset = element_state
1549 .scroll_offset
1550 .as_ref()
1551 .map(|scroll_offset| *scroll_offset.borrow());
1552
1553 let key_down_listeners = mem::take(&mut self.key_down_listeners);
1554 let key_up_listeners = mem::take(&mut self.key_up_listeners);
1555 let action_listeners = mem::take(&mut self.action_listeners);
1556 cx.with_key_dispatch(
1557 self.key_context.clone(),
1558 element_state.focus_handle.clone(),
1559 |_, cx| {
1560 for listener in key_down_listeners {
1561 cx.on_key_event(move |event: &KeyDownEvent, phase, cx| {
1562 listener(event, phase, cx);
1563 })
1564 }
1565
1566 for listener in key_up_listeners {
1567 cx.on_key_event(move |event: &KeyUpEvent, phase, cx| {
1568 listener(event, phase, cx);
1569 })
1570 }
1571
1572 for (action_type, listener) in action_listeners {
1573 cx.on_action(action_type, listener)
1574 }
1575
1576 f(&style, scroll_offset.unwrap_or_default(), cx)
1577 },
1578 );
1579
1580 if let Some(group) = self.group.as_ref() {
1581 GroupBounds::pop(group, cx);
1582 }
1583 });
1584 });
1585 });
1586 });
1587 }
1588
1589 pub fn compute_style(
1590 &self,
1591 bounds: Option<Bounds<Pixels>>,
1592 element_state: &mut InteractiveElementState,
1593 cx: &mut WindowContext,
1594 ) -> Style {
1595 let mut style = Style::default();
1596 style.refine(&self.base_style);
1597
1598 cx.with_z_index(style.z_index.unwrap_or(0), |cx| {
1599 if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
1600 if let Some(in_focus_style) = self.in_focus_style.as_ref() {
1601 if focus_handle.within_focused(cx) {
1602 style.refine(in_focus_style);
1603 }
1604 }
1605
1606 if let Some(focus_style) = self.focus_style.as_ref() {
1607 if focus_handle.is_focused(cx) {
1608 style.refine(focus_style);
1609 }
1610 }
1611 }
1612
1613 if let Some(bounds) = bounds {
1614 let mouse_position = cx.mouse_position();
1615 if !cx.has_active_drag() {
1616 if let Some(group_hover) = self.group_hover_style.as_ref() {
1617 if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
1618 if group_bounds.contains(&mouse_position)
1619 && cx.was_top_layer(&mouse_position, cx.stacking_order())
1620 {
1621 style.refine(&group_hover.style);
1622 }
1623 }
1624 }
1625
1626 if let Some(hover_style) = self.hover_style.as_ref() {
1627 if bounds
1628 .intersect(&cx.content_mask().bounds)
1629 .contains(&mouse_position)
1630 && cx.was_top_layer(&mouse_position, cx.stacking_order())
1631 {
1632 style.refine(hover_style);
1633 }
1634 }
1635 }
1636
1637 if let Some(drag) = cx.active_drag.take() {
1638 let mut can_drop = true;
1639 if let Some(can_drop_predicate) = &self.can_drop_predicate {
1640 can_drop = can_drop_predicate(drag.value.as_ref(), cx);
1641 }
1642
1643 if can_drop {
1644 for (state_type, group_drag_style) in &self.group_drag_over_styles {
1645 if let Some(group_bounds) =
1646 GroupBounds::get(&group_drag_style.group, cx)
1647 {
1648 if *state_type == drag.value.as_ref().type_id()
1649 && group_bounds.contains(&mouse_position)
1650 {
1651 style.refine(&group_drag_style.style);
1652 }
1653 }
1654 }
1655
1656 for (state_type, drag_over_style) in &self.drag_over_styles {
1657 if *state_type == drag.value.as_ref().type_id()
1658 && bounds
1659 .intersect(&cx.content_mask().bounds)
1660 .contains(&mouse_position)
1661 && cx.was_top_layer_under_active_drag(
1662 &mouse_position,
1663 cx.stacking_order(),
1664 )
1665 {
1666 style.refine(drag_over_style);
1667 }
1668 }
1669 }
1670
1671 cx.active_drag = Some(drag);
1672 }
1673 }
1674
1675 let clicked_state = element_state
1676 .clicked_state
1677 .get_or_insert_with(Default::default)
1678 .borrow();
1679 if clicked_state.group {
1680 if let Some(group) = self.group_active_style.as_ref() {
1681 style.refine(&group.style)
1682 }
1683 }
1684
1685 if let Some(active_style) = self.active_style.as_ref() {
1686 if clicked_state.element {
1687 style.refine(active_style)
1688 }
1689 }
1690 });
1691
1692 style
1693 }
1694}
1695
1696impl Default for Interactivity {
1697 fn default() -> Self {
1698 Self {
1699 element_id: None,
1700 key_context: None,
1701 focusable: false,
1702 tracked_focus_handle: None,
1703 scroll_handle: None,
1704 // scroll_offset: Point::default(),
1705 group: None,
1706 base_style: Box::new(StyleRefinement::default()),
1707 focus_style: None,
1708 in_focus_style: None,
1709 hover_style: None,
1710 group_hover_style: None,
1711 active_style: None,
1712 group_active_style: None,
1713 drag_over_styles: Vec::new(),
1714 group_drag_over_styles: Vec::new(),
1715 mouse_down_listeners: Vec::new(),
1716 mouse_up_listeners: Vec::new(),
1717 mouse_move_listeners: Vec::new(),
1718 scroll_wheel_listeners: Vec::new(),
1719 key_down_listeners: Vec::new(),
1720 key_up_listeners: Vec::new(),
1721 action_listeners: Vec::new(),
1722 drop_listeners: Vec::new(),
1723 can_drop_predicate: None,
1724 click_listeners: Vec::new(),
1725 drag_listener: None,
1726 hover_listener: None,
1727 tooltip_builder: None,
1728 block_mouse: false,
1729
1730 #[cfg(debug_assertions)]
1731 location: None,
1732 }
1733 }
1734}
1735
1736#[derive(Default)]
1737pub struct InteractiveElementState {
1738 pub focus_handle: Option<FocusHandle>,
1739 pub clicked_state: Option<Rc<RefCell<ElementClickedState>>>,
1740 pub hover_state: Option<Rc<RefCell<bool>>>,
1741 pub pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
1742 pub scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
1743 pub active_tooltip: Option<Rc<RefCell<Option<ActiveTooltip>>>>,
1744}
1745
1746pub struct ActiveTooltip {
1747 tooltip: Option<AnyTooltip>,
1748 _task: Option<Task<()>>,
1749}
1750
1751/// Whether or not the element or a group that contains it is clicked by the mouse.
1752#[derive(Copy, Clone, Default, Eq, PartialEq)]
1753pub struct ElementClickedState {
1754 pub group: bool,
1755 pub element: bool,
1756}
1757
1758impl ElementClickedState {
1759 fn is_clicked(&self) -> bool {
1760 self.group || self.element
1761 }
1762}
1763
1764#[derive(Default)]
1765pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
1766
1767impl GroupBounds {
1768 pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
1769 cx.default_global::<Self>()
1770 .0
1771 .get(name)
1772 .and_then(|bounds_stack| bounds_stack.last())
1773 .cloned()
1774 }
1775
1776 pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
1777 cx.default_global::<Self>()
1778 .0
1779 .entry(name)
1780 .or_default()
1781 .push(bounds);
1782 }
1783
1784 pub fn pop(name: &SharedString, cx: &mut AppContext) {
1785 cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
1786 }
1787}
1788
1789pub struct Focusable<E> {
1790 pub element: E,
1791}
1792
1793impl<E: InteractiveElement> FocusableElement for Focusable<E> {}
1794
1795impl<E> InteractiveElement for Focusable<E>
1796where
1797 E: InteractiveElement,
1798{
1799 fn interactivity(&mut self) -> &mut Interactivity {
1800 self.element.interactivity()
1801 }
1802}
1803
1804impl<E: StatefulInteractiveElement> StatefulInteractiveElement for Focusable<E> {}
1805
1806impl<E> Styled for Focusable<E>
1807where
1808 E: Styled,
1809{
1810 fn style(&mut self) -> &mut StyleRefinement {
1811 self.element.style()
1812 }
1813}
1814
1815impl<E> Element for Focusable<E>
1816where
1817 E: Element,
1818{
1819 type State = E::State;
1820
1821 fn layout(
1822 &mut self,
1823 state: Option<Self::State>,
1824 cx: &mut WindowContext,
1825 ) -> (LayoutId, Self::State) {
1826 self.element.layout(state, cx)
1827 }
1828
1829 fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
1830 self.element.paint(bounds, state, cx)
1831 }
1832}
1833
1834impl<E> IntoElement for Focusable<E>
1835where
1836 E: IntoElement,
1837{
1838 type Element = E::Element;
1839
1840 fn element_id(&self) -> Option<ElementId> {
1841 self.element.element_id()
1842 }
1843
1844 fn into_element(self) -> Self::Element {
1845 self.element.into_element()
1846 }
1847}
1848
1849impl<E> ParentElement for Focusable<E>
1850where
1851 E: ParentElement,
1852{
1853 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
1854 self.element.children_mut()
1855 }
1856}
1857
1858pub struct Stateful<E> {
1859 element: E,
1860}
1861
1862impl<E> Styled for Stateful<E>
1863where
1864 E: Styled,
1865{
1866 fn style(&mut self) -> &mut StyleRefinement {
1867 self.element.style()
1868 }
1869}
1870
1871impl<E> StatefulInteractiveElement for Stateful<E>
1872where
1873 E: Element,
1874 Self: InteractiveElement,
1875{
1876}
1877
1878impl<E> InteractiveElement for Stateful<E>
1879where
1880 E: InteractiveElement,
1881{
1882 fn interactivity(&mut self) -> &mut Interactivity {
1883 self.element.interactivity()
1884 }
1885}
1886
1887impl<E: FocusableElement> FocusableElement for Stateful<E> {}
1888
1889impl<E> Element for Stateful<E>
1890where
1891 E: Element,
1892{
1893 type State = E::State;
1894
1895 fn layout(
1896 &mut self,
1897 state: Option<Self::State>,
1898 cx: &mut WindowContext,
1899 ) -> (LayoutId, Self::State) {
1900 self.element.layout(state, cx)
1901 }
1902
1903 fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
1904 self.element.paint(bounds, state, cx)
1905 }
1906}
1907
1908impl<E> IntoElement for Stateful<E>
1909where
1910 E: Element,
1911{
1912 type Element = Self;
1913
1914 fn element_id(&self) -> Option<ElementId> {
1915 self.element.element_id()
1916 }
1917
1918 fn into_element(self) -> Self::Element {
1919 self
1920 }
1921}
1922
1923impl<E> ParentElement for Stateful<E>
1924where
1925 E: ParentElement,
1926{
1927 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
1928 self.element.children_mut()
1929 }
1930}
1931
1932#[derive(Default)]
1933struct ScrollHandleState {
1934 // not great to have the nested rc's...
1935 offset: Rc<RefCell<Point<Pixels>>>,
1936 bounds: Bounds<Pixels>,
1937 child_bounds: Vec<Bounds<Pixels>>,
1938 requested_scroll_top: Option<(usize, Pixels)>,
1939 overflow: Point<Overflow>,
1940}
1941
1942#[derive(Clone)]
1943pub struct ScrollHandle(Rc<RefCell<ScrollHandleState>>);
1944
1945impl ScrollHandle {
1946 pub fn new() -> Self {
1947 Self(Rc::default())
1948 }
1949
1950 pub fn offset(&self) -> Point<Pixels> {
1951 self.0.borrow().offset.borrow().clone()
1952 }
1953
1954 pub fn top_item(&self) -> usize {
1955 let state = self.0.borrow();
1956 let top = state.bounds.top() - state.offset.borrow().y;
1957
1958 match state.child_bounds.binary_search_by(|bounds| {
1959 if top < bounds.top() {
1960 Ordering::Greater
1961 } else if top > bounds.bottom() {
1962 Ordering::Less
1963 } else {
1964 Ordering::Equal
1965 }
1966 }) {
1967 Ok(ix) => ix,
1968 Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)),
1969 }
1970 }
1971
1972 pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
1973 self.0.borrow().child_bounds.get(ix).cloned()
1974 }
1975
1976 /// scroll_to_item scrolls the minimal amount to ensure that the item is
1977 /// fully visible
1978 pub fn scroll_to_item(&self, ix: usize) {
1979 let state = self.0.borrow();
1980
1981 let Some(bounds) = state.child_bounds.get(ix) else {
1982 return;
1983 };
1984
1985 let mut scroll_offset = state.offset.borrow_mut();
1986
1987 if state.overflow.y == Overflow::Scroll {
1988 if bounds.top() + scroll_offset.y < state.bounds.top() {
1989 scroll_offset.y = state.bounds.top() - bounds.top();
1990 } else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() {
1991 scroll_offset.y = state.bounds.bottom() - bounds.bottom();
1992 }
1993 }
1994
1995 if state.overflow.x == Overflow::Scroll {
1996 if bounds.left() + scroll_offset.x < state.bounds.left() {
1997 scroll_offset.x = state.bounds.left() - bounds.left();
1998 } else if bounds.right() + scroll_offset.x > state.bounds.right() {
1999 scroll_offset.x = state.bounds.right() - bounds.right();
2000 }
2001 }
2002 }
2003
2004 pub fn logical_scroll_top(&self) -> (usize, Pixels) {
2005 let ix = self.top_item();
2006 let state = self.0.borrow();
2007
2008 if let Some(child_bounds) = state.child_bounds.get(ix) {
2009 (
2010 ix,
2011 child_bounds.top() + state.offset.borrow().y - state.bounds.top(),
2012 )
2013 } else {
2014 (ix, px(0.))
2015 }
2016 }
2017
2018 pub fn set_logical_scroll_top(&self, ix: usize, px: Pixels) {
2019 self.0.borrow_mut().requested_scroll_top = Some((ix, px));
2020 }
2021}