div.rs

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