interactive.rs

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