div.rs

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