div.rs

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