interactive.rs

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