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