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, Style, StyleRefinement, Styled, Task, View, Visibility, WindowContext,
   7};
   8use collections::HashMap;
   9use refineable::Refineable;
  10use smallvec::SmallVec;
  11use std::{
  12    any::{Any, TypeId},
  13    cell::RefCell,
  14    fmt::Debug,
  15    mem,
  16    rc::Rc,
  17    time::Duration,
  18};
  19use taffy::style::Overflow;
  20use util::ResultExt;
  21
  22const DRAG_THRESHOLD: f64 = 2.;
  23const TOOLTIP_DELAY: Duration = Duration::from_millis(500);
  24
  25pub struct GroupStyle {
  26    pub group: SharedString,
  27    pub style: StyleRefinement,
  28}
  29
  30pub trait InteractiveElement: Sized + Element {
  31    fn interactivity(&mut self) -> &mut Interactivity;
  32
  33    fn group(mut self, group: impl Into<SharedString>) -> Self {
  34        self.interactivity().group = Some(group.into());
  35        self
  36    }
  37
  38    fn id(mut self, id: impl Into<ElementId>) -> Stateful<Self> {
  39        self.interactivity().element_id = Some(id.into());
  40
  41        Stateful { element: self }
  42    }
  43
  44    fn track_focus(mut self, focus_handle: &FocusHandle) -> Focusable<Self> {
  45        self.interactivity().focusable = true;
  46        self.interactivity().tracked_focus_handle = Some(focus_handle.clone());
  47        Focusable { element: self }
  48    }
  49
  50    fn key_context<C, E>(mut self, key_context: C) -> Self
  51    where
  52        C: TryInto<KeyContext, Error = E>,
  53        E: Debug,
  54    {
  55        if let Some(key_context) = key_context.try_into().log_err() {
  56            self.interactivity().key_context = key_context;
  57        }
  58        self
  59    }
  60
  61    fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
  62        self.interactivity().hover_style = f(StyleRefinement::default());
  63        self
  64    }
  65
  66    fn group_hover(
  67        mut self,
  68        group_name: impl Into<SharedString>,
  69        f: impl FnOnce(StyleRefinement) -> StyleRefinement,
  70    ) -> Self {
  71        self.interactivity().group_hover_style = Some(GroupStyle {
  72            group: group_name.into(),
  73            style: f(StyleRefinement::default()),
  74        });
  75        self
  76    }
  77
  78    fn on_mouse_down(
  79        mut self,
  80        button: MouseButton,
  81        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
  82    ) -> Self {
  83        self.interactivity().mouse_down_listeners.push(Box::new(
  84            move |event, bounds, phase, cx| {
  85                if phase == DispatchPhase::Bubble
  86                    && event.button == button
  87                    && bounds.contains_point(&event.position)
  88                {
  89                    (listener)(event, cx)
  90                }
  91            },
  92        ));
  93        self
  94    }
  95
  96    fn on_any_mouse_down(
  97        mut self,
  98        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
  99    ) -> Self {
 100        self.interactivity().mouse_down_listeners.push(Box::new(
 101            move |event, bounds, phase, cx| {
 102                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
 103                    (listener)(event, cx)
 104                }
 105            },
 106        ));
 107        self
 108    }
 109
 110    fn on_mouse_up(
 111        mut self,
 112        button: MouseButton,
 113        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
 114    ) -> Self {
 115        self.interactivity()
 116            .mouse_up_listeners
 117            .push(Box::new(move |event, bounds, phase, cx| {
 118                if phase == DispatchPhase::Bubble
 119                    && event.button == button
 120                    && bounds.contains_point(&event.position)
 121                {
 122                    (listener)(event, cx)
 123                }
 124            }));
 125        self
 126    }
 127
 128    fn on_any_mouse_up(
 129        mut self,
 130        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
 131    ) -> Self {
 132        self.interactivity()
 133            .mouse_up_listeners
 134            .push(Box::new(move |event, bounds, phase, cx| {
 135                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
 136                    (listener)(event, cx)
 137                }
 138            }));
 139        self
 140    }
 141
 142    fn on_mouse_down_out(
 143        mut self,
 144        listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
 145    ) -> Self {
 146        self.interactivity().mouse_down_listeners.push(Box::new(
 147            move |event, bounds, phase, cx| {
 148                if phase == DispatchPhase::Capture && !bounds.contains_point(&event.position) {
 149                    (listener)(event, cx)
 150                }
 151            },
 152        ));
 153        self
 154    }
 155
 156    fn on_mouse_up_out(
 157        mut self,
 158        button: MouseButton,
 159        listener: impl Fn(&MouseUpEvent, &mut WindowContext) + 'static,
 160    ) -> Self {
 161        self.interactivity()
 162            .mouse_up_listeners
 163            .push(Box::new(move |event, bounds, phase, cx| {
 164                if phase == DispatchPhase::Capture
 165                    && event.button == button
 166                    && !bounds.contains_point(&event.position)
 167                {
 168                    (listener)(event, cx);
 169                }
 170            }));
 171        self
 172    }
 173
 174    fn on_mouse_move(
 175        mut self,
 176        listener: impl Fn(&MouseMoveEvent, &mut WindowContext) + 'static,
 177    ) -> Self {
 178        self.interactivity().mouse_move_listeners.push(Box::new(
 179            move |event, bounds, phase, cx| {
 180                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
 181                    (listener)(event, cx);
 182                }
 183            },
 184        ));
 185        self
 186    }
 187
 188    fn on_scroll_wheel(
 189        mut self,
 190        listener: impl Fn(&ScrollWheelEvent, &mut WindowContext) + 'static,
 191    ) -> Self {
 192        self.interactivity().scroll_wheel_listeners.push(Box::new(
 193            move |event, bounds, phase, cx| {
 194                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
 195                    (listener)(event, cx);
 196                }
 197            },
 198        ));
 199        self
 200    }
 201
 202    /// Capture the given action, before normal action dispatch can fire
 203    fn capture_action<A: Action>(
 204        mut self,
 205        listener: impl Fn(&A, &mut WindowContext) + 'static,
 206    ) -> Self {
 207        self.interactivity().action_listeners.push((
 208            TypeId::of::<A>(),
 209            Box::new(move |action, phase, cx| {
 210                let action = action.downcast_ref().unwrap();
 211                if phase == DispatchPhase::Capture {
 212                    (listener)(action, cx)
 213                }
 214            }),
 215        ));
 216        self
 217    }
 218
 219    /// Add a listener for the given action, fires during the bubble event phase
 220    fn on_action<A: Action>(mut self, listener: impl Fn(&A, &mut WindowContext) + 'static) -> Self {
 221        // NOTE: this debug assert has the side-effect of working around
 222        // a bug where a crate consisting only of action definitions does
 223        // not register the actions in debug builds:
 224        //
 225        // https://github.com/rust-lang/rust/issues/47384
 226        // https://github.com/mmastrac/rust-ctor/issues/280
 227        //
 228        // if we are relying on this side-effect still, removing the debug_assert!
 229        // likely breaks the command_palette tests.
 230        // debug_assert!(
 231        //     A::is_registered(),
 232        //     "{:?} is not registered as an action",
 233        //     A::qualified_name()
 234        // );
 235        self.interactivity().action_listeners.push((
 236            TypeId::of::<A>(),
 237            Box::new(move |action, phase, cx| {
 238                let action = action.downcast_ref().unwrap();
 239                if phase == DispatchPhase::Bubble {
 240                    (listener)(action, cx)
 241                }
 242            }),
 243        ));
 244        self
 245    }
 246
 247    fn on_key_down(
 248        mut self,
 249        listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
 250    ) -> Self {
 251        self.interactivity()
 252            .key_down_listeners
 253            .push(Box::new(move |event, phase, cx| {
 254                if phase == DispatchPhase::Bubble {
 255                    (listener)(event, cx)
 256                }
 257            }));
 258        self
 259    }
 260
 261    fn capture_key_down(
 262        mut self,
 263        listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static,
 264    ) -> Self {
 265        self.interactivity()
 266            .key_down_listeners
 267            .push(Box::new(move |event, phase, cx| {
 268                if phase == DispatchPhase::Capture {
 269                    listener(event, cx)
 270                }
 271            }));
 272        self
 273    }
 274
 275    fn on_key_up(mut self, listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static) -> Self {
 276        self.interactivity()
 277            .key_up_listeners
 278            .push(Box::new(move |event, phase, cx| {
 279                if phase == DispatchPhase::Bubble {
 280                    listener(event, cx)
 281                }
 282            }));
 283        self
 284    }
 285
 286    fn capture_key_up(
 287        mut self,
 288        listener: impl Fn(&KeyUpEvent, &mut WindowContext) + 'static,
 289    ) -> Self {
 290        self.interactivity()
 291            .key_up_listeners
 292            .push(Box::new(move |event, phase, cx| {
 293                if phase == DispatchPhase::Capture {
 294                    listener(event, cx)
 295                }
 296            }));
 297        self
 298    }
 299
 300    fn drag_over<S: 'static>(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
 301        self.interactivity()
 302            .drag_over_styles
 303            .push((TypeId::of::<S>(), f(StyleRefinement::default())));
 304        self
 305    }
 306
 307    fn group_drag_over<S: 'static>(
 308        mut self,
 309        group_name: impl Into<SharedString>,
 310        f: impl FnOnce(StyleRefinement) -> StyleRefinement,
 311    ) -> Self {
 312        self.interactivity().group_drag_over_styles.push((
 313            TypeId::of::<S>(),
 314            GroupStyle {
 315                group: group_name.into(),
 316                style: f(StyleRefinement::default()),
 317            },
 318        ));
 319        self
 320    }
 321
 322    fn on_drop<W: 'static>(
 323        mut self,
 324        listener: impl Fn(&View<W>, &mut WindowContext) + 'static,
 325    ) -> Self {
 326        self.interactivity().drop_listeners.push((
 327            TypeId::of::<W>(),
 328            Box::new(move |dragged_view, cx| {
 329                listener(&dragged_view.downcast().unwrap(), cx);
 330            }),
 331        ));
 332        self
 333    }
 334}
 335
 336pub trait StatefulInteractiveElement: InteractiveElement {
 337    fn focusable(mut self) -> Focusable<Self> {
 338        self.interactivity().focusable = true;
 339        Focusable { element: self }
 340    }
 341
 342    fn overflow_scroll(mut self) -> Self {
 343        self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
 344        self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
 345        self
 346    }
 347
 348    fn overflow_x_scroll(mut self) -> Self {
 349        self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
 350        self
 351    }
 352
 353    fn overflow_y_scroll(mut self) -> Self {
 354        self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
 355        self
 356    }
 357
 358    fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
 359    where
 360        Self: Sized,
 361    {
 362        self.interactivity().active_style = f(StyleRefinement::default());
 363        self
 364    }
 365
 366    fn group_active(
 367        mut self,
 368        group_name: impl Into<SharedString>,
 369        f: impl FnOnce(StyleRefinement) -> StyleRefinement,
 370    ) -> Self
 371    where
 372        Self: Sized,
 373    {
 374        self.interactivity().group_active_style = Some(GroupStyle {
 375            group: group_name.into(),
 376            style: f(StyleRefinement::default()),
 377        });
 378        self
 379    }
 380
 381    fn on_click(mut self, listener: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self
 382    where
 383        Self: Sized,
 384    {
 385        self.interactivity()
 386            .click_listeners
 387            .push(Box::new(move |event, cx| listener(event, cx)));
 388        self
 389    }
 390
 391    fn on_drag<W>(mut self, listener: impl Fn(&mut WindowContext) -> View<W> + 'static) -> Self
 392    where
 393        Self: Sized,
 394        W: 'static + Render,
 395    {
 396        debug_assert!(
 397            self.interactivity().drag_listener.is_none(),
 398            "calling on_drag more than once on the same element is not supported"
 399        );
 400        self.interactivity().drag_listener = Some(Box::new(move |cursor_offset, cx| AnyDrag {
 401            view: listener(cx).into(),
 402            cursor_offset,
 403        }));
 404        self
 405    }
 406
 407    fn on_hover(mut self, listener: impl Fn(&bool, &mut WindowContext) + 'static) -> Self
 408    where
 409        Self: Sized,
 410    {
 411        debug_assert!(
 412            self.interactivity().hover_listener.is_none(),
 413            "calling on_hover more than once on the same element is not supported"
 414        );
 415        self.interactivity().hover_listener = Some(Box::new(listener));
 416        self
 417    }
 418
 419    fn tooltip(mut self, build_tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self
 420    where
 421        Self: Sized,
 422    {
 423        debug_assert!(
 424            self.interactivity().tooltip_builder.is_none(),
 425            "calling tooltip more than once on the same element is not supported"
 426        );
 427        self.interactivity().tooltip_builder = Some(Rc::new(build_tooltip));
 428
 429        self
 430    }
 431}
 432
 433pub trait FocusableElement: InteractiveElement {
 434    fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
 435    where
 436        Self: Sized,
 437    {
 438        self.interactivity().focus_style = f(StyleRefinement::default());
 439        self
 440    }
 441
 442    fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
 443    where
 444        Self: Sized,
 445    {
 446        self.interactivity().in_focus_style = f(StyleRefinement::default());
 447        self
 448    }
 449
 450    fn on_focus(mut self, listener: impl Fn(&FocusEvent, &mut WindowContext) + 'static) -> Self
 451    where
 452        Self: Sized,
 453    {
 454        self.interactivity()
 455            .focus_listeners
 456            .push(Box::new(move |focus_handle, event, cx| {
 457                if event.focused.as_ref() == Some(focus_handle) {
 458                    listener(event, cx)
 459                }
 460            }));
 461        self
 462    }
 463
 464    fn on_blur(mut self, listener: impl Fn(&FocusEvent, &mut WindowContext) + 'static) -> Self
 465    where
 466        Self: Sized,
 467    {
 468        self.interactivity()
 469            .focus_listeners
 470            .push(Box::new(move |focus_handle, event, cx| {
 471                if event.blurred.as_ref() == Some(focus_handle) {
 472                    listener(event, cx)
 473                }
 474            }));
 475        self
 476    }
 477
 478    fn on_focus_in(mut self, listener: impl Fn(&FocusEvent, &mut WindowContext) + 'static) -> Self
 479    where
 480        Self: Sized,
 481    {
 482        self.interactivity()
 483            .focus_listeners
 484            .push(Box::new(move |focus_handle, event, cx| {
 485                let descendant_blurred = event
 486                    .blurred
 487                    .as_ref()
 488                    .map_or(false, |blurred| focus_handle.contains(blurred, cx));
 489                let descendant_focused = event
 490                    .focused
 491                    .as_ref()
 492                    .map_or(false, |focused| focus_handle.contains(focused, cx));
 493
 494                if !descendant_blurred && descendant_focused {
 495                    listener(event, cx)
 496                }
 497            }));
 498        self
 499    }
 500
 501    fn on_focus_out(mut self, listener: impl Fn(&FocusEvent, &mut WindowContext) + 'static) -> Self
 502    where
 503        Self: Sized,
 504    {
 505        self.interactivity()
 506            .focus_listeners
 507            .push(Box::new(move |focus_handle, event, cx| {
 508                let descendant_blurred = event
 509                    .blurred
 510                    .as_ref()
 511                    .map_or(false, |blurred| focus_handle.contains(blurred, cx));
 512                let descendant_focused = event
 513                    .focused
 514                    .as_ref()
 515                    .map_or(false, |focused| focus_handle.contains(focused, cx));
 516                if descendant_blurred && !descendant_focused {
 517                    listener(event, cx)
 518                }
 519            }));
 520        self
 521    }
 522}
 523
 524pub type FocusListeners = SmallVec<[FocusListener; 2]>;
 525
 526pub type FocusListener = Box<dyn Fn(&FocusHandle, &FocusEvent, &mut WindowContext) + 'static>;
 527
 528pub type MouseDownListener =
 529    Box<dyn Fn(&MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut WindowContext) + 'static>;
 530pub type MouseUpListener =
 531    Box<dyn Fn(&MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut WindowContext) + 'static>;
 532
 533pub type MouseMoveListener =
 534    Box<dyn Fn(&MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut WindowContext) + 'static>;
 535
 536pub type ScrollWheelListener =
 537    Box<dyn Fn(&ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut WindowContext) + 'static>;
 538
 539pub type ClickListener = Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>;
 540
 541pub type DragListener = Box<dyn Fn(Point<Pixels>, &mut WindowContext) -> AnyDrag + 'static>;
 542
 543type DropListener = dyn Fn(AnyView, &mut WindowContext) + 'static;
 544
 545pub type TooltipBuilder = Rc<dyn Fn(&mut WindowContext) -> AnyView + 'static>;
 546
 547pub type KeyDownListener = Box<dyn Fn(&KeyDownEvent, DispatchPhase, &mut WindowContext) + 'static>;
 548
 549pub type KeyUpListener = Box<dyn Fn(&KeyUpEvent, DispatchPhase, &mut WindowContext) + 'static>;
 550
 551pub type ActionListener = Box<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
 552
 553pub fn div() -> Div {
 554    Div {
 555        interactivity: Interactivity::default(),
 556        children: SmallVec::default(),
 557    }
 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 mut interactivity = mem::take(&mut self.interactivity);
 593        let (layout_id, interactive_state) = interactivity.layout(
 594            element_state.map(|s| s.interactive_state),
 595            cx,
 596            |style, cx| {
 597                cx.with_text_style(style.text_style().cloned(), |cx| {
 598                    child_layout_ids = self
 599                        .children
 600                        .iter_mut()
 601                        .map(|child| child.layout(cx))
 602                        .collect::<SmallVec<_>>();
 603                    cx.request_layout(&style, child_layout_ids.iter().copied())
 604                })
 605            },
 606        );
 607        self.interactivity = interactivity;
 608        (
 609            layout_id,
 610            DivState {
 611                interactive_state,
 612                child_layout_ids,
 613            },
 614        )
 615    }
 616
 617    fn paint(
 618        self,
 619        bounds: Bounds<Pixels>,
 620        element_state: &mut Self::State,
 621        cx: &mut WindowContext,
 622    ) {
 623        let mut child_min = point(Pixels::MAX, Pixels::MAX);
 624        let mut child_max = Point::default();
 625        let content_size = if element_state.child_layout_ids.is_empty() {
 626            bounds.size
 627        } else {
 628            for child_layout_id in &element_state.child_layout_ids {
 629                let child_bounds = cx.layout_bounds(*child_layout_id);
 630                child_min = child_min.min(&child_bounds.origin);
 631                child_max = child_max.max(&child_bounds.lower_right());
 632            }
 633            (child_max - child_min).into()
 634        };
 635
 636        self.interactivity.paint(
 637            bounds,
 638            content_size,
 639            &mut element_state.interactive_state,
 640            cx,
 641            |style, scroll_offset, cx| {
 642                if style.visibility == Visibility::Hidden {
 643                    return;
 644                }
 645
 646                let z_index = style.z_index.unwrap_or(0);
 647
 648                cx.with_z_index(z_index, |cx| {
 649                    cx.with_z_index(0, |cx| {
 650                        style.paint(bounds, cx);
 651                    });
 652                    cx.with_z_index(1, |cx| {
 653                        cx.with_text_style(style.text_style().cloned(), |cx| {
 654                            cx.with_content_mask(style.overflow_mask(bounds), |cx| {
 655                                cx.with_element_offset(scroll_offset, |cx| {
 656                                    for child in self.children {
 657                                        child.paint(cx);
 658                                    }
 659                                })
 660                            })
 661                        })
 662                    })
 663                })
 664            },
 665        );
 666    }
 667}
 668
 669impl IntoElement for Div {
 670    type Element = Self;
 671
 672    fn element_id(&self) -> Option<ElementId> {
 673        self.interactivity.element_id.clone()
 674    }
 675
 676    fn into_element(self) -> Self::Element {
 677        self
 678    }
 679}
 680
 681pub struct DivState {
 682    child_layout_ids: SmallVec<[LayoutId; 4]>,
 683    interactive_state: InteractiveElementState,
 684}
 685
 686impl DivState {
 687    pub fn is_active(&self) -> bool {
 688        self.interactive_state.pending_mouse_down.borrow().is_some()
 689    }
 690}
 691
 692pub struct Interactivity {
 693    pub element_id: Option<ElementId>,
 694    pub key_context: KeyContext,
 695    pub focusable: bool,
 696    pub tracked_focus_handle: Option<FocusHandle>,
 697    pub focus_listeners: FocusListeners,
 698    pub group: Option<SharedString>,
 699    pub base_style: StyleRefinement,
 700    pub focus_style: StyleRefinement,
 701    pub in_focus_style: StyleRefinement,
 702    pub hover_style: StyleRefinement,
 703    pub group_hover_style: Option<GroupStyle>,
 704    pub active_style: StyleRefinement,
 705    pub group_active_style: Option<GroupStyle>,
 706    pub drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
 707    pub group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
 708    pub mouse_down_listeners: SmallVec<[MouseDownListener; 2]>,
 709    pub mouse_up_listeners: SmallVec<[MouseUpListener; 2]>,
 710    pub mouse_move_listeners: SmallVec<[MouseMoveListener; 2]>,
 711    pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener; 2]>,
 712    pub key_down_listeners: SmallVec<[KeyDownListener; 2]>,
 713    pub key_up_listeners: SmallVec<[KeyUpListener; 2]>,
 714    pub action_listeners: SmallVec<[(TypeId, ActionListener); 8]>,
 715    pub drop_listeners: SmallVec<[(TypeId, Box<DropListener>); 2]>,
 716    pub click_listeners: SmallVec<[ClickListener; 2]>,
 717    pub drag_listener: Option<DragListener>,
 718    pub hover_listener: Option<Box<dyn Fn(&bool, &mut WindowContext)>>,
 719    pub tooltip_builder: Option<TooltipBuilder>,
 720}
 721
 722impl Interactivity {
 723    pub fn layout(
 724        &mut self,
 725        element_state: Option<InteractiveElementState>,
 726        cx: &mut WindowContext,
 727        f: impl FnOnce(Style, &mut WindowContext) -> LayoutId,
 728    ) -> (LayoutId, InteractiveElementState) {
 729        let mut element_state = element_state.unwrap_or_default();
 730
 731        // Ensure we store a focus handle in our element state if we're focusable.
 732        // If there's an explicit focus handle we're tracking, use that. Otherwise
 733        // create a new handle and store it in the element state, which lives for as
 734        // as frames contain an element with this id.
 735        if self.focusable {
 736            element_state.focus_handle.get_or_insert_with(|| {
 737                self.tracked_focus_handle
 738                    .clone()
 739                    .unwrap_or_else(|| cx.focus_handle())
 740            });
 741        }
 742
 743        let style = self.compute_style(None, &mut element_state, cx);
 744        let layout_id = f(style, cx);
 745        (layout_id, element_state)
 746    }
 747
 748    pub fn paint(
 749        mut self,
 750        bounds: Bounds<Pixels>,
 751        content_size: Size<Pixels>,
 752        element_state: &mut InteractiveElementState,
 753        cx: &mut WindowContext,
 754        f: impl FnOnce(Style, Point<Pixels>, &mut WindowContext),
 755    ) {
 756        let style = self.compute_style(Some(bounds), element_state, cx);
 757
 758        if style
 759            .background
 760            .as_ref()
 761            .is_some_and(|fill| fill.color().is_some_and(|color| !color.is_transparent()))
 762        {
 763            cx.with_z_index(style.z_index.unwrap_or(0), |cx| cx.add_opaque_layer(bounds))
 764        }
 765
 766        if let Some(mouse_cursor) = style.mouse_cursor {
 767            let hovered = bounds.contains_point(&cx.mouse_position());
 768            if hovered {
 769                cx.set_cursor_style(mouse_cursor);
 770            }
 771        }
 772
 773        for listener in self.mouse_down_listeners.drain(..) {
 774            cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
 775                listener(event, &bounds, phase, cx);
 776            })
 777        }
 778
 779        for listener in self.mouse_up_listeners.drain(..) {
 780            cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
 781                listener(event, &bounds, phase, cx);
 782            })
 783        }
 784
 785        for listener in self.mouse_move_listeners.drain(..) {
 786            cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
 787                listener(event, &bounds, phase, cx);
 788            })
 789        }
 790
 791        for listener in self.scroll_wheel_listeners.drain(..) {
 792            cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
 793                listener(event, &bounds, phase, cx);
 794            })
 795        }
 796
 797        let hover_group_bounds = self
 798            .group_hover_style
 799            .as_ref()
 800            .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
 801
 802        if let Some(group_bounds) = hover_group_bounds {
 803            let hovered = group_bounds.contains_point(&cx.mouse_position());
 804            cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
 805                if phase == DispatchPhase::Capture {
 806                    if group_bounds.contains_point(&event.position) != hovered {
 807                        cx.notify();
 808                    }
 809                }
 810            });
 811        }
 812
 813        if self.hover_style.is_some()
 814            || (cx.active_drag.is_some() && !self.drag_over_styles.is_empty())
 815        {
 816            let hovered = bounds.contains_point(&cx.mouse_position());
 817            cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
 818                if phase == DispatchPhase::Capture {
 819                    if bounds.contains_point(&event.position) != hovered {
 820                        cx.notify();
 821                    }
 822                }
 823            });
 824        }
 825
 826        if cx.active_drag.is_some() {
 827            let drop_listeners = mem::take(&mut self.drop_listeners);
 828            cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
 829                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
 830                    if let Some(drag_state_type) =
 831                        cx.active_drag.as_ref().map(|drag| drag.view.entity_type())
 832                    {
 833                        for (drop_state_type, listener) in &drop_listeners {
 834                            if *drop_state_type == drag_state_type {
 835                                let drag = cx
 836                                    .active_drag
 837                                    .take()
 838                                    .expect("checked for type drag state type above");
 839                                listener(drag.view.clone(), cx);
 840                                cx.notify();
 841                                cx.stop_propagation();
 842                            }
 843                        }
 844                    }
 845                }
 846            });
 847        }
 848
 849        let click_listeners = mem::take(&mut self.click_listeners);
 850        let drag_listener = mem::take(&mut self.drag_listener);
 851
 852        if !click_listeners.is_empty() || drag_listener.is_some() {
 853            let pending_mouse_down = element_state.pending_mouse_down.clone();
 854            let mouse_down = pending_mouse_down.borrow().clone();
 855            if let Some(mouse_down) = mouse_down {
 856                if let Some(drag_listener) = drag_listener {
 857                    let active_state = element_state.clicked_state.clone();
 858
 859                    cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
 860                        if cx.active_drag.is_some() {
 861                            if phase == DispatchPhase::Capture {
 862                                cx.notify();
 863                            }
 864                        } else if phase == DispatchPhase::Bubble
 865                            && bounds.contains_point(&event.position)
 866                            && (event.position - mouse_down.position).magnitude() > DRAG_THRESHOLD
 867                        {
 868                            *active_state.borrow_mut() = ElementClickedState::default();
 869                            let cursor_offset = event.position - bounds.origin;
 870                            let drag = drag_listener(cursor_offset, cx);
 871                            cx.active_drag = Some(drag);
 872                            cx.notify();
 873                            cx.stop_propagation();
 874                        }
 875                    });
 876                }
 877
 878                cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
 879                    if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
 880                        let mouse_click = ClickEvent {
 881                            down: mouse_down.clone(),
 882                            up: event.clone(),
 883                        };
 884                        for listener in &click_listeners {
 885                            listener(&mouse_click, cx);
 886                        }
 887                    }
 888                    *pending_mouse_down.borrow_mut() = None;
 889                    cx.notify();
 890                });
 891            } else {
 892                cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
 893                    if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
 894                        *pending_mouse_down.borrow_mut() = Some(event.clone());
 895                        cx.notify();
 896                    }
 897                });
 898            }
 899        }
 900
 901        if let Some(hover_listener) = self.hover_listener.take() {
 902            let was_hovered = element_state.hover_state.clone();
 903            let has_mouse_down = element_state.pending_mouse_down.clone();
 904
 905            cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
 906                if phase != DispatchPhase::Bubble {
 907                    return;
 908                }
 909                let is_hovered =
 910                    bounds.contains_point(&event.position) && has_mouse_down.borrow().is_none();
 911                let mut was_hovered = was_hovered.borrow_mut();
 912
 913                if is_hovered != was_hovered.clone() {
 914                    *was_hovered = is_hovered;
 915                    drop(was_hovered);
 916
 917                    hover_listener(&is_hovered, cx);
 918                }
 919            });
 920        }
 921
 922        if let Some(tooltip_builder) = self.tooltip_builder.take() {
 923            let active_tooltip = element_state.active_tooltip.clone();
 924            let pending_mouse_down = element_state.pending_mouse_down.clone();
 925
 926            cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
 927                if phase != DispatchPhase::Bubble {
 928                    return;
 929                }
 930
 931                let is_hovered =
 932                    bounds.contains_point(&event.position) && pending_mouse_down.borrow().is_none();
 933                if !is_hovered {
 934                    active_tooltip.borrow_mut().take();
 935                    return;
 936                }
 937
 938                if active_tooltip.borrow().is_none() {
 939                    let task = cx.spawn({
 940                        let active_tooltip = active_tooltip.clone();
 941                        let tooltip_builder = tooltip_builder.clone();
 942
 943                        move |mut cx| async move {
 944                            cx.background_executor().timer(TOOLTIP_DELAY).await;
 945                            cx.update(|_, cx| {
 946                                active_tooltip.borrow_mut().replace(ActiveTooltip {
 947                                    tooltip: Some(AnyTooltip {
 948                                        view: tooltip_builder(cx),
 949                                        cursor_offset: cx.mouse_position(),
 950                                    }),
 951                                    _task: None,
 952                                });
 953                                cx.notify();
 954                            })
 955                            .ok();
 956                        }
 957                    });
 958                    active_tooltip.borrow_mut().replace(ActiveTooltip {
 959                        tooltip: None,
 960                        _task: Some(task),
 961                    });
 962                }
 963            });
 964
 965            let active_tooltip = element_state.active_tooltip.clone();
 966            cx.on_mouse_event(move |_: &MouseDownEvent, _, _| {
 967                active_tooltip.borrow_mut().take();
 968            });
 969
 970            if let Some(active_tooltip) = element_state.active_tooltip.borrow().as_ref() {
 971                if active_tooltip.tooltip.is_some() {
 972                    cx.active_tooltip = active_tooltip.tooltip.clone()
 973                }
 974            }
 975        }
 976
 977        let active_state = element_state.clicked_state.clone();
 978        if !active_state.borrow().is_clicked() {
 979            cx.on_mouse_event(move |_: &MouseUpEvent, phase, cx| {
 980                if phase == DispatchPhase::Capture {
 981                    *active_state.borrow_mut() = ElementClickedState::default();
 982                    cx.notify();
 983                }
 984            });
 985        } else {
 986            let active_group_bounds = self
 987                .group_active_style
 988                .as_ref()
 989                .and_then(|group_active| GroupBounds::get(&group_active.group, cx));
 990            cx.on_mouse_event(move |down: &MouseDownEvent, phase, cx| {
 991                if phase == DispatchPhase::Bubble {
 992                    let group = active_group_bounds
 993                        .map_or(false, |bounds| bounds.contains_point(&down.position));
 994                    let element = bounds.contains_point(&down.position);
 995                    if group || element {
 996                        *active_state.borrow_mut() = ElementClickedState { group, element };
 997                        cx.notify();
 998                    }
 999                }
1000            });
1001        }
1002
1003        let overflow = style.overflow;
1004        if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
1005            let scroll_offset = element_state
1006                .scroll_offset
1007                .get_or_insert_with(Rc::default)
1008                .clone();
1009            let line_height = cx.line_height();
1010            let scroll_max = (content_size - bounds.size).max(&Size::default());
1011
1012            cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
1013                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
1014                    let mut scroll_offset = scroll_offset.borrow_mut();
1015                    let old_scroll_offset = *scroll_offset;
1016                    let delta = event.delta.pixel_delta(line_height);
1017
1018                    if overflow.x == Overflow::Scroll {
1019                        scroll_offset.x =
1020                            (scroll_offset.x + delta.x).clamp(-scroll_max.width, px(0.));
1021                    }
1022
1023                    if overflow.y == Overflow::Scroll {
1024                        scroll_offset.y =
1025                            (scroll_offset.y + delta.y).clamp(-scroll_max.height, px(0.));
1026                    }
1027
1028                    if *scroll_offset != old_scroll_offset {
1029                        cx.notify();
1030                        cx.stop_propagation();
1031                    }
1032                }
1033            });
1034        }
1035
1036        if let Some(group) = self.group.clone() {
1037            GroupBounds::push(group, bounds, cx);
1038        }
1039
1040        let scroll_offset = element_state
1041            .scroll_offset
1042            .as_ref()
1043            .map(|scroll_offset| *scroll_offset.borrow());
1044
1045        cx.with_key_dispatch(
1046            self.key_context.clone(),
1047            element_state.focus_handle.clone(),
1048            |_, cx| {
1049                for listener in self.key_down_listeners.drain(..) {
1050                    cx.on_key_event(move |event: &KeyDownEvent, phase, cx| {
1051                        listener(event, phase, cx);
1052                    })
1053                }
1054
1055                for listener in self.key_up_listeners.drain(..) {
1056                    cx.on_key_event(move |event: &KeyUpEvent, phase, cx| {
1057                        listener(event, phase, cx);
1058                    })
1059                }
1060
1061                for (action_type, listener) in self.action_listeners {
1062                    cx.on_action(action_type, listener)
1063                }
1064
1065                if let Some(focus_handle) = element_state.focus_handle.as_ref() {
1066                    for listener in self.focus_listeners {
1067                        let focus_handle = focus_handle.clone();
1068                        cx.on_focus_changed(move |event, cx| listener(&focus_handle, event, cx));
1069                    }
1070                }
1071
1072                f(style, scroll_offset.unwrap_or_default(), cx)
1073            },
1074        );
1075
1076        if let Some(group) = self.group.as_ref() {
1077            GroupBounds::pop(group, cx);
1078        }
1079    }
1080
1081    pub fn compute_style(
1082        &self,
1083        bounds: Option<Bounds<Pixels>>,
1084        element_state: &mut InteractiveElementState,
1085        cx: &mut WindowContext,
1086    ) -> Style {
1087        let mut style = Style::default();
1088        style.refine(&self.base_style);
1089
1090        if let Some(focus_handle) = self.tracked_focus_handle.as_ref() {
1091            if focus_handle.within_focused(cx) {
1092                style.refine(&self.in_focus_style);
1093            }
1094
1095            if focus_handle.is_focused(cx) {
1096                style.refine(&self.focus_style);
1097            }
1098        }
1099
1100        if let Some(bounds) = bounds {
1101            let mouse_position = cx.mouse_position();
1102            if let Some(group_hover) = self.group_hover_style.as_ref() {
1103                if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
1104                    if group_bounds.contains_point(&mouse_position) {
1105                        style.refine(&group_hover.style);
1106                    }
1107                }
1108            }
1109            if self.hover_style.is_some() {
1110                if bounds
1111                    .intersect(&cx.content_mask().bounds)
1112                    .contains_point(&mouse_position)
1113                    && cx.was_top_layer(&mouse_position, cx.stacking_order())
1114                {
1115                    style.refine(&self.hover_style);
1116                }
1117            }
1118
1119            if let Some(drag) = cx.active_drag.take() {
1120                for (state_type, group_drag_style) in &self.group_drag_over_styles {
1121                    if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) {
1122                        if *state_type == drag.view.entity_type()
1123                        // todo!() needs to handle cx.content_mask() and cx.is_top()
1124                            && group_bounds.contains_point(&mouse_position)
1125                        {
1126                            style.refine(&group_drag_style.style);
1127                        }
1128                    }
1129                }
1130
1131                for (state_type, drag_over_style) in &self.drag_over_styles {
1132                    if *state_type == drag.view.entity_type()
1133                        && bounds
1134                            .intersect(&cx.content_mask().bounds)
1135                            .contains_point(&mouse_position)
1136                        && cx.was_top_layer(&mouse_position, cx.stacking_order())
1137                    {
1138                        style.refine(drag_over_style);
1139                    }
1140                }
1141
1142                cx.active_drag = Some(drag);
1143            }
1144        }
1145
1146        let clicked_state = element_state.clicked_state.borrow();
1147        if clicked_state.group {
1148            if let Some(group) = self.group_active_style.as_ref() {
1149                style.refine(&group.style)
1150            }
1151        }
1152
1153        if clicked_state.element {
1154            style.refine(&self.active_style)
1155        }
1156
1157        style
1158    }
1159}
1160
1161impl Default for Interactivity {
1162    fn default() -> Self {
1163        Self {
1164            element_id: None,
1165            key_context: KeyContext::default(),
1166            focusable: false,
1167            tracked_focus_handle: None,
1168            focus_listeners: SmallVec::default(),
1169            // scroll_offset: Point::default(),
1170            group: None,
1171            base_style: StyleRefinement::default(),
1172            focus_style: StyleRefinement::default(),
1173            in_focus_style: StyleRefinement::default(),
1174            hover_style: StyleRefinement::default(),
1175            group_hover_style: None,
1176            active_style: StyleRefinement::default(),
1177            group_active_style: None,
1178            drag_over_styles: SmallVec::new(),
1179            group_drag_over_styles: SmallVec::new(),
1180            mouse_down_listeners: SmallVec::new(),
1181            mouse_up_listeners: SmallVec::new(),
1182            mouse_move_listeners: SmallVec::new(),
1183            scroll_wheel_listeners: SmallVec::new(),
1184            key_down_listeners: SmallVec::new(),
1185            key_up_listeners: SmallVec::new(),
1186            action_listeners: SmallVec::new(),
1187            drop_listeners: SmallVec::new(),
1188            click_listeners: SmallVec::new(),
1189            drag_listener: None,
1190            hover_listener: None,
1191            tooltip_builder: None,
1192        }
1193    }
1194}
1195
1196#[derive(Default)]
1197pub struct InteractiveElementState {
1198    pub focus_handle: Option<FocusHandle>,
1199    pub clicked_state: Rc<RefCell<ElementClickedState>>,
1200    pub hover_state: Rc<RefCell<bool>>,
1201    pub pending_mouse_down: Rc<RefCell<Option<MouseDownEvent>>>,
1202    pub scroll_offset: Option<Rc<RefCell<Point<Pixels>>>>,
1203    pub active_tooltip: Rc<RefCell<Option<ActiveTooltip>>>,
1204}
1205
1206pub struct ActiveTooltip {
1207    tooltip: Option<AnyTooltip>,
1208    _task: Option<Task<()>>,
1209}
1210
1211/// Whether or not the element or a group that contains it is clicked by the mouse.
1212#[derive(Copy, Clone, Default, Eq, PartialEq)]
1213pub struct ElementClickedState {
1214    pub group: bool,
1215    pub element: bool,
1216}
1217
1218impl ElementClickedState {
1219    fn is_clicked(&self) -> bool {
1220        self.group || self.element
1221    }
1222}
1223
1224#[derive(Default)]
1225pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
1226
1227impl GroupBounds {
1228    pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
1229        cx.default_global::<Self>()
1230            .0
1231            .get(name)
1232            .and_then(|bounds_stack| bounds_stack.last())
1233            .cloned()
1234    }
1235
1236    pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
1237        cx.default_global::<Self>()
1238            .0
1239            .entry(name)
1240            .or_default()
1241            .push(bounds);
1242    }
1243
1244    pub fn pop(name: &SharedString, cx: &mut AppContext) {
1245        cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
1246    }
1247}
1248
1249pub struct Focusable<E> {
1250    element: E,
1251}
1252
1253impl<E: InteractiveElement> FocusableElement for Focusable<E> {}
1254
1255impl<E> InteractiveElement for Focusable<E>
1256where
1257    E: InteractiveElement,
1258{
1259    fn interactivity(&mut self) -> &mut Interactivity {
1260        self.element.interactivity()
1261    }
1262}
1263
1264impl<E: StatefulInteractiveElement> StatefulInteractiveElement for Focusable<E> {}
1265
1266impl<E> Styled for Focusable<E>
1267where
1268    E: Styled,
1269{
1270    fn style(&mut self) -> &mut StyleRefinement {
1271        self.element.style()
1272    }
1273}
1274
1275impl<E> Element for Focusable<E>
1276where
1277    E: Element,
1278{
1279    type State = E::State;
1280
1281    fn layout(
1282        &mut self,
1283        state: Option<Self::State>,
1284        cx: &mut WindowContext,
1285    ) -> (LayoutId, Self::State) {
1286        self.element.layout(state, cx)
1287    }
1288
1289    fn paint(self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
1290        self.element.paint(bounds, state, cx)
1291    }
1292}
1293
1294impl<E> IntoElement for Focusable<E>
1295where
1296    E: Element,
1297{
1298    type Element = E;
1299
1300    fn element_id(&self) -> Option<ElementId> {
1301        self.element.element_id()
1302    }
1303
1304    fn into_element(self) -> Self::Element {
1305        self.element
1306    }
1307}
1308
1309impl<E> ParentElement for Focusable<E>
1310where
1311    E: ParentElement,
1312{
1313    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
1314        self.element.children_mut()
1315    }
1316}
1317
1318pub struct Stateful<E> {
1319    element: E,
1320}
1321
1322impl<E> Styled for Stateful<E>
1323where
1324    E: Styled,
1325{
1326    fn style(&mut self) -> &mut StyleRefinement {
1327        self.element.style()
1328    }
1329}
1330
1331impl<E> StatefulInteractiveElement for Stateful<E>
1332where
1333    E: Element,
1334    Self: InteractiveElement,
1335{
1336}
1337
1338impl<E> InteractiveElement for Stateful<E>
1339where
1340    E: InteractiveElement,
1341{
1342    fn interactivity(&mut self) -> &mut Interactivity {
1343        self.element.interactivity()
1344    }
1345}
1346
1347impl<E: FocusableElement> FocusableElement for Stateful<E> {}
1348
1349impl<E> Element for Stateful<E>
1350where
1351    E: Element,
1352{
1353    type State = E::State;
1354
1355    fn layout(
1356        &mut self,
1357        state: Option<Self::State>,
1358        cx: &mut WindowContext,
1359    ) -> (LayoutId, Self::State) {
1360        self.element.layout(state, cx)
1361    }
1362
1363    fn paint(self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
1364        self.element.paint(bounds, state, cx)
1365    }
1366}
1367
1368impl<E> IntoElement for Stateful<E>
1369where
1370    E: Element,
1371{
1372    type Element = Self;
1373
1374    fn element_id(&self) -> Option<ElementId> {
1375        self.element.element_id()
1376    }
1377
1378    fn into_element(self) -> Self::Element {
1379        self
1380    }
1381}
1382
1383impl<E> ParentElement for Stateful<E>
1384where
1385    E: ParentElement,
1386{
1387    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
1388        self.element.children_mut()
1389    }
1390}