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