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