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    sync::Arc,
  17};
  18
  19const DRAG_THRESHOLD: f64 = 2.;
  20
  21pub trait StatelessInteractive: Element {
  22    fn stateless_interaction(&mut self) -> &mut StatelessInteraction<Self::ViewState>;
  23
  24    fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
  25    where
  26        Self: Sized,
  27    {
  28        self.stateless_interaction().hover_style = f(StyleRefinement::default());
  29        self
  30    }
  31
  32    fn group_hover(
  33        mut self,
  34        group_name: impl Into<SharedString>,
  35        f: impl FnOnce(StyleRefinement) -> StyleRefinement,
  36    ) -> Self
  37    where
  38        Self: Sized,
  39    {
  40        self.stateless_interaction().group_hover_style = Some(GroupStyle {
  41            group: group_name.into(),
  42            style: f(StyleRefinement::default()),
  43        });
  44        self
  45    }
  46
  47    fn on_mouse_down(
  48        mut self,
  49        button: MouseButton,
  50        handler: impl Fn(&mut Self::ViewState, &MouseDownEvent, &mut ViewContext<Self::ViewState>)
  51            + Send
  52            + Sync
  53            + 'static,
  54    ) -> Self
  55    where
  56        Self: Sized,
  57    {
  58        self.stateless_interaction()
  59            .mouse_down_listeners
  60            .push(Arc::new(move |view, event, bounds, phase, cx| {
  61                if phase == DispatchPhase::Bubble
  62                    && event.button == button
  63                    && bounds.contains_point(&event.position)
  64                {
  65                    handler(view, event, cx)
  66                }
  67            }));
  68        self
  69    }
  70
  71    fn on_mouse_up(
  72        mut self,
  73        button: MouseButton,
  74        handler: impl Fn(&mut Self::ViewState, &MouseUpEvent, &mut ViewContext<Self::ViewState>)
  75            + Send
  76            + Sync
  77            + 'static,
  78    ) -> Self
  79    where
  80        Self: Sized,
  81    {
  82        self.stateless_interaction()
  83            .mouse_up_listeners
  84            .push(Arc::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 Self::ViewState, &MouseDownEvent, &mut ViewContext<Self::ViewState>)
  99            + Send
 100            + Sync
 101            + 'static,
 102    ) -> Self
 103    where
 104        Self: Sized,
 105    {
 106        self.stateless_interaction()
 107            .mouse_down_listeners
 108            .push(Arc::new(move |view, event, bounds, phase, cx| {
 109                if phase == DispatchPhase::Capture
 110                    && event.button == button
 111                    && !bounds.contains_point(&event.position)
 112                {
 113                    handler(view, event, cx)
 114                }
 115            }));
 116        self
 117    }
 118
 119    fn on_mouse_up_out(
 120        mut self,
 121        button: MouseButton,
 122        handler: impl Fn(&mut Self::ViewState, &MouseUpEvent, &mut ViewContext<Self::ViewState>)
 123            + Send
 124            + Sync
 125            + 'static,
 126    ) -> Self
 127    where
 128        Self: Sized,
 129    {
 130        self.stateless_interaction()
 131            .mouse_up_listeners
 132            .push(Arc::new(move |view, event, bounds, phase, cx| {
 133                if phase == DispatchPhase::Capture
 134                    && event.button == button
 135                    && !bounds.contains_point(&event.position)
 136                {
 137                    handler(view, event, cx);
 138                }
 139            }));
 140        self
 141    }
 142
 143    fn on_mouse_move(
 144        mut self,
 145        handler: impl Fn(&mut Self::ViewState, &MouseMoveEvent, &mut ViewContext<Self::ViewState>)
 146            + Send
 147            + Sync
 148            + 'static,
 149    ) -> Self
 150    where
 151        Self: Sized,
 152    {
 153        self.stateless_interaction()
 154            .mouse_move_listeners
 155            .push(Arc::new(move |view, event, bounds, phase, cx| {
 156                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
 157                    handler(view, event, cx);
 158                }
 159            }));
 160        self
 161    }
 162
 163    fn on_scroll_wheel(
 164        mut self,
 165        handler: impl Fn(&mut Self::ViewState, &ScrollWheelEvent, &mut ViewContext<Self::ViewState>)
 166            + Send
 167            + Sync
 168            + 'static,
 169    ) -> Self
 170    where
 171        Self: Sized,
 172    {
 173        self.stateless_interaction()
 174            .scroll_wheel_listeners
 175            .push(Arc::new(move |view, event, bounds, phase, cx| {
 176                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
 177                    handler(view, event, cx);
 178                }
 179            }));
 180        self
 181    }
 182
 183    fn context<C>(mut self, context: C) -> Self
 184    where
 185        Self: Sized,
 186        C: TryInto<DispatchContext>,
 187        C::Error: Debug,
 188    {
 189        self.stateless_interaction().dispatch_context =
 190            context.try_into().expect("invalid dispatch context");
 191        self
 192    }
 193
 194    fn on_action<A: 'static>(
 195        mut self,
 196        listener: impl Fn(&mut Self::ViewState, &A, DispatchPhase, &mut ViewContext<Self::ViewState>)
 197            + Send
 198            + Sync
 199            + 'static,
 200    ) -> Self
 201    where
 202        Self: Sized,
 203    {
 204        self.stateless_interaction().key_listeners.push((
 205            TypeId::of::<A>(),
 206            Arc::new(move |view, event, _, phase, cx| {
 207                let event = event.downcast_ref().unwrap();
 208                listener(view, event, phase, cx);
 209                None
 210            }),
 211        ));
 212        self
 213    }
 214
 215    fn on_key_down(
 216        mut self,
 217        listener: impl Fn(
 218                &mut Self::ViewState,
 219                &KeyDownEvent,
 220                DispatchPhase,
 221                &mut ViewContext<Self::ViewState>,
 222            ) + Send
 223            + Sync
 224            + 'static,
 225    ) -> Self
 226    where
 227        Self: Sized,
 228    {
 229        self.stateless_interaction().key_listeners.push((
 230            TypeId::of::<KeyDownEvent>(),
 231            Arc::new(move |view, event, _, phase, cx| {
 232                let event = event.downcast_ref().unwrap();
 233                listener(view, event, phase, cx);
 234                None
 235            }),
 236        ));
 237        self
 238    }
 239
 240    fn on_key_up(
 241        mut self,
 242        listener: impl Fn(&mut Self::ViewState, &KeyUpEvent, DispatchPhase, &mut ViewContext<Self::ViewState>)
 243            + Send
 244            + Sync
 245            + 'static,
 246    ) -> Self
 247    where
 248        Self: Sized,
 249    {
 250        self.stateless_interaction().key_listeners.push((
 251            TypeId::of::<KeyUpEvent>(),
 252            Arc::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_interaction()
 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_interaction().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<S: 'static>(
 290        mut self,
 291        listener: impl Fn(&mut Self::ViewState, S, &mut ViewContext<Self::ViewState>)
 292            + Send
 293            + Sync
 294            + 'static,
 295    ) -> Self
 296    where
 297        Self: Sized,
 298    {
 299        self.stateless_interaction().drop_listeners.push((
 300            TypeId::of::<S>(),
 301            Arc::new(move |view, drag_state, cx| {
 302                listener(view, *drag_state.downcast().unwrap(), cx);
 303            }),
 304        ));
 305        self
 306    }
 307}
 308
 309pub trait StatefulInteractive: StatelessInteractive {
 310    fn stateful_interaction(&mut self) -> &mut StatefulInteraction<Self::ViewState>;
 311
 312    fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
 313    where
 314        Self: Sized,
 315    {
 316        self.stateful_interaction().active_style = f(StyleRefinement::default());
 317        self
 318    }
 319
 320    fn group_active(
 321        mut self,
 322        group_name: impl Into<SharedString>,
 323        f: impl FnOnce(StyleRefinement) -> StyleRefinement,
 324    ) -> Self
 325    where
 326        Self: Sized,
 327    {
 328        self.stateful_interaction().group_active_style = Some(GroupStyle {
 329            group: group_name.into(),
 330            style: f(StyleRefinement::default()),
 331        });
 332        self
 333    }
 334
 335    fn on_click(
 336        mut self,
 337        listener: impl Fn(&mut Self::ViewState, &ClickEvent, &mut ViewContext<Self::ViewState>)
 338            + Send
 339            + Sync
 340            + 'static,
 341    ) -> Self
 342    where
 343        Self: Sized,
 344    {
 345        self.stateful_interaction()
 346            .click_listeners
 347            .push(Arc::new(move |view, event, cx| listener(view, event, cx)));
 348        self
 349    }
 350
 351    fn on_drag<S, R, E>(
 352        mut self,
 353        listener: impl Fn(
 354                &mut Self::ViewState,
 355                &mut ViewContext<Self::ViewState>,
 356            ) -> Drag<S, R, Self::ViewState, E>
 357            + Send
 358            + Sync
 359            + 'static,
 360    ) -> Self
 361    where
 362        Self: Sized,
 363        S: Any + Send + Sync,
 364        R: Fn(&mut Self::ViewState, &mut ViewContext<Self::ViewState>) -> E,
 365        R: 'static + 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>: '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> {
 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: 'static> ElementInteraction<V> for StatefulInteraction<V> {
 697    fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
 698        Some(self)
 699    }
 700
 701    fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
 702        Some(self)
 703    }
 704
 705    fn as_stateless(&self) -> &StatelessInteraction<V> {
 706        &self.stateless
 707    }
 708
 709    fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
 710        &mut self.stateless
 711    }
 712}
 713
 714impl<V> From<ElementId> for StatefulInteraction<V> {
 715    fn from(id: ElementId) -> Self {
 716        Self {
 717            id,
 718            stateless: StatelessInteraction::default(),
 719            click_listeners: SmallVec::new(),
 720            drag_listener: None,
 721            active_style: StyleRefinement::default(),
 722            group_active_style: None,
 723        }
 724    }
 725}
 726
 727type DropListener<V> = dyn Fn(&mut V, AnyBox, &mut ViewContext<V>) + 'static + Send + Sync;
 728
 729pub struct StatelessInteraction<V> {
 730    pub dispatch_context: DispatchContext,
 731    pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
 732    pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
 733    pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
 734    pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
 735    pub key_listeners: SmallVec<[(TypeId, KeyListener<V>); 32]>,
 736    pub hover_style: StyleRefinement,
 737    pub group_hover_style: Option<GroupStyle>,
 738    drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
 739    group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
 740    drop_listeners: SmallVec<[(TypeId, Arc<DropListener<V>>); 2]>,
 741}
 742
 743impl<V> StatelessInteraction<V> {
 744    pub fn into_stateful(self, id: impl Into<ElementId>) -> StatefulInteraction<V> {
 745        StatefulInteraction {
 746            id: id.into(),
 747            stateless: self,
 748            click_listeners: SmallVec::new(),
 749            drag_listener: None,
 750            active_style: StyleRefinement::default(),
 751            group_active_style: None,
 752        }
 753    }
 754}
 755
 756pub struct GroupStyle {
 757    pub group: SharedString,
 758    pub style: StyleRefinement,
 759}
 760
 761#[derive(Default)]
 762pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
 763
 764impl GroupBounds {
 765    pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
 766        cx.default_global_mut::<Self>()
 767            .0
 768            .get(name)
 769            .and_then(|bounds_stack| bounds_stack.last())
 770            .cloned()
 771    }
 772
 773    pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
 774        cx.default_global_mut::<Self>()
 775            .0
 776            .entry(name)
 777            .or_default()
 778            .push(bounds);
 779    }
 780
 781    pub fn pop(name: &SharedString, cx: &mut AppContext) {
 782        cx.default_global_mut::<Self>()
 783            .0
 784            .get_mut(name)
 785            .unwrap()
 786            .pop();
 787    }
 788}
 789
 790#[derive(Copy, Clone, Default, Eq, PartialEq)]
 791struct ActiveState {
 792    pub group: bool,
 793    pub element: bool,
 794}
 795
 796impl ActiveState {
 797    pub fn is_none(&self) -> bool {
 798        !self.group && !self.element
 799    }
 800}
 801
 802#[derive(Default)]
 803pub struct InteractiveElementState {
 804    active_state: Arc<Mutex<ActiveState>>,
 805    pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
 806    scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
 807}
 808
 809impl InteractiveElementState {
 810    pub fn scroll_offset(&self) -> Option<Point<Pixels>> {
 811        self.scroll_offset
 812            .as_ref()
 813            .map(|offset| offset.lock().clone())
 814    }
 815}
 816
 817impl<V> Default for StatelessInteraction<V> {
 818    fn default() -> Self {
 819        Self {
 820            dispatch_context: DispatchContext::default(),
 821            mouse_down_listeners: SmallVec::new(),
 822            mouse_up_listeners: SmallVec::new(),
 823            mouse_move_listeners: SmallVec::new(),
 824            scroll_wheel_listeners: SmallVec::new(),
 825            key_listeners: SmallVec::new(),
 826            hover_style: StyleRefinement::default(),
 827            group_hover_style: None,
 828            drag_over_styles: SmallVec::new(),
 829            group_drag_over_styles: SmallVec::new(),
 830            drop_listeners: SmallVec::new(),
 831        }
 832    }
 833}
 834
 835impl<V: 'static> ElementInteraction<V> for StatelessInteraction<V> {
 836    fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
 837        None
 838    }
 839
 840    fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
 841        None
 842    }
 843
 844    fn as_stateless(&self) -> &StatelessInteraction<V> {
 845        self
 846    }
 847
 848    fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
 849        self
 850    }
 851}
 852
 853#[derive(Clone, Debug, Eq, PartialEq)]
 854pub struct KeyDownEvent {
 855    pub keystroke: Keystroke,
 856    pub is_held: bool,
 857}
 858
 859#[derive(Clone, Debug)]
 860pub struct KeyUpEvent {
 861    pub keystroke: Keystroke,
 862}
 863
 864#[derive(Clone, Debug, Default)]
 865pub struct ModifiersChangedEvent {
 866    pub modifiers: Modifiers,
 867}
 868
 869impl Deref for ModifiersChangedEvent {
 870    type Target = Modifiers;
 871
 872    fn deref(&self) -> &Self::Target {
 873        &self.modifiers
 874    }
 875}
 876
 877/// The phase of a touch motion event.
 878/// Based on the winit enum of the same name.
 879#[derive(Clone, Copy, Debug)]
 880pub enum TouchPhase {
 881    Started,
 882    Moved,
 883    Ended,
 884}
 885
 886#[derive(Clone, Debug, Default)]
 887pub struct MouseDownEvent {
 888    pub button: MouseButton,
 889    pub position: Point<Pixels>,
 890    pub modifiers: Modifiers,
 891    pub click_count: usize,
 892}
 893
 894#[derive(Clone, Debug, Default)]
 895pub struct MouseUpEvent {
 896    pub button: MouseButton,
 897    pub position: Point<Pixels>,
 898    pub modifiers: Modifiers,
 899    pub click_count: usize,
 900}
 901
 902#[derive(Clone, Debug, Default)]
 903pub struct ClickEvent {
 904    pub down: MouseDownEvent,
 905    pub up: MouseUpEvent,
 906}
 907
 908pub struct Drag<S, R, V, E>
 909where
 910    R: Fn(&mut V, &mut ViewContext<V>) -> E,
 911    E: Element<ViewState = V>,
 912{
 913    pub state: S,
 914    pub render_drag_handle: R,
 915    view_type: PhantomData<V>,
 916}
 917
 918impl<S, R, V, E> Drag<S, R, V, E>
 919where
 920    R: Fn(&mut V, &mut ViewContext<V>) -> E,
 921    E: Element<ViewState = V>,
 922{
 923    pub fn new(state: S, render_drag_handle: R) -> Self {
 924        Drag {
 925            state,
 926            render_drag_handle,
 927            view_type: PhantomData,
 928        }
 929    }
 930}
 931
 932#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
 933pub enum MouseButton {
 934    Left,
 935    Right,
 936    Middle,
 937    Navigate(NavigationDirection),
 938}
 939
 940impl MouseButton {
 941    pub fn all() -> Vec<Self> {
 942        vec![
 943            MouseButton::Left,
 944            MouseButton::Right,
 945            MouseButton::Middle,
 946            MouseButton::Navigate(NavigationDirection::Back),
 947            MouseButton::Navigate(NavigationDirection::Forward),
 948        ]
 949    }
 950}
 951
 952impl Default for MouseButton {
 953    fn default() -> Self {
 954        Self::Left
 955    }
 956}
 957
 958#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
 959pub enum NavigationDirection {
 960    Back,
 961    Forward,
 962}
 963
 964impl Default for NavigationDirection {
 965    fn default() -> Self {
 966        Self::Back
 967    }
 968}
 969
 970#[derive(Clone, Debug, Default)]
 971pub struct MouseMoveEvent {
 972    pub position: Point<Pixels>,
 973    pub pressed_button: Option<MouseButton>,
 974    pub modifiers: Modifiers,
 975}
 976
 977#[derive(Clone, Debug)]
 978pub struct ScrollWheelEvent {
 979    pub position: Point<Pixels>,
 980    pub delta: ScrollDelta,
 981    pub modifiers: Modifiers,
 982    pub touch_phase: TouchPhase,
 983}
 984
 985impl Deref for ScrollWheelEvent {
 986    type Target = Modifiers;
 987
 988    fn deref(&self) -> &Self::Target {
 989        &self.modifiers
 990    }
 991}
 992
 993#[derive(Clone, Copy, Debug)]
 994pub enum ScrollDelta {
 995    Pixels(Point<Pixels>),
 996    Lines(Point<f32>),
 997}
 998
 999impl Default for ScrollDelta {
1000    fn default() -> Self {
1001        Self::Lines(Default::default())
1002    }
1003}
1004
1005impl ScrollDelta {
1006    pub fn precise(&self) -> bool {
1007        match self {
1008            ScrollDelta::Pixels(_) => true,
1009            ScrollDelta::Lines(_) => false,
1010        }
1011    }
1012
1013    pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
1014        match self {
1015            ScrollDelta::Pixels(delta) => *delta,
1016            ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
1017        }
1018    }
1019}
1020
1021#[derive(Clone, Debug, Default)]
1022pub struct MouseExitEvent {
1023    pub position: Point<Pixels>,
1024    pub pressed_button: Option<MouseButton>,
1025    pub modifiers: Modifiers,
1026}
1027
1028impl Deref for MouseExitEvent {
1029    type Target = Modifiers;
1030
1031    fn deref(&self) -> &Self::Target {
1032        &self.modifiers
1033    }
1034}
1035
1036#[derive(Clone, Debug)]
1037pub enum InputEvent {
1038    KeyDown(KeyDownEvent),
1039    KeyUp(KeyUpEvent),
1040    ModifiersChanged(ModifiersChangedEvent),
1041    MouseDown(MouseDownEvent),
1042    MouseUp(MouseUpEvent),
1043    MouseMoved(MouseMoveEvent),
1044    MouseExited(MouseExitEvent),
1045    ScrollWheel(ScrollWheelEvent),
1046}
1047
1048impl InputEvent {
1049    pub fn position(&self) -> Option<Point<Pixels>> {
1050        match self {
1051            InputEvent::KeyDown { .. } => None,
1052            InputEvent::KeyUp { .. } => None,
1053            InputEvent::ModifiersChanged { .. } => None,
1054            InputEvent::MouseDown(event) => Some(event.position),
1055            InputEvent::MouseUp(event) => Some(event.position),
1056            InputEvent::MouseMoved(event) => Some(event.position),
1057            InputEvent::MouseExited(event) => Some(event.position),
1058            InputEvent::ScrollWheel(event) => Some(event.position),
1059        }
1060    }
1061
1062    pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
1063        match self {
1064            InputEvent::KeyDown { .. } => None,
1065            InputEvent::KeyUp { .. } => None,
1066            InputEvent::ModifiersChanged { .. } => None,
1067            InputEvent::MouseDown(event) => Some(event),
1068            InputEvent::MouseUp(event) => Some(event),
1069            InputEvent::MouseMoved(event) => Some(event),
1070            InputEvent::MouseExited(event) => Some(event),
1071            InputEvent::ScrollWheel(event) => Some(event),
1072        }
1073    }
1074
1075    pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> {
1076        match self {
1077            InputEvent::KeyDown(event) => Some(event),
1078            InputEvent::KeyUp(event) => Some(event),
1079            InputEvent::ModifiersChanged(event) => Some(event),
1080            InputEvent::MouseDown(_) => None,
1081            InputEvent::MouseUp(_) => None,
1082            InputEvent::MouseMoved(_) => None,
1083            InputEvent::MouseExited(_) => None,
1084            InputEvent::ScrollWheel(_) => None,
1085        }
1086    }
1087}
1088
1089pub struct FocusEvent {
1090    pub blurred: Option<FocusHandle>,
1091    pub focused: Option<FocusHandle>,
1092}
1093
1094pub type MouseDownListener<V> = Arc<
1095    dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1096        + Send
1097        + Sync
1098        + 'static,
1099>;
1100pub type MouseUpListener<V> = Arc<
1101    dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1102        + Send
1103        + Sync
1104        + 'static,
1105>;
1106
1107pub type MouseMoveListener<V> = Arc<
1108    dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1109        + Send
1110        + Sync
1111        + 'static,
1112>;
1113
1114pub type ScrollWheelListener<V> = Arc<
1115    dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1116        + Send
1117        + Sync
1118        + 'static,
1119>;
1120
1121pub type ClickListener<V> =
1122    Arc<dyn Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
1123
1124pub(crate) type DragListener<V> =
1125    Arc<dyn Fn(&mut V, Point<Pixels>, &mut ViewContext<V>) -> AnyDrag + Send + Sync + 'static>;
1126
1127pub type KeyListener<V> = Arc<
1128    dyn Fn(
1129            &mut V,
1130            &dyn Any,
1131            &[&DispatchContext],
1132            DispatchPhase,
1133            &mut ViewContext<V>,
1134        ) -> Option<Box<dyn Action>>
1135        + Send
1136        + Sync
1137        + 'static,
1138>;