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