interactive.rs

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