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