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