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: 'static + Send + Sync,
 365        R: 'static + Fn(&mut Self::ViewState, &mut ViewContext<Self::ViewState>) -> E + Send + Sync,
 366        E: Element<ViewState = Self::ViewState>,
 367    {
 368        debug_assert!(
 369            self.stateful_interaction().drag_listener.is_none(),
 370            "calling on_drag more than once on the same element is not supported"
 371        );
 372        self.stateful_interaction().drag_listener =
 373            Some(Arc::new(move |view_state, cursor_offset, cx| {
 374                let drag = listener(view_state, cx);
 375                let view_handle = cx.handle().upgrade().unwrap();
 376                let drag_handle_view = view(view_handle, move |view_state, cx| {
 377                    (drag.render_drag_handle)(view_state, cx)
 378                })
 379                .into_any();
 380                AnyDrag {
 381                    drag_handle_view,
 382                    cursor_offset,
 383                    state: Box::new(drag.state),
 384                    state_type: TypeId::of::<S>(),
 385                }
 386            }));
 387        self
 388    }
 389}
 390
 391pub trait ElementInteraction<V: 'static + Send + Sync>: 'static + Send + Sync {
 392    fn as_stateless(&self) -> &StatelessInteraction<V>;
 393    fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V>;
 394    fn as_stateful(&self) -> Option<&StatefulInteraction<V>>;
 395    fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>>;
 396
 397    fn initialize<R>(
 398        &mut self,
 399        cx: &mut ViewContext<V>,
 400        f: impl FnOnce(&mut ViewContext<V>) -> R,
 401    ) -> R {
 402        if let Some(stateful) = self.as_stateful_mut() {
 403            cx.with_element_id(stateful.id.clone(), |global_id, cx| {
 404                stateful.key_listeners.push((
 405                    TypeId::of::<KeyDownEvent>(),
 406                    Arc::new(move |_, key_down, context, phase, cx| {
 407                        if phase == DispatchPhase::Bubble {
 408                            let key_down = key_down.downcast_ref::<KeyDownEvent>().unwrap();
 409                            if let KeyMatch::Some(action) =
 410                                cx.match_keystroke(&global_id, &key_down.keystroke, context)
 411                            {
 412                                return Some(action);
 413                            }
 414                        }
 415
 416                        None
 417                    }),
 418                ));
 419                let result = stateful.stateless.initialize(cx, f);
 420                stateful.key_listeners.pop();
 421                result
 422            })
 423        } else {
 424            let stateless = self.as_stateless();
 425            cx.with_key_dispatch_context(stateless.dispatch_context.clone(), |cx| {
 426                cx.with_key_listeners(&stateless.key_listeners, f)
 427            })
 428        }
 429    }
 430
 431    fn refine_style(
 432        &self,
 433        style: &mut Style,
 434        bounds: Bounds<Pixels>,
 435        element_state: &InteractiveElementState,
 436        cx: &mut ViewContext<V>,
 437    ) {
 438        let mouse_position = cx.mouse_position();
 439        let stateless = self.as_stateless();
 440        if let Some(group_hover) = stateless.group_hover_style.as_ref() {
 441            if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
 442                if group_bounds.contains_point(&mouse_position) {
 443                    style.refine(&group_hover.style);
 444                }
 445            }
 446        }
 447        if bounds.contains_point(&mouse_position) {
 448            style.refine(&stateless.hover_style);
 449        }
 450
 451        if let Some(drag) = cx.active_drag.take() {
 452            for (state_type, group_drag_style) in &self.as_stateless().group_drag_over_styles {
 453                if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) {
 454                    if *state_type == drag.state_type
 455                        && group_bounds.contains_point(&mouse_position)
 456                    {
 457                        style.refine(&group_drag_style.style);
 458                    }
 459                }
 460            }
 461
 462            for (state_type, drag_over_style) in &self.as_stateless().drag_over_styles {
 463                if *state_type == drag.state_type && bounds.contains_point(&mouse_position) {
 464                    style.refine(drag_over_style);
 465                }
 466            }
 467
 468            cx.active_drag = Some(drag);
 469        }
 470
 471        if let Some(stateful) = self.as_stateful() {
 472            let active_state = element_state.active_state.lock();
 473            if active_state.group {
 474                if let Some(group_style) = stateful.group_active_style.as_ref() {
 475                    style.refine(&group_style.style);
 476                }
 477            }
 478            if active_state.element {
 479                style.refine(&stateful.active_style);
 480            }
 481        }
 482    }
 483
 484    fn paint(
 485        &mut self,
 486        bounds: Bounds<Pixels>,
 487        content_size: Size<Pixels>,
 488        overflow: Point<Overflow>,
 489        element_state: &mut InteractiveElementState,
 490        cx: &mut ViewContext<V>,
 491    ) {
 492        let stateless = self.as_stateless();
 493        for listener in stateless.mouse_down_listeners.iter().cloned() {
 494            cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
 495                listener(state, event, &bounds, phase, cx);
 496            })
 497        }
 498
 499        for listener in stateless.mouse_up_listeners.iter().cloned() {
 500            cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
 501                listener(state, event, &bounds, phase, cx);
 502            })
 503        }
 504
 505        for listener in stateless.mouse_move_listeners.iter().cloned() {
 506            cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
 507                listener(state, event, &bounds, phase, cx);
 508            })
 509        }
 510
 511        for listener in stateless.scroll_wheel_listeners.iter().cloned() {
 512            cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
 513                listener(state, event, &bounds, phase, cx);
 514            })
 515        }
 516
 517        let hover_group_bounds = stateless
 518            .group_hover_style
 519            .as_ref()
 520            .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
 521
 522        if let Some(group_bounds) = hover_group_bounds {
 523            let hovered = group_bounds.contains_point(&cx.mouse_position());
 524            cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
 525                if phase == DispatchPhase::Capture {
 526                    if group_bounds.contains_point(&event.position) != hovered {
 527                        cx.notify();
 528                    }
 529                }
 530            });
 531        }
 532
 533        if stateless.hover_style.is_some()
 534            || (cx.active_drag.is_some() && !stateless.drag_over_styles.is_empty())
 535        {
 536            let hovered = bounds.contains_point(&cx.mouse_position());
 537            cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
 538                if phase == DispatchPhase::Capture {
 539                    if bounds.contains_point(&event.position) != hovered {
 540                        cx.notify();
 541                    }
 542                }
 543            });
 544        }
 545
 546        if cx.active_drag.is_some() {
 547            let drop_listeners = stateless.drop_listeners.clone();
 548            cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| {
 549                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
 550                    if let Some(drag_state_type) =
 551                        cx.active_drag.as_ref().map(|drag| drag.state_type)
 552                    {
 553                        for (drop_state_type, listener) in &drop_listeners {
 554                            if *drop_state_type == drag_state_type {
 555                                let drag = cx
 556                                    .active_drag
 557                                    .take()
 558                                    .expect("checked for type drag state type above");
 559                                listener(view, drag.state, cx);
 560                                cx.notify();
 561                                cx.stop_propagation();
 562                            }
 563                        }
 564                    }
 565                }
 566            });
 567        }
 568
 569        if let Some(stateful) = self.as_stateful() {
 570            let click_listeners = stateful.click_listeners.clone();
 571            let drag_listener = stateful.drag_listener.clone();
 572
 573            if !click_listeners.is_empty() || drag_listener.is_some() {
 574                let pending_mouse_down = element_state.pending_mouse_down.clone();
 575                let mouse_down = pending_mouse_down.lock().clone();
 576                if let Some(mouse_down) = mouse_down {
 577                    if let Some(drag_listener) = drag_listener {
 578                        let active_state = element_state.active_state.clone();
 579
 580                        cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
 581                            if cx.active_drag.is_some() {
 582                                if phase == DispatchPhase::Capture {
 583                                    cx.notify();
 584                                }
 585                            } else if phase == DispatchPhase::Bubble
 586                                && bounds.contains_point(&event.position)
 587                                && (event.position - mouse_down.position).magnitude()
 588                                    > DRAG_THRESHOLD
 589                            {
 590                                *active_state.lock() = ActiveState::default();
 591                                let cursor_offset = event.position - bounds.origin;
 592                                let drag = drag_listener(view_state, cursor_offset, cx);
 593                                cx.active_drag = Some(drag);
 594                                cx.notify();
 595                                cx.stop_propagation();
 596                            }
 597                        });
 598                    }
 599
 600                    cx.on_mouse_event(move |view_state, event: &MouseUpEvent, phase, cx| {
 601                        if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position)
 602                        {
 603                            let mouse_click = ClickEvent {
 604                                down: mouse_down.clone(),
 605                                up: event.clone(),
 606                            };
 607                            for listener in &click_listeners {
 608                                listener(view_state, &mouse_click, cx);
 609                            }
 610                        }
 611                        *pending_mouse_down.lock() = None;
 612                    });
 613                } else {
 614                    cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| {
 615                        if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position)
 616                        {
 617                            *pending_mouse_down.lock() = Some(event.clone());
 618                        }
 619                    });
 620                }
 621            }
 622
 623            let active_state = element_state.active_state.clone();
 624            if active_state.lock().is_none() {
 625                let active_group_bounds = stateful
 626                    .group_active_style
 627                    .as_ref()
 628                    .and_then(|group_active| GroupBounds::get(&group_active.group, cx));
 629                cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
 630                    if phase == DispatchPhase::Bubble {
 631                        let group = active_group_bounds
 632                            .map_or(false, |bounds| bounds.contains_point(&down.position));
 633                        let element = bounds.contains_point(&down.position);
 634                        if group || element {
 635                            *active_state.lock() = ActiveState { group, element };
 636                            cx.notify();
 637                        }
 638                    }
 639                });
 640            } else {
 641                cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
 642                    if phase == DispatchPhase::Capture {
 643                        *active_state.lock() = ActiveState::default();
 644                        cx.notify();
 645                    }
 646                });
 647            }
 648
 649            if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
 650                let scroll_offset = element_state
 651                    .scroll_offset
 652                    .get_or_insert_with(Arc::default)
 653                    .clone();
 654                let line_height = cx.line_height();
 655                let scroll_max = (content_size - bounds.size).max(&Size::default());
 656
 657                cx.on_mouse_event(move |_, event: &ScrollWheelEvent, phase, cx| {
 658                    if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
 659                        let mut scroll_offset = scroll_offset.lock();
 660                        let old_scroll_offset = *scroll_offset;
 661                        let delta = event.delta.pixel_delta(line_height);
 662
 663                        if overflow.x == Overflow::Scroll {
 664                            scroll_offset.x =
 665                                (scroll_offset.x + delta.x).clamp(-scroll_max.width, px(0.));
 666                        }
 667
 668                        if overflow.y == Overflow::Scroll {
 669                            scroll_offset.y =
 670                                (scroll_offset.y + delta.y).clamp(-scroll_max.height, px(0.));
 671                        }
 672
 673                        if *scroll_offset != old_scroll_offset {
 674                            cx.notify();
 675                            cx.stop_propagation();
 676                        }
 677                    }
 678                });
 679            }
 680        }
 681    }
 682}
 683
 684#[derive(Deref, DerefMut)]
 685pub struct StatefulInteraction<V: 'static + Send + Sync> {
 686    pub id: ElementId,
 687    #[deref]
 688    #[deref_mut]
 689    stateless: StatelessInteraction<V>,
 690    click_listeners: SmallVec<[ClickListener<V>; 2]>,
 691    active_style: StyleRefinement,
 692    group_active_style: Option<GroupStyle>,
 693    drag_listener: Option<DragListener<V>>,
 694}
 695
 696impl<V> ElementInteraction<V> for StatefulInteraction<V>
 697where
 698    V: 'static + Send + Sync,
 699{
 700    fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
 701        Some(self)
 702    }
 703
 704    fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
 705        Some(self)
 706    }
 707
 708    fn as_stateless(&self) -> &StatelessInteraction<V> {
 709        &self.stateless
 710    }
 711
 712    fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
 713        &mut self.stateless
 714    }
 715}
 716
 717impl<V> From<ElementId> for StatefulInteraction<V>
 718where
 719    V: 'static + Send + Sync,
 720{
 721    fn from(id: ElementId) -> Self {
 722        Self {
 723            id,
 724            stateless: StatelessInteraction::default(),
 725            click_listeners: SmallVec::new(),
 726            drag_listener: None,
 727            active_style: StyleRefinement::default(),
 728            group_active_style: None,
 729        }
 730    }
 731}
 732
 733type DropListener<V> = dyn Fn(&mut V, AnyBox, &mut ViewContext<V>) + Send + Sync;
 734
 735pub struct StatelessInteraction<V> {
 736    pub dispatch_context: DispatchContext,
 737    pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
 738    pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
 739    pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
 740    pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
 741    pub key_listeners: SmallVec<[(TypeId, KeyListener<V>); 32]>,
 742    pub hover_style: StyleRefinement,
 743    pub group_hover_style: Option<GroupStyle>,
 744    drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
 745    group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
 746    drop_listeners: SmallVec<[(TypeId, Arc<DropListener<V>>); 2]>,
 747}
 748
 749impl<V> StatelessInteraction<V>
 750where
 751    V: 'static + Send + Sync,
 752{
 753    pub fn into_stateful(self, id: impl Into<ElementId>) -> StatefulInteraction<V> {
 754        StatefulInteraction {
 755            id: id.into(),
 756            stateless: self,
 757            click_listeners: SmallVec::new(),
 758            drag_listener: None,
 759            active_style: StyleRefinement::default(),
 760            group_active_style: None,
 761        }
 762    }
 763}
 764
 765pub struct GroupStyle {
 766    pub group: SharedString,
 767    pub style: StyleRefinement,
 768}
 769
 770#[derive(Default)]
 771pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
 772
 773impl GroupBounds {
 774    pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
 775        cx.default_global_mut::<Self>()
 776            .0
 777            .get(name)
 778            .and_then(|bounds_stack| bounds_stack.last())
 779            .cloned()
 780    }
 781
 782    pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
 783        cx.default_global_mut::<Self>()
 784            .0
 785            .entry(name)
 786            .or_default()
 787            .push(bounds);
 788    }
 789
 790    pub fn pop(name: &SharedString, cx: &mut AppContext) {
 791        cx.default_global_mut::<Self>()
 792            .0
 793            .get_mut(name)
 794            .unwrap()
 795            .pop();
 796    }
 797}
 798
 799#[derive(Copy, Clone, Default, Eq, PartialEq)]
 800struct ActiveState {
 801    pub group: bool,
 802    pub element: bool,
 803}
 804
 805impl ActiveState {
 806    pub fn is_none(&self) -> bool {
 807        !self.group && !self.element
 808    }
 809}
 810
 811#[derive(Default)]
 812pub struct InteractiveElementState {
 813    active_state: Arc<Mutex<ActiveState>>,
 814    pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
 815    scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
 816}
 817
 818impl InteractiveElementState {
 819    pub fn scroll_offset(&self) -> Option<Point<Pixels>> {
 820        self.scroll_offset
 821            .as_ref()
 822            .map(|offset| offset.lock().clone())
 823    }
 824}
 825
 826impl<V> Default for StatelessInteraction<V> {
 827    fn default() -> Self {
 828        Self {
 829            dispatch_context: DispatchContext::default(),
 830            mouse_down_listeners: SmallVec::new(),
 831            mouse_up_listeners: SmallVec::new(),
 832            mouse_move_listeners: SmallVec::new(),
 833            scroll_wheel_listeners: SmallVec::new(),
 834            key_listeners: SmallVec::new(),
 835            hover_style: StyleRefinement::default(),
 836            group_hover_style: None,
 837            drag_over_styles: SmallVec::new(),
 838            group_drag_over_styles: SmallVec::new(),
 839            drop_listeners: SmallVec::new(),
 840        }
 841    }
 842}
 843
 844impl<V> ElementInteraction<V> for StatelessInteraction<V>
 845where
 846    V: 'static + Send + Sync,
 847{
 848    fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
 849        None
 850    }
 851
 852    fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
 853        None
 854    }
 855
 856    fn as_stateless(&self) -> &StatelessInteraction<V> {
 857        self
 858    }
 859
 860    fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
 861        self
 862    }
 863}
 864
 865#[derive(Clone, Debug, Eq, PartialEq)]
 866pub struct KeyDownEvent {
 867    pub keystroke: Keystroke,
 868    pub is_held: bool,
 869}
 870
 871#[derive(Clone, Debug)]
 872pub struct KeyUpEvent {
 873    pub keystroke: Keystroke,
 874}
 875
 876#[derive(Clone, Debug, Default)]
 877pub struct ModifiersChangedEvent {
 878    pub modifiers: Modifiers,
 879}
 880
 881impl Deref for ModifiersChangedEvent {
 882    type Target = Modifiers;
 883
 884    fn deref(&self) -> &Self::Target {
 885        &self.modifiers
 886    }
 887}
 888
 889/// The phase of a touch motion event.
 890/// Based on the winit enum of the same name.
 891#[derive(Clone, Copy, Debug)]
 892pub enum TouchPhase {
 893    Started,
 894    Moved,
 895    Ended,
 896}
 897
 898#[derive(Clone, Debug, Default)]
 899pub struct MouseDownEvent {
 900    pub button: MouseButton,
 901    pub position: Point<Pixels>,
 902    pub modifiers: Modifiers,
 903    pub click_count: usize,
 904}
 905
 906#[derive(Clone, Debug, Default)]
 907pub struct MouseUpEvent {
 908    pub button: MouseButton,
 909    pub position: Point<Pixels>,
 910    pub modifiers: Modifiers,
 911    pub click_count: usize,
 912}
 913
 914#[derive(Clone, Debug, Default)]
 915pub struct ClickEvent {
 916    pub down: MouseDownEvent,
 917    pub up: MouseUpEvent,
 918}
 919
 920pub struct Drag<S, R, V, E>
 921where
 922    S: 'static + Send + Sync,
 923    R: Fn(&mut V, &mut ViewContext<V>) -> E,
 924    V: 'static + Send + Sync,
 925    E: Element<ViewState = V>,
 926{
 927    pub state: S,
 928    pub render_drag_handle: R,
 929    view_type: PhantomData<V>,
 930}
 931
 932impl<S, R, V, E> Drag<S, R, V, E>
 933where
 934    S: 'static + Send + Sync,
 935    R: Fn(&mut V, &mut ViewContext<V>) -> E + Send + Sync,
 936    V: 'static + Send + Sync,
 937    E: Element<ViewState = V>,
 938{
 939    pub fn new(state: S, render_drag_handle: R) -> Self {
 940        Drag {
 941            state,
 942            render_drag_handle,
 943            view_type: PhantomData,
 944        }
 945    }
 946}
 947
 948#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
 949pub enum MouseButton {
 950    Left,
 951    Right,
 952    Middle,
 953    Navigate(NavigationDirection),
 954}
 955
 956impl MouseButton {
 957    pub fn all() -> Vec<Self> {
 958        vec![
 959            MouseButton::Left,
 960            MouseButton::Right,
 961            MouseButton::Middle,
 962            MouseButton::Navigate(NavigationDirection::Back),
 963            MouseButton::Navigate(NavigationDirection::Forward),
 964        ]
 965    }
 966}
 967
 968impl Default for MouseButton {
 969    fn default() -> Self {
 970        Self::Left
 971    }
 972}
 973
 974#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
 975pub enum NavigationDirection {
 976    Back,
 977    Forward,
 978}
 979
 980impl Default for NavigationDirection {
 981    fn default() -> Self {
 982        Self::Back
 983    }
 984}
 985
 986#[derive(Clone, Debug, Default)]
 987pub struct MouseMoveEvent {
 988    pub position: Point<Pixels>,
 989    pub pressed_button: Option<MouseButton>,
 990    pub modifiers: Modifiers,
 991}
 992
 993#[derive(Clone, Debug)]
 994pub struct ScrollWheelEvent {
 995    pub position: Point<Pixels>,
 996    pub delta: ScrollDelta,
 997    pub modifiers: Modifiers,
 998    pub touch_phase: TouchPhase,
 999}
