interactive.rs

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