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