interactive.rs

   1use crate::{
   2    div, point, px, Action, AnyDrag, AnyTooltip, AnyView, AppContext, BorrowWindow, Bounds,
   3    Component, DispatchContext, DispatchPhase, Div, Element, ElementId, FocusHandle, KeyMatch,
   4    Keystroke, Modifiers, Overflow, Pixels, Point, Render, SharedString, Size, Style,
   5    StyleRefinement, Task, View, ViewContext,
   6};
   7use collections::HashMap;
   8use derive_more::{Deref, DerefMut};
   9use parking_lot::Mutex;
  10use refineable::Refineable;
  11use smallvec::SmallVec;
  12use std::{
  13    any::{Any, TypeId},
  14    fmt::Debug,
  15    marker::PhantomData,
  16    mem,
  17    ops::Deref,
  18    path::PathBuf,
  19    sync::Arc,
  20    time::Duration,
  21};
  22
  23const DRAG_THRESHOLD: f64 = 2.;
  24const TOOLTIP_DELAY: Duration = Duration::from_millis(500);
  25const TOOLTIP_OFFSET: Point<Pixels> = Point::new(px(10.0), px(8.0));
  26
  27pub trait StatelessInteractive<V: 'static>: Element<V> {
  28    fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V>;
  29
  30    fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
  31    where
  32        Self: Sized,
  33    {
  34        self.stateless_interactivity().hover_style = f(StyleRefinement::default());
  35        self
  36    }
  37
  38    fn group_hover(
  39        mut self,
  40        group_name: impl Into<SharedString>,
  41        f: impl FnOnce(StyleRefinement) -> StyleRefinement,
  42    ) -> Self
  43    where
  44        Self: Sized,
  45    {
  46        self.stateless_interactivity().group_hover_style = Some(GroupStyle {
  47            group: group_name.into(),
  48            style: f(StyleRefinement::default()),
  49        });
  50        self
  51    }
  52
  53    fn on_mouse_down(
  54        mut self,
  55        button: MouseButton,
  56        handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
  57    ) -> Self
  58    where
  59        Self: Sized,
  60    {
  61        self.stateless_interactivity()
  62            .mouse_down_listeners
  63            .push(Box::new(move |view, event, bounds, phase, cx| {
  64                if phase == DispatchPhase::Bubble
  65                    && event.button == button
  66                    && bounds.contains_point(&event.position)
  67                {
  68                    handler(view, event, cx)
  69                }
  70            }));
  71        self
  72    }
  73
  74    fn on_mouse_up(
  75        mut self,
  76        button: MouseButton,
  77        handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
  78    ) -> Self
  79    where
  80        Self: Sized,
  81    {
  82        self.stateless_interactivity()
  83            .mouse_up_listeners
  84            .push(Box::new(move |view, event, bounds, phase, cx| {
  85                if phase == DispatchPhase::Bubble
  86                    && event.button == button
  87                    && bounds.contains_point(&event.position)
  88                {
  89                    handler(view, event, cx)
  90                }
  91            }));
  92        self
  93    }
  94
  95    fn on_mouse_down_out(
  96        mut self,
  97        button: MouseButton,
  98        handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
  99    ) -> Self
 100    where
 101        Self: Sized,
 102    {
 103        self.stateless_interactivity()
 104            .mouse_down_listeners
 105            .push(Box::new(move |view, event, bounds, phase, cx| {
 106                if phase == DispatchPhase::Capture
 107                    && event.button == button
 108                    && !bounds.contains_point(&event.position)
 109                {
 110                    handler(view, event, cx)
 111                }
 112            }));
 113        self
 114    }
 115
 116    fn on_mouse_up_out(
 117        mut self,
 118        button: MouseButton,
 119        handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
 120    ) -> Self
 121    where
 122        Self: Sized,
 123    {
 124        self.stateless_interactivity()
 125            .mouse_up_listeners
 126            .push(Box::new(move |view, event, bounds, phase, cx| {
 127                if phase == DispatchPhase::Capture
 128                    && event.button == button
 129                    && !bounds.contains_point(&event.position)
 130                {
 131                    handler(view, event, cx);
 132                }
 133            }));
 134        self
 135    }
 136
 137    fn on_mouse_move(
 138        mut self,
 139        handler: impl Fn(&mut V, &MouseMoveEvent, &mut ViewContext<V>) + 'static,
 140    ) -> Self
 141    where
 142        Self: Sized,
 143    {
 144        self.stateless_interactivity()
 145            .mouse_move_listeners
 146            .push(Box::new(move |view, event, bounds, phase, cx| {
 147                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
 148                    handler(view, event, cx);
 149                }
 150            }));
 151        self
 152    }
 153
 154    fn on_scroll_wheel(
 155        mut self,
 156        handler: impl Fn(&mut V, &ScrollWheelEvent, &mut ViewContext<V>) + 'static,
 157    ) -> Self
 158    where
 159        Self: Sized,
 160    {
 161        self.stateless_interactivity()
 162            .scroll_wheel_listeners
 163            .push(Box::new(move |view, event, bounds, phase, cx| {
 164                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
 165                    handler(view, event, cx);
 166                }
 167            }));
 168        self
 169    }
 170
 171    fn context<C>(mut self, context: C) -> Self
 172    where
 173        Self: Sized,
 174        C: TryInto<DispatchContext>,
 175        C::Error: Debug,
 176    {
 177        self.stateless_interactivity().dispatch_context =
 178            context.try_into().expect("invalid dispatch context");
 179        self
 180    }
 181
 182    /// Capture the given action, fires during the capture phase
 183    fn capture_action<A: 'static>(
 184        mut self,
 185        listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
 186    ) -> Self
 187    where
 188        Self: Sized,
 189    {
 190        self.stateless_interactivity().key_listeners.push((
 191            TypeId::of::<A>(),
 192            Box::new(move |view, event, _, phase, cx| {
 193                let event = event.downcast_ref().unwrap();
 194                if phase == DispatchPhase::Capture {
 195                    listener(view, event, cx)
 196                }
 197                None
 198            }),
 199        ));
 200        self
 201    }
 202
 203    /// Add a listener for the given action, fires during the bubble event phase
 204    fn on_action<A: 'static>(
 205        mut self,
 206        listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
 207    ) -> Self
 208    where
 209        Self: Sized,
 210    {
 211        self.stateless_interactivity().key_listeners.push((
 212            TypeId::of::<A>(),
 213            Box::new(move |view, event, _, phase, cx| {
 214                let event = event.downcast_ref().unwrap();
 215                if phase == DispatchPhase::Bubble {
 216                    listener(view, event, cx)
 217                }
 218
 219                None
 220            }),
 221        ));
 222        self
 223    }
 224
 225    fn on_key_down(
 226        mut self,
 227        listener: impl Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + 'static,
 228    ) -> Self
 229    where
 230        Self: Sized,
 231    {
 232        self.stateless_interactivity().key_listeners.push((
 233            TypeId::of::<KeyDownEvent>(),
 234            Box::new(move |view, event, _, phase, cx| {
 235                let event = event.downcast_ref().unwrap();
 236                listener(view, event, phase, cx);
 237                None
 238            }),
 239        ));
 240        self
 241    }
 242
 243    fn on_key_up(
 244        mut self,
 245        listener: impl Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + 'static,
 246    ) -> Self
 247    where
 248        Self: Sized,
 249    {
 250        self.stateless_interactivity().key_listeners.push((
 251            TypeId::of::<KeyUpEvent>(),
 252            Box::new(move |view, event, _, phase, cx| {
 253                let event = event.downcast_ref().unwrap();
 254                listener(view, event, phase, cx);
 255                None
 256            }),
 257        ));
 258        self
 259    }
 260
 261    fn drag_over<S: 'static>(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
 262    where
 263        Self: Sized,
 264    {
 265        self.stateless_interactivity()
 266            .drag_over_styles
 267            .push((TypeId::of::<S>(), f(StyleRefinement::default())));
 268        self
 269    }
 270
 271    fn group_drag_over<S: 'static>(
 272        mut self,
 273        group_name: impl Into<SharedString>,
 274        f: impl FnOnce(StyleRefinement) -> StyleRefinement,
 275    ) -> Self
 276    where
 277        Self: Sized,
 278    {
 279        self.stateless_interactivity().group_drag_over_styles.push((
 280            TypeId::of::<S>(),
 281            GroupStyle {
 282                group: group_name.into(),
 283                style: f(StyleRefinement::default()),
 284            },
 285        ));
 286        self
 287    }
 288
 289    fn on_drop<W: 'static>(
 290        mut self,
 291        listener: impl Fn(&mut V, View<W>, &mut ViewContext<V>) + 'static,
 292    ) -> Self
 293    where
 294        Self: Sized,
 295    {
 296        self.stateless_interactivity().drop_listeners.push((
 297            TypeId::of::<W>(),
 298            Box::new(move |view, dragged_view, cx| {
 299                listener(view, dragged_view.downcast().unwrap(), cx);
 300            }),
 301        ));
 302        self
 303    }
 304}
 305
 306pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
 307    fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V>;
 308
 309    fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
 310    where
 311        Self: Sized,
 312    {
 313        self.stateful_interactivity().active_style = f(StyleRefinement::default());
 314        self
 315    }
 316
 317    fn group_active(
 318        mut self,
 319        group_name: impl Into<SharedString>,
 320        f: impl FnOnce(StyleRefinement) -> StyleRefinement,
 321    ) -> Self
 322    where
 323        Self: Sized,
 324    {
 325        self.stateful_interactivity().group_active_style = Some(GroupStyle {
 326            group: group_name.into(),
 327            style: f(StyleRefinement::default()),
 328        });
 329        self
 330    }
 331
 332    fn on_click(
 333        mut self,
 334        listener: impl Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static,
 335    ) -> Self
 336    where
 337        Self: Sized,
 338    {
 339        self.stateful_interactivity()
 340            .click_listeners
 341            .push(Box::new(move |view, event, cx| listener(view, event, cx)));
 342        self
 343    }
 344
 345    fn on_drag<W>(
 346        mut self,
 347        listener: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
 348    ) -> Self
 349    where
 350        Self: Sized,
 351        W: 'static + Render,
 352    {
 353        debug_assert!(
 354            self.stateful_interactivity().drag_listener.is_none(),
 355            "calling on_drag more than once on the same element is not supported"
 356        );
 357        self.stateful_interactivity().drag_listener =
 358            Some(Box::new(move |view_state, cursor_offset, cx| AnyDrag {
 359                view: listener(view_state, cx).into(),
 360                cursor_offset,
 361            }));
 362        self
 363    }
 364
 365    fn on_hover(mut self, listener: impl 'static + Fn(&mut V, bool, &mut ViewContext<V>)) -> Self
 366    where
 367        Self: Sized,
 368    {
 369        debug_assert!(
 370            self.stateful_interactivity().hover_listener.is_none(),
 371            "calling on_hover more than once on the same element is not supported"
 372        );
 373        self.stateful_interactivity().hover_listener = Some(Box::new(listener));
 374        self
 375    }
 376
 377    fn tooltip<W>(
 378        mut self,
 379        build_tooltip: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
 380    ) -> Self
 381    where
 382        Self: Sized,
 383        W: 'static + Render,
 384    {
 385        debug_assert!(
 386            self.stateful_interactivity().tooltip_builder.is_none(),
 387            "calling tooltip more than once on the same element is not supported"
 388        );
 389        self.stateful_interactivity().tooltip_builder = Some(Arc::new(move |view_state, cx| {
 390            build_tooltip(view_state, cx).into()
 391        }));
 392
 393        self
 394    }
 395}
 396
 397pub trait ElementInteractivity<V: 'static>: 'static {
 398    fn as_stateless(&self) -> &StatelessInteractivity<V>;
 399    fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V>;
 400    fn as_stateful(&self) -> Option<&StatefulInteractivity<V>>;
 401    fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>>;
 402
 403    fn initialize<R>(
 404        &mut self,
 405        cx: &mut ViewContext<V>,
 406        f: impl FnOnce(&mut ViewContext<V>) -> R,
 407    ) -> R {
 408        if let Some(stateful) = self.as_stateful_mut() {
 409            cx.with_element_id(stateful.id.clone(), |global_id, cx| {
 410                stateful.key_listeners.push((
 411                    TypeId::of::<KeyDownEvent>(),
 412                    Box::new(move |_, key_down, context, phase, cx| {
 413                        if phase == DispatchPhase::Bubble {
 414                            let key_down = key_down.downcast_ref::<KeyDownEvent>().unwrap();
 415                            if let KeyMatch::Some(action) =
 416                                cx.match_keystroke(&global_id, &key_down.keystroke, context)
 417                            {
 418                                return Some(action);
 419                            }
 420                        }
 421
 422                        None
 423                    }),
 424                ));
 425                let result = stateful.stateless.initialize(cx, f);
 426                stateful.key_listeners.pop();
 427                result
 428            })
 429        } else {
 430            let stateless = self.as_stateless_mut();
 431            cx.with_key_dispatch_context(stateless.dispatch_context.clone(), |cx| {
 432                cx.with_key_listeners(mem::take(&mut stateless.key_listeners), f)
 433            })
 434        }
 435    }
 436
 437    fn refine_style(
 438        &self,
 439        style: &mut Style,
 440        bounds: Bounds<Pixels>,
 441        element_state: &InteractiveElementState,
 442        cx: &mut ViewContext<V>,
 443    ) {
 444        let mouse_position = cx.mouse_position();
 445        let stateless = self.as_stateless();
 446        if let Some(group_hover) = stateless.group_hover_style.as_ref() {
 447            if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
 448                if group_bounds.contains_point(&mouse_position) {
 449                    style.refine(&group_hover.style);
 450                }
 451            }
 452        }
 453        if bounds.contains_point(&mouse_position) {
 454            style.refine(&stateless.hover_style);
 455        }
 456
 457        if let Some(drag) = cx.active_drag.take() {
 458            for (state_type, group_drag_style) in &self.as_stateless().group_drag_over_styles {
 459                if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) {
 460                    if *state_type == drag.view.entity_type()
 461                        && group_bounds.contains_point(&mouse_position)
 462                    {
 463                        style.refine(&group_drag_style.style);
 464                    }
 465                }
 466            }
 467
 468            for (state_type, drag_over_style) in &self.as_stateless().drag_over_styles {
 469                if *state_type == drag.view.entity_type() && bounds.contains_point(&mouse_position)
 470                {
 471                    style.refine(drag_over_style);
 472                }
 473            }
 474
 475            cx.active_drag = Some(drag);
 476        }
 477
 478        if let Some(stateful) = self.as_stateful() {
 479            let active_state = element_state.active_state.lock();
 480            if active_state.group {
 481                if let Some(group_style) = stateful.group_active_style.as_ref() {
 482                    style.refine(&group_style.style);
 483                }
 484            }
 485            if active_state.element {
 486                style.refine(&stateful.active_style);
 487            }
 488        }
 489    }
 490
 491    fn paint(
 492        &mut self,
 493        bounds: Bounds<Pixels>,
 494        content_size: Size<Pixels>,
 495        overflow: Point<Overflow>,
 496        element_state: &mut InteractiveElementState,
 497        cx: &mut ViewContext<V>,
 498    ) {
 499        let stateless = self.as_stateless_mut();
 500        for listener in stateless.mouse_down_listeners.drain(..) {
 501            cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
 502                listener(state, event, &bounds, phase, cx);
 503            })
 504        }
 505
 506        for listener in stateless.mouse_up_listeners.drain(..) {
 507            cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
 508                listener(state, event, &bounds, phase, cx);
 509            })
 510        }
 511
 512        for listener in stateless.mouse_move_listeners.drain(..) {
 513            cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
 514                listener(state, event, &bounds, phase, cx);
 515            })
 516        }
 517
 518        for listener in stateless.scroll_wheel_listeners.drain(..) {
 519            cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
 520                listener(state, event, &bounds, phase, cx);
 521            })
 522        }
 523
 524        let hover_group_bounds = stateless
 525            .group_hover_style
 526            .as_ref()
 527            .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
 528
 529        if let Some(group_bounds) = hover_group_bounds {
 530            let hovered = group_bounds.contains_point(&cx.mouse_position());
 531            cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
 532                if phase == DispatchPhase::Capture {
 533                    if group_bounds.contains_point(&event.position) != hovered {
 534                        cx.notify();
 535                    }
 536                }
 537            });
 538        }
 539
 540        if stateless.hover_style.is_some()
 541            || (cx.active_drag.is_some() && !stateless.drag_over_styles.is_empty())
 542        {
 543            let hovered = bounds.contains_point(&cx.mouse_position());
 544            cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
 545                if phase == DispatchPhase::Capture {
 546                    if bounds.contains_point(&event.position) != hovered {
 547                        cx.notify();
 548                    }
 549                }
 550            });
 551        }
 552
 553        if cx.active_drag.is_some() {
 554            let drop_listeners = mem::take(&mut stateless.drop_listeners);
 555            cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| {
 556                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
 557                    if let Some(drag_state_type) =
 558                        cx.active_drag.as_ref().map(|drag| drag.view.entity_type())
 559                    {
 560                        for (drop_state_type, listener) in &drop_listeners {
 561                            if *drop_state_type == drag_state_type {
 562                                let drag = cx
 563                                    .active_drag
 564                                    .take()
 565                                    .expect("checked for type drag state type above");
 566                                listener(view, drag.view.clone(), cx);
 567                                cx.notify();
 568                                cx.stop_propagation();
 569                            }
 570                        }
 571                    }
 572                }
 573            });
 574        }
 575
 576        if let Some(stateful) = self.as_stateful_mut() {
 577            let click_listeners = mem::take(&mut stateful.click_listeners);
 578            let drag_listener = mem::take(&mut stateful.drag_listener);
 579
 580            if !click_listeners.is_empty() || drag_listener.is_some() {
 581                let pending_mouse_down = element_state.pending_mouse_down.clone();
 582                let mouse_down = pending_mouse_down.lock().clone();
 583                if let Some(mouse_down) = mouse_down {
 584                    if let Some(drag_listener) = drag_listener {
 585                        let active_state = element_state.active_state.clone();
 586
 587                        cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
 588                            if cx.active_drag.is_some() {
 589                                if phase == DispatchPhase::Capture {
 590                                    cx.notify();
 591                                }
 592                            } else if phase == DispatchPhase::Bubble
 593                                && bounds.contains_point(&event.position)
 594                                && (event.position - mouse_down.position).magnitude()
 595                                    > DRAG_THRESHOLD
 596                            {
 597                                *active_state.lock() = ActiveState::default();
 598                                let cursor_offset = event.position - bounds.origin;
 599                                let drag = drag_listener(view_state, cursor_offset, cx);
 600                                cx.active_drag = Some(drag);
 601                                cx.notify();
 602                                cx.stop_propagation();
 603                            }
 604                        });
 605                    }
 606
 607                    cx.on_mouse_event(move |view_state, event: &MouseUpEvent, phase, cx| {
 608                        if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position)
 609                        {
 610                            let mouse_click = ClickEvent {
 611                                down: mouse_down.clone(),
 612                                up: event.clone(),
 613                            };
 614                            for listener in &click_listeners {
 615                                listener(view_state, &mouse_click, cx);
 616                            }
 617                        }
 618                        *pending_mouse_down.lock() = None;
 619                    });
 620                } else {
 621                    cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| {
 622                        if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position)
 623                        {
 624                            *pending_mouse_down.lock() = Some(event.clone());
 625                        }
 626                    });
 627                }
 628            }
 629
 630            if let Some(hover_listener) = stateful.hover_listener.take() {
 631                let was_hovered = element_state.hover_state.clone();
 632                let has_mouse_down = element_state.pending_mouse_down.clone();
 633
 634                cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
 635                    if phase != DispatchPhase::Bubble {
 636                        return;
 637                    }
 638                    let is_hovered =
 639                        bounds.contains_point(&event.position) && has_mouse_down.lock().is_none();
 640                    let mut was_hovered = was_hovered.lock();
 641
 642                    if is_hovered != was_hovered.clone() {
 643                        *was_hovered = is_hovered;
 644                        drop(was_hovered);
 645
 646                        hover_listener(view_state, is_hovered, cx);
 647                    }
 648                });
 649            }
 650
 651            if let Some(tooltip_builder) = stateful.tooltip_builder.take() {
 652                let active_tooltip = element_state.active_tooltip.clone();
 653                let pending_mouse_down = element_state.pending_mouse_down.clone();
 654
 655                cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
 656                    if phase != DispatchPhase::Bubble {
 657                        return;
 658                    }
 659
 660                    let is_hovered = bounds.contains_point(&event.position)
 661                        && pending_mouse_down.lock().is_none();
 662                    if !is_hovered {
 663                        active_tooltip.lock().take();
 664                        return;
 665                    }
 666
 667                    if active_tooltip.lock().is_none() {
 668                        let task = cx.spawn({
 669                            let active_tooltip = active_tooltip.clone();
 670                            let tooltip_builder = tooltip_builder.clone();
 671
 672                            move |view, mut cx| async move {
 673                                cx.background_executor().timer(TOOLTIP_DELAY).await;
 674                                view.update(&mut cx, move |view_state, cx| {
 675                                    active_tooltip.lock().replace(ActiveTooltip {
 676                                        waiting: None,
 677                                        tooltip: Some(AnyTooltip {
 678                                            view: tooltip_builder(view_state, cx),
 679                                            cursor_offset: cx.mouse_position() + TOOLTIP_OFFSET,
 680                                        }),
 681                                    });
 682                                    cx.notify();
 683                                })
 684                                .ok();
 685                            }
 686                        });
 687                        active_tooltip.lock().replace(ActiveTooltip {
 688                            waiting: Some(task),
 689                            tooltip: None,
 690                        });
 691                    }
 692                });
 693
 694                if let Some(active_tooltip) = element_state.active_tooltip.lock().as_ref() {
 695                    if active_tooltip.tooltip.is_some() {
 696                        cx.active_tooltip = active_tooltip.tooltip.clone()
 697                    }
 698                }
 699            }
 700
 701            let active_state = element_state.active_state.clone();
 702            if active_state.lock().is_none() {
 703                let active_group_bounds = stateful
 704                    .group_active_style
 705                    .as_ref()
 706                    .and_then(|group_active| GroupBounds::get(&group_active.group, cx));
 707                cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
 708                    if phase == DispatchPhase::Bubble {
 709                        let group = active_group_bounds
 710                            .map_or(false, |bounds| bounds.contains_point(&down.position));
 711                        let element = bounds.contains_point(&down.position);
 712                        if group || element {
 713                            *active_state.lock() = ActiveState { group, element };
 714                            cx.notify();
 715                        }
 716                    }
 717                });
 718            } else {
 719                cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
 720                    if phase == DispatchPhase::Capture {
 721                        *active_state.lock() = ActiveState::default();
 722                        cx.notify();
 723                    }
 724                });
 725            }
 726
 727            if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
 728                let scroll_offset = element_state
 729                    .scroll_offset
 730                    .get_or_insert_with(Arc::default)
 731                    .clone();
 732                let line_height = cx.line_height();
 733                let scroll_max = (content_size - bounds.size).max(&Size::default());
 734
 735                cx.on_mouse_event(move |_, event: &ScrollWheelEvent, phase, cx| {
 736                    if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
 737                        let mut scroll_offset = scroll_offset.lock();
 738                        let old_scroll_offset = *scroll_offset;
 739                        let delta = event.delta.pixel_delta(line_height);
 740
 741                        if overflow.x == Overflow::Scroll {
 742                            scroll_offset.x =
 743                                (scroll_offset.x + delta.x).clamp(-scroll_max.width, px(0.));
 744                        }
 745
 746                        if overflow.y == Overflow::Scroll {
 747                            scroll_offset.y =
 748                                (scroll_offset.y + delta.y).clamp(-scroll_max.height, px(0.));
 749                        }
 750
 751                        if *scroll_offset != old_scroll_offset {
 752                            cx.notify();
 753                            cx.stop_propagation();
 754                        }
 755                    }
 756                });
 757            }
 758        }
 759    }
 760}
 761
 762#[derive(Deref, DerefMut)]
 763pub struct StatefulInteractivity<V> {
 764    pub id: ElementId,
 765    #[deref]
 766    #[deref_mut]
 767    stateless: StatelessInteractivity<V>,
 768    click_listeners: SmallVec<[ClickListener<V>; 2]>,
 769    active_style: StyleRefinement,
 770    group_active_style: Option<GroupStyle>,
 771    drag_listener: Option<DragListener<V>>,
 772    hover_listener: Option<HoverListener<V>>,
 773    tooltip_builder: Option<TooltipBuilder<V>>,
 774}
 775
 776impl<V: 'static> ElementInteractivity<V> for StatefulInteractivity<V> {
 777    fn as_stateful(&self) -> Option<&StatefulInteractivity<V>> {
 778        Some(self)
 779    }
 780
 781    fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>> {
 782        Some(self)
 783    }
 784
 785    fn as_stateless(&self) -> &StatelessInteractivity<V> {
 786        &self.stateless
 787    }
 788
 789    fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V> {
 790        &mut self.stateless
 791    }
 792}
 793
 794impl<V> From<ElementId> for StatefulInteractivity<V> {
 795    fn from(id: ElementId) -> Self {
 796        Self {
 797            id,
 798            stateless: StatelessInteractivity::default(),
 799            click_listeners: SmallVec::new(),
 800            drag_listener: None,
 801            hover_listener: None,
 802            tooltip_builder: None,
 803            active_style: StyleRefinement::default(),
 804            group_active_style: None,
 805        }
 806    }
 807}
 808
 809type DropListener<V> = dyn Fn(&mut V, AnyView, &mut ViewContext<V>) + 'static;
 810
 811pub struct StatelessInteractivity<V> {
 812    pub dispatch_context: DispatchContext,
 813    pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
 814    pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
 815    pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
 816    pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
 817    pub key_listeners: SmallVec<[(TypeId, KeyListener<V>); 32]>,
 818    pub hover_style: StyleRefinement,
 819    pub group_hover_style: Option<GroupStyle>,
 820    drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
 821    group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
 822    drop_listeners: SmallVec<[(TypeId, Box<DropListener<V>>); 2]>,
 823}
 824
 825impl<V> StatelessInteractivity<V> {
 826    pub fn into_stateful(self, id: impl Into<ElementId>) -> StatefulInteractivity<V> {
 827        StatefulInteractivity {
 828            id: id.into(),
 829            stateless: self,
 830            click_listeners: SmallVec::new(),
 831            drag_listener: None,
 832            hover_listener: None,
 833            tooltip_builder: None,
 834            active_style: StyleRefinement::default(),
 835            group_active_style: None,
 836        }
 837    }
 838}
 839
 840pub struct GroupStyle {
 841    pub group: SharedString,
 842    pub style: StyleRefinement,
 843}
 844
 845#[derive(Default)]
 846pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
 847
 848impl GroupBounds {
 849    pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
 850        cx.default_global::<Self>()
 851            .0
 852            .get(name)
 853            .and_then(|bounds_stack| bounds_stack.last())
 854            .cloned()
 855    }
 856
 857    pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
 858        cx.default_global::<Self>()
 859            .0
 860            .entry(name)
 861            .or_default()
 862            .push(bounds);
 863    }
 864
 865    pub fn pop(name: &SharedString, cx: &mut AppContext) {
 866        cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
 867    }
 868}
 869
 870#[derive(Copy, Clone, Default, Eq, PartialEq)]
 871struct ActiveState {
 872    pub group: bool,
 873    pub element: bool,
 874}
 875
 876impl ActiveState {
 877    pub fn is_none(&self) -> bool {
 878        !self.group && !self.element
 879    }
 880}
 881
 882#[derive(Default)]
 883pub struct InteractiveElementState {
 884    active_state: Arc<Mutex<ActiveState>>,
 885    hover_state: Arc<Mutex<bool>>,
 886    pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
 887    scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
 888    active_tooltip: Arc<Mutex<Option<ActiveTooltip>>>,
 889}
 890
 891struct ActiveTooltip {
 892    #[allow(unused)] // used to drop the task
 893    waiting: Option<Task<()>>,
 894    tooltip: Option<AnyTooltip>,
 895}
 896
 897impl InteractiveElementState {
 898    pub fn scroll_offset(&self) -> Option<Point<Pixels>> {
 899        self.scroll_offset
 900            .as_ref()
 901            .map(|offset| offset.lock().clone())
 902    }
 903}
 904
 905impl<V> Default for StatelessInteractivity<V> {
 906    fn default() -> Self {
 907        Self {
 908            dispatch_context: DispatchContext::default(),
 909            mouse_down_listeners: SmallVec::new(),
 910            mouse_up_listeners: SmallVec::new(),
 911            mouse_move_listeners: SmallVec::new(),
 912            scroll_wheel_listeners: SmallVec::new(),
 913            key_listeners: SmallVec::new(),
 914            hover_style: StyleRefinement::default(),
 915            group_hover_style: None,
 916            drag_over_styles: SmallVec::new(),
 917            group_drag_over_styles: SmallVec::new(),
 918            drop_listeners: SmallVec::new(),
 919        }
 920    }
 921}
 922
 923impl<V: 'static> ElementInteractivity<V> for StatelessInteractivity<V> {
 924    fn as_stateful(&self) -> Option<&StatefulInteractivity<V>> {
 925        None
 926    }
 927
 928    fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>> {
 929        None
 930    }
 931
 932    fn as_stateless(&self) -> &StatelessInteractivity<V> {
 933        self
 934    }
 935
 936    fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V> {
 937        self
 938    }
 939}
 940
 941#[derive(Clone, Debug, Eq, PartialEq)]
 942pub struct KeyDownEvent {
 943    pub keystroke: Keystroke,
 944    pub is_held: bool,
 945}
 946
 947#[derive(Clone, Debug)]
 948pub struct KeyUpEvent {
 949    pub keystroke: Keystroke,
 950}
 951
 952#[derive(Clone, Debug, Default)]
 953pub struct ModifiersChangedEvent {
 954    pub modifiers: Modifiers,
 955}
 956
 957impl Deref for ModifiersChangedEvent {
 958    type Target = Modifiers;
 959
 960    fn deref(&self) -> &Self::Target {
 961        &self.modifiers
 962    }
 963}
 964
 965/// The phase of a touch motion event.
 966/// Based on the winit enum of the same name.
 967#[derive(Clone, Copy, Debug)]
 968pub enum TouchPhase {
 969    Started,
 970    Moved,
 971    Ended,
 972}
 973
 974#[derive(Clone, Debug, Default)]
 975pub struct MouseDownEvent {
 976    pub button: MouseButton,
 977    pub position: Point<Pixels>,
 978    pub modifiers: Modifiers,
 979    pub click_count: usize,
 980}
 981
 982#[derive(Clone, Debug, Default)]
 983pub struct MouseUpEvent {
 984    pub button: MouseButton,
 985    pub position: Point<Pixels>,
 986    pub modifiers: Modifiers,
 987    pub click_count: usize,
 988}
 989
 990#[derive(Clone, Debug, Default)]
 991pub struct ClickEvent {
 992    pub down: MouseDownEvent,
 993    pub up: MouseUpEvent,
 994}
 995
 996pub struct Drag<S, R, V, E>
 997where
 998    R: Fn(&mut V, &mut ViewContext<V>) -> E,
 999    V: 'static,
