div.rs

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