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