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