1000
1001impl Deref for ScrollWheelEvent {
1002    type Target = Modifiers;
1003
1004    fn deref(&self) -> &Self::Target {
1005        &self.modifiers
1006    }
1007}
1008
1009#[derive(Clone, Copy, Debug)]
1010pub enum ScrollDelta {
1011    Pixels(Point<Pixels>),
1012    Lines(Point<f32>),
1013}
1014
1015impl Default for ScrollDelta {
1016    fn default() -> Self {
1017        Self::Lines(Default::default())
1018    }
1019}
1020
1021impl ScrollDelta {
1022    pub fn precise(&self) -> bool {
1023        match self {
1024            ScrollDelta::Pixels(_) => true,
1025            ScrollDelta::Lines(_) => false,
1026        }
1027    }
1028
1029    pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
1030        match self {
1031            ScrollDelta::Pixels(delta) => *delta,
1032            ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
1033        }
1034    }
1035}
1036
1037#[derive(Clone, Debug, Default)]
1038pub struct MouseExitEvent {
1039    pub position: Point<Pixels>,
1040    pub pressed_button: Option<MouseButton>,
1041    pub modifiers: Modifiers,
1042}
1043
1044impl Deref for MouseExitEvent {
1045    type Target = Modifiers;
1046
1047    fn deref(&self) -> &Self::Target {
1048        &self.modifiers
1049    }
1050}
1051
1052#[derive(Debug, Clone, Default)]
1053pub enum FileDropEvent {
1054    #[default]
1055    End,
1056    Pending {
1057        position: Point<Pixels>,
1058    },
1059    Submit {
1060        position: Point<Pixels>,
1061        paths: Vec<PathBuf>,
1062    },
1063}
1064
1065#[derive(Clone, Debug)]
1066pub enum InputEvent {
1067    KeyDown(KeyDownEvent),
1068    KeyUp(KeyUpEvent),
1069    ModifiersChanged(ModifiersChangedEvent),
1070    MouseDown(MouseDownEvent),
1071    MouseUp(MouseUpEvent),
1072    MouseMoved(MouseMoveEvent),
1073    MouseExited(MouseExitEvent),
1074    ScrollWheel(ScrollWheelEvent),
1075    FileDrop(FileDropEvent),
1076}
1077
1078impl InputEvent {
1079    pub fn position(&self) -> Option<Point<Pixels>> {
1080        match self {
1081            InputEvent::KeyDown { .. } => None,
1082            InputEvent::KeyUp { .. } => None,
1083            InputEvent::ModifiersChanged { .. } => None,
1084            InputEvent::MouseDown(event) => Some(event.position),
1085            InputEvent::MouseUp(event) => Some(event.position),
1086            InputEvent::MouseMoved(event) => Some(event.position),
1087            InputEvent::MouseExited(event) => Some(event.position),
1088            InputEvent::ScrollWheel(event) => Some(event.position),
1089            InputEvent::FileDrop(FileDropEvent::End) => None,
1090            InputEvent::FileDrop(
1091                FileDropEvent::Pending { position } | FileDropEvent::Submit { position, .. },
1092            ) => Some(*position),
1093        }
1094    }
1095
1096    pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
1097        match self {
1098            InputEvent::KeyDown { .. } => None,
1099            InputEvent::KeyUp { .. } => None,
1100            InputEvent::ModifiersChanged { .. } => None,
1101            InputEvent::MouseDown(event) => Some(event),
1102            InputEvent::MouseUp(event) => Some(event),
1103            InputEvent::MouseMoved(event) => Some(event),
1104            InputEvent::MouseExited(event) => Some(event),
1105            InputEvent::ScrollWheel(event) => Some(event),
1106            InputEvent::FileDrop(event) => Some(event),
1107        }
1108    }
1109
1110    pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> {
1111        match self {
1112            InputEvent::KeyDown(event) => Some(event),
1113            InputEvent::KeyUp(event) => Some(event),
1114            InputEvent::ModifiersChanged(event) => Some(event),
1115            InputEvent::MouseDown(_) => None,
1116            InputEvent::MouseUp(_) => None,
1117            InputEvent::MouseMoved(_) => None,
1118            InputEvent::MouseExited(_) => None,
1119            InputEvent::ScrollWheel(_) => None,
1120            InputEvent::FileDrop(_) => None,
1121        }
1122    }
1123}
1124
1125pub struct FocusEvent {
1126    pub blurred: Option<FocusHandle>,
1127    pub focused: Option<FocusHandle>,
1128}
1129
1130pub type MouseDownListener<V> = Arc<
1131    dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1132        + Send
1133        + Sync
1134        + 'static,
1135>;
1136pub type MouseUpListener<V> = Arc<
1137    dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1138        + Send
1139        + Sync
1140        + 'static,
1141>;
1142
1143pub type MouseMoveListener<V> = Arc<
1144    dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1145        + Send
1146        + Sync
1147        + 'static,
1148>;
1149
1150pub type ScrollWheelListener<V> = Arc<
1151    dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1152        + Send
1153        + Sync
1154        + 'static,
1155>;
1156
1157pub type ClickListener<V> =
1158    Arc<dyn Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
1159
1160pub(crate) type DragListener<V> =
1161    Arc<dyn Fn(&mut V, Point<Pixels>, &mut ViewContext<V>) -> AnyDrag + Send + Sync + 'static>;
1162
1163pub type KeyListener<V> = Arc<
1164    dyn Fn(
1165            &mut V,
1166            &dyn Any,
1167            &[&DispatchContext],
1168            DispatchPhase,
1169            &mut ViewContext<V>,
1170        ) -> Option<Box<dyn Action>>
1171        + Send
1172        + Sync
1173        + 'static,
1174>;