1000    E: Component<()>,
1001{
1002    pub state: S,
1003    pub render_drag_handle: R,
1004    view_type: PhantomData<V>,
1005}
1006
1007impl<S, R, V, E> Drag<S, R, V, E>
1008where
1009    R: Fn(&mut V, &mut ViewContext<V>) -> E,
1010    V: 'static,
1011    E: Component<()>,
1012{
1013    pub fn new(state: S, render_drag_handle: R) -> Self {
1014        Drag {
1015            state,
1016            render_drag_handle,
1017            view_type: PhantomData,
1018        }
1019    }
1020}
1021
1022// impl<S, R, V, E> Render for Drag<S, R, V, E> {
1023//     // fn render(&mut self, cx: ViewContext<Self>) ->
1024// }
1025
1026#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
1027pub enum MouseButton {
1028    Left,
1029    Right,
1030    Middle,
1031    Navigate(NavigationDirection),
1032}
1033
1034impl MouseButton {
1035    pub fn all() -> Vec<Self> {
1036        vec![
1037            MouseButton::Left,
1038            MouseButton::Right,
1039            MouseButton::Middle,
1040            MouseButton::Navigate(NavigationDirection::Back),
1041            MouseButton::Navigate(NavigationDirection::Forward),
1042        ]
1043    }
1044}
1045
1046impl Default for MouseButton {
1047    fn default() -> Self {
1048        Self::Left
1049    }
1050}
1051
1052#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
1053pub enum NavigationDirection {
1054    Back,
1055    Forward,
1056}
1057
1058impl Default for NavigationDirection {
1059    fn default() -> Self {
1060        Self::Back
1061    }
1062}
1063
1064#[derive(Clone, Debug, Default)]
1065pub struct MouseMoveEvent {
1066    pub position: Point<Pixels>,
1067    pub pressed_button: Option<MouseButton>,
1068    pub modifiers: Modifiers,
1069}
1070
1071#[derive(Clone, Debug)]
1072pub struct ScrollWheelEvent {
1073    pub position: Point<Pixels>,
1074    pub delta: ScrollDelta,
1075    pub modifiers: Modifiers,
1076    pub touch_phase: TouchPhase,
1077}
1078
1079impl Deref for ScrollWheelEvent {
1080    type Target = Modifiers;
1081
1082    fn deref(&self) -> &Self::Target {
1083        &self.modifiers
1084    }
1085}
1086
1087#[derive(Clone, Copy, Debug)]
1088pub enum ScrollDelta {
1089    Pixels(Point<Pixels>),
1090    Lines(Point<f32>),
1091}
1092
1093impl Default for ScrollDelta {
1094    fn default() -> Self {
1095        Self::Lines(Default::default())
1096    }
1097}
1098
1099impl ScrollDelta {
1100    pub fn precise(&self) -> bool {
1101        match self {
1102            ScrollDelta::Pixels(_) => true,
1103            ScrollDelta::Lines(_) => false,
1104        }
1105    }
1106
1107    pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
1108        match self {
1109            ScrollDelta::Pixels(delta) => *delta,
1110            ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
1111        }
1112    }
1113}
1114
1115#[derive(Clone, Debug, Default)]
1116pub struct MouseExitEvent {
1117    pub position: Point<Pixels>,
1118    pub pressed_button: Option<MouseButton>,
1119    pub modifiers: Modifiers,
1120}
1121
1122impl Deref for MouseExitEvent {
1123    type Target = Modifiers;
1124
1125    fn deref(&self) -> &Self::Target {
1126        &self.modifiers
1127    }
1128}
1129
1130#[derive(Debug, Clone, Default)]
1131pub struct ExternalPaths(pub(crate) SmallVec<[PathBuf; 2]>);
1132
1133impl Render for ExternalPaths {
1134    type Element = Div<Self>;
1135
1136    fn render(&mut self, _: &mut ViewContext<Self>) -> Self::Element {
1137        div() // Intentionally left empty because the platform will render icons for the dragged files
1138    }
1139}
1140
1141#[derive(Debug, Clone)]
1142pub enum FileDropEvent {
1143    Entered {
1144        position: Point<Pixels>,
1145        files: ExternalPaths,
1146    },
1147    Pending {
1148        position: Point<Pixels>,
1149    },
1150    Submit {
1151        position: Point<Pixels>,
1152    },
1153    Exited,
1154}
1155
1156#[derive(Clone, Debug)]
1157pub enum InputEvent {
1158    KeyDown(KeyDownEvent),
1159    KeyUp(KeyUpEvent),
1160    ModifiersChanged(ModifiersChangedEvent),
1161    MouseDown(MouseDownEvent),
1162    MouseUp(MouseUpEvent),
1163    MouseMove(MouseMoveEvent),
1164    MouseExited(MouseExitEvent),
1165    ScrollWheel(ScrollWheelEvent),
1166    FileDrop(FileDropEvent),
1167}
1168
1169impl InputEvent {
1170    pub fn position(&self) -> Option<Point<Pixels>> {
1171        match self {
1172            InputEvent::KeyDown { .. } => None,
1173            InputEvent::KeyUp { .. } => None,
1174            InputEvent::ModifiersChanged { .. } => None,
1175            InputEvent::MouseDown(event) => Some(event.position),
1176            InputEvent::MouseUp(event) => Some(event.position),
1177            InputEvent::MouseMove(event) => Some(event.position),
1178            InputEvent::MouseExited(event) => Some(event.position),
1179            InputEvent::ScrollWheel(event) => Some(event.position),
1180            InputEvent::FileDrop(FileDropEvent::Exited) => None,
1181            InputEvent::FileDrop(
1182                FileDropEvent::Entered { position, .. }
1183                | FileDropEvent::Pending { position, .. }
1184                | FileDropEvent::Submit { position, .. },
1185            ) => Some(*position),
1186        }
1187    }
1188
1189    pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
1190        match self {
1191            InputEvent::KeyDown { .. } => None,
1192            InputEvent::KeyUp { .. } => None,
1193            InputEvent::ModifiersChanged { .. } => None,
1194            InputEvent::MouseDown(event) => Some(event),
1195            InputEvent::MouseUp(event) => Some(event),
1196            InputEvent::MouseMove(event) => Some(event),
1197            InputEvent::MouseExited(event) => Some(event),
1198            InputEvent::ScrollWheel(event) => Some(event),
1199            InputEvent::FileDrop(event) => Some(event),
1200        }
1201    }
1202
1203    pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> {
1204        match self {
1205            InputEvent::KeyDown(event) => Some(event),
1206            InputEvent::KeyUp(event) => Some(event),
1207            InputEvent::ModifiersChanged(event) => Some(event),
1208            InputEvent::MouseDown(_) => None,
1209            InputEvent::MouseUp(_) => None,
1210            InputEvent::MouseMove(_) => None,
1211            InputEvent::MouseExited(_) => None,
1212            InputEvent::ScrollWheel(_) => None,
1213            InputEvent::FileDrop(_) => None,
1214        }
1215    }
1216}
1217
1218pub struct FocusEvent {
1219    pub blurred: Option<FocusHandle>,
1220    pub focused: Option<FocusHandle>,
1221}
1222
1223pub type MouseDownListener<V> = Box<
1224    dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
1225>;
1226pub type MouseUpListener<V> = Box<
1227    dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
1228>;
1229
1230pub type MouseMoveListener<V> = Box<
1231    dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
1232>;
1233
1234pub type ScrollWheelListener<V> = Box<
1235    dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1236        + 'static,
1237>;
1238
1239pub type ClickListener<V> = Box<dyn Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static>;
1240
1241pub(crate) type DragListener<V> =
1242    Box<dyn Fn(&mut V, Point<Pixels>, &mut ViewContext<V>) -> AnyDrag + 'static>;
1243
1244pub(crate) type HoverListener<V> = Box<dyn Fn(&mut V, bool, &mut ViewContext<V>) + 'static>;
1245
1246pub(crate) type TooltipBuilder<V> = Arc<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static>;
1247
1248pub type KeyListener<V> = Box<
1249    dyn Fn(
1250            &mut V,
1251            &dyn Any,
1252            &[&DispatchContext],
1253            DispatchPhase,
1254            &mut ViewContext<V>,
1255        ) -> Option<Box<dyn Action>>
1256        + 'static,
1257>;