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