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