interactive.rs

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