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};
  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>) + '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>) + '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>) + '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>) + '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>) + '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>) + '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>) + '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>) + '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>) + '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<W: 'static>(
 262        mut self,
 263        listener: impl Fn(&mut V, View<W>, &mut ViewContext<V>) + 'static,
 264    ) -> Self
 265    where
 266        Self: Sized,
 267    {
 268        self.stateless_interaction().drop_listeners.push((
 269            TypeId::of::<W>(),
 270            Box::new(move |view, dragged_view, cx| {
 271                listener(view, dragged_view.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>) + '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<W>(
 318        mut self,
 319        listener: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
 320    ) -> Self
 321    where
 322        Self: Sized,
 323        W: 'static + Render,
 324    {
 325        debug_assert!(
 326            self.stateful_interaction().drag_listener.is_none(),
 327            "calling on_drag more than once on the same element is not supported"
 328        );
 329        self.stateful_interaction().drag_listener =
 330            Some(Box::new(move |view_state, cursor_offset, cx| AnyDrag {
 331                view: listener(view_state, cx).into(),
 332                cursor_offset,
 333            }));
 334        self
 335    }
 336
 337    fn on_hover(mut self, listener: impl 'static + Fn(&mut V, bool, &mut ViewContext<V>)) -> Self
 338    where
 339        Self: Sized,
 340    {
 341        debug_assert!(
 342            self.stateful_interaction().hover_listener.is_none(),
 343            "calling on_hover more than once on the same element is not supported"
 344        );
 345        self.stateful_interaction().hover_listener = Some(Box::new(listener));
 346        self
 347    }
 348
 349    fn tooltip<W>(
 350        mut self,
 351        build_tooltip: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
 352    ) -> Self
 353    where
 354        Self: Sized,
 355        W: 'static + Render,
 356    {
 357        debug_assert!(
 358            self.stateful_interaction().tooltip_builder.is_none(),
 359            "calling tooltip more than once on the same element is not supported"
 360        );
 361        self.stateful_interaction().tooltip_builder = Some(Arc::new(move |view_state, cx| {
 362            build_tooltip(view_state, cx).into()
 363        }));
 364
 365        self
 366    }
 367}
 368
 369pub trait ElementInteraction<V: 'static>: 'static {
 370    fn as_stateless(&self) -> &StatelessInteraction<V>;
 371    fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V>;
 372    fn as_stateful(&self) -> Option<&StatefulInteraction<V>>;
 373    fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>>;
 374
 375    fn initialize<R>(
 376        &mut self,
 377        cx: &mut ViewContext<V>,
 378        f: impl FnOnce(&mut ViewContext<V>) -> R,
 379    ) -> R {
 380        if let Some(stateful) = self.as_stateful_mut() {
 381            cx.with_element_id(stateful.id.clone(), |global_id, cx| {
 382                stateful.key_listeners.push((
 383                    TypeId::of::<KeyDownEvent>(),
 384                    Box::new(move |_, key_down, context, phase, cx| {
 385                        if phase == DispatchPhase::Bubble {
 386                            let key_down = key_down.downcast_ref::<KeyDownEvent>().unwrap();
 387                            if let KeyMatch::Some(action) =
 388                                cx.match_keystroke(&global_id, &key_down.keystroke, context)
 389                            {
 390                                return Some(action);
 391                            }
 392                        }
 393
 394                        None
 395                    }),
 396                ));
 397                let result = stateful.stateless.initialize(cx, f);
 398                stateful.key_listeners.pop();
 399                result
 400            })
 401        } else {
 402            let stateless = self.as_stateless_mut();
 403            cx.with_key_dispatch_context(stateless.dispatch_context.clone(), |cx| {
 404                cx.with_key_listeners(mem::take(&mut stateless.key_listeners), f)
 405            })
 406        }
 407    }
 408
 409    fn refine_style(
 410        &self,
 411        style: &mut Style,
 412        bounds: Bounds<Pixels>,
 413        element_state: &InteractiveElementState,
 414        cx: &mut ViewContext<V>,
 415    ) {
 416        let mouse_position = cx.mouse_position();
 417        let stateless = self.as_stateless();
 418        if let Some(group_hover) = stateless.group_hover_style.as_ref() {
 419            if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
 420                if group_bounds.contains_point(&mouse_position) {
 421                    style.refine(&group_hover.style);
 422                }
 423            }
 424        }
 425        if bounds.contains_point(&mouse_position) {
 426            style.refine(&stateless.hover_style);
 427        }
 428
 429        if let Some(drag) = cx.active_drag.take() {
 430            for (state_type, group_drag_style) in &self.as_stateless().group_drag_over_styles {
 431                if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) {
 432                    if *state_type == drag.view.entity_type()
 433                        && group_bounds.contains_point(&mouse_position)
 434                    {
 435                        style.refine(&group_drag_style.style);
 436                    }
 437                }
 438            }
 439
 440            for (state_type, drag_over_style) in &self.as_stateless().drag_over_styles {
 441                if *state_type == drag.view.entity_type() && bounds.contains_point(&mouse_position)
 442                {
 443                    style.refine(drag_over_style);
 444                }
 445            }
 446
 447            cx.active_drag = Some(drag);
 448        }
 449
 450        if let Some(stateful) = self.as_stateful() {
 451            let active_state = element_state.active_state.lock();
 452            if active_state.group {
 453                if let Some(group_style) = stateful.group_active_style.as_ref() {
 454                    style.refine(&group_style.style);
 455                }
 456            }
 457            if active_state.element {
 458                style.refine(&stateful.active_style);
 459            }
 460        }
 461    }
 462
 463    fn paint(
 464        &mut self,
 465        bounds: Bounds<Pixels>,
 466        content_size: Size<Pixels>,
 467        overflow: Point<Overflow>,
 468        element_state: &mut InteractiveElementState,
 469        cx: &mut ViewContext<V>,
 470    ) {
 471        let stateless = self.as_stateless_mut();
 472        for listener in stateless.mouse_down_listeners.drain(..) {
 473            cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
 474                listener(state, event, &bounds, phase, cx);
 475            })
 476        }
 477
 478        for listener in stateless.mouse_up_listeners.drain(..) {
 479            cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
 480                listener(state, event, &bounds, phase, cx);
 481            })
 482        }
 483
 484        for listener in stateless.mouse_move_listeners.drain(..) {
 485            cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
 486                listener(state, event, &bounds, phase, cx);
 487            })
 488        }
 489
 490        for listener in stateless.scroll_wheel_listeners.drain(..) {
 491            cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
 492                listener(state, event, &bounds, phase, cx);
 493            })
 494        }
 495
 496        let hover_group_bounds = stateless
 497            .group_hover_style
 498            .as_ref()
 499            .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
 500
 501        if let Some(group_bounds) = hover_group_bounds {
 502            let hovered = group_bounds.contains_point(&cx.mouse_position());
 503            cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
 504                if phase == DispatchPhase::Capture {
 505                    if group_bounds.contains_point(&event.position) != hovered {
 506                        cx.notify();
 507                    }
 508                }
 509            });
 510        }
 511
 512        if stateless.hover_style.is_some()
 513            || (cx.active_drag.is_some() && !stateless.drag_over_styles.is_empty())
 514        {
 515            let hovered = bounds.contains_point(&cx.mouse_position());
 516            cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
 517                if phase == DispatchPhase::Capture {
 518                    if bounds.contains_point(&event.position) != hovered {
 519                        cx.notify();
 520                    }
 521                }
 522            });
 523        }
 524
 525        if cx.active_drag.is_some() {
 526            let drop_listeners = mem::take(&mut stateless.drop_listeners);
 527            cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| {
 528                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
 529                    if let Some(drag_state_type) =
 530                        cx.active_drag.as_ref().map(|drag| drag.view.entity_type())
 531                    {
 532                        for (drop_state_type, listener) in &drop_listeners {
 533                            if *drop_state_type == drag_state_type {
 534                                let drag = cx
 535                                    .active_drag
 536                                    .take()
 537                                    .expect("checked for type drag state type above");
 538                                listener(view, drag.view.clone(), cx);
 539                                cx.notify();
 540                                cx.stop_propagation();
 541                            }
 542                        }
 543                    }
 544                }
 545            });
 546        }
 547
 548        if let Some(stateful) = self.as_stateful_mut() {
 549            let click_listeners = mem::take(&mut stateful.click_listeners);
 550            let drag_listener = mem::take(&mut stateful.drag_listener);
 551
 552            if !click_listeners.is_empty() || drag_listener.is_some() {
 553                let pending_mouse_down = element_state.pending_mouse_down.clone();
 554                let mouse_down = pending_mouse_down.lock().clone();
 555                if let Some(mouse_down) = mouse_down {
 556                    if let Some(drag_listener) = drag_listener {
 557                        let active_state = element_state.active_state.clone();
 558
 559                        cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
 560                            if cx.active_drag.is_some() {
 561                                if phase == DispatchPhase::Capture {
 562                                    cx.notify();
 563                                }
 564                            } else if phase == DispatchPhase::Bubble
 565                                && bounds.contains_point(&event.position)
 566                                && (event.position - mouse_down.position).magnitude()
 567                                    > DRAG_THRESHOLD
 568                            {
 569                                *active_state.lock() = ActiveState::default();
 570                                let cursor_offset = event.position - bounds.origin;
 571                                let drag = drag_listener(view_state, cursor_offset, cx);
 572                                cx.active_drag = Some(drag);
 573                                cx.notify();
 574                                cx.stop_propagation();
 575                            }
 576                        });
 577                    }
 578
 579                    cx.on_mouse_event(move |view_state, event: &MouseUpEvent, phase, cx| {
 580                        if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position)
 581                        {
 582                            let mouse_click = ClickEvent {
 583                                down: mouse_down.clone(),
 584                                up: event.clone(),
 585                            };
 586                            for listener in &click_listeners {
 587                                listener(view_state, &mouse_click, cx);
 588                            }
 589                        }
 590                        *pending_mouse_down.lock() = None;
 591                    });
 592                } else {
 593                    cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| {
 594                        if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position)
 595                        {
 596                            *pending_mouse_down.lock() = Some(event.clone());
 597                        }
 598                    });
 599                }
 600            }
 601
 602            if let Some(hover_listener) = stateful.hover_listener.take() {
 603                let was_hovered = element_state.hover_state.clone();
 604                let has_mouse_down = element_state.pending_mouse_down.lock().is_some();
 605
 606                let active_tooltip = element_state.active_tooltip.clone();
 607                let tooltip_builder = stateful.tooltip_builder.clone();
 608
 609                cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
 610                    if phase != DispatchPhase::Bubble {
 611                        return;
 612                    }
 613                    let is_hovered = bounds.contains_point(&event.position) && !has_mouse_down;
 614                    let mut was_hovered = was_hovered.lock();
 615
 616                    if is_hovered != was_hovered.clone() {
 617                        *was_hovered = is_hovered;
 618                        drop(was_hovered);
 619                        if let Some(tooltip_builder) = &tooltip_builder {
 620                            let mut active_tooltip = active_tooltip.lock();
 621                            if is_hovered && active_tooltip.is_none() {
 622                                *active_tooltip = Some(tooltip_builder(view_state, cx));
 623                            } else if !is_hovered {
 624                                active_tooltip.take();
 625                            }
 626                        }
 627
 628                        hover_listener(view_state, is_hovered, cx);
 629                    }
 630                });
 631            }
 632
 633            if let Some(active_tooltip) = element_state.active_tooltip.lock().as_ref() {
 634                if *element_state.hover_state.lock() {
 635                    cx.active_tooltip = Some(active_tooltip.clone());
 636                }
 637            }
 638
 639            let active_state = element_state.active_state.clone();
 640            if active_state.lock().is_none() {
 641                let active_group_bounds = stateful
 642                    .group_active_style
 643                    .as_ref()
 644                    .and_then(|group_active| GroupBounds::get(&group_active.group, cx));
 645                cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
 646                    if phase == DispatchPhase::Bubble {
 647                        let group = active_group_bounds
 648                            .map_or(false, |bounds| bounds.contains_point(&down.position));
 649                        let element = bounds.contains_point(&down.position);
 650                        if group || element {
 651                            *active_state.lock() = ActiveState { group, element };
 652                            cx.notify();
 653                        }
 654                    }
 655                });
 656            } else {
 657                cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
 658                    if phase == DispatchPhase::Capture {
 659                        *active_state.lock() = ActiveState::default();
 660                        cx.notify();
 661                    }
 662                });
 663            }
 664
 665            if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
 666                let scroll_offset = element_state
 667                    .scroll_offset
 668                    .get_or_insert_with(Arc::default)
 669                    .clone();
 670                let line_height = cx.line_height();
 671                let scroll_max = (content_size - bounds.size).max(&Size::default());
 672
 673                cx.on_mouse_event(move |_, event: &ScrollWheelEvent, phase, cx| {
 674                    if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
 675                        let mut scroll_offset = scroll_offset.lock();
 676                        let old_scroll_offset = *scroll_offset;
 677                        let delta = event.delta.pixel_delta(line_height);
 678
 679                        if overflow.x == Overflow::Scroll {
 680                            scroll_offset.x =
 681                                (scroll_offset.x + delta.x).clamp(-scroll_max.width, px(0.));
 682                        }
 683
 684                        if overflow.y == Overflow::Scroll {
 685                            scroll_offset.y =
 686                                (scroll_offset.y + delta.y).clamp(-scroll_max.height, px(0.));
 687                        }
 688
 689                        if *scroll_offset != old_scroll_offset {
 690                            cx.notify();
 691                            cx.stop_propagation();
 692                        }
 693                    }
 694                });
 695            }
 696        }
 697    }
 698}
 699
 700#[derive(Deref, DerefMut)]
 701pub struct StatefulInteraction<V> {
 702    pub id: ElementId,
 703    #[deref]
 704    #[deref_mut]
 705    stateless: StatelessInteraction<V>,
 706    click_listeners: SmallVec<[ClickListener<V>; 2]>,
 707    active_style: StyleRefinement,
 708    group_active_style: Option<GroupStyle>,
 709    drag_listener: Option<DragListener<V>>,
 710    hover_listener: Option<HoverListener<V>>,
 711    tooltip_builder: Option<TooltipBuilder<V>>,
 712}
 713
 714impl<V: 'static> ElementInteraction<V> for StatefulInteraction<V> {
 715    fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
 716        Some(self)
 717    }
 718
 719    fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
 720        Some(self)
 721    }
 722
 723    fn as_stateless(&self) -> &StatelessInteraction<V> {
 724        &self.stateless
 725    }
 726
 727    fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
 728        &mut self.stateless
 729    }
 730}
 731
 732impl<V> From<ElementId> for StatefulInteraction<V> {
 733    fn from(id: ElementId) -> Self {
 734        Self {
 735            id,
 736            stateless: StatelessInteraction::default(),
 737            click_listeners: SmallVec::new(),
 738            drag_listener: None,
 739            hover_listener: None,
 740            tooltip_builder: None,
 741            active_style: StyleRefinement::default(),
 742            group_active_style: None,
 743        }
 744    }
 745}
 746
 747type DropListener<V> = dyn Fn(&mut V, AnyView, &mut ViewContext<V>) + 'static;
 748
 749pub struct StatelessInteraction<V> {
 750    pub dispatch_context: DispatchContext,
 751    pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
 752    pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
 753    pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
 754    pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
 755    pub key_listeners: SmallVec<[(TypeId, KeyListener<V>); 32]>,
 756    pub hover_style: StyleRefinement,
 757    pub group_hover_style: Option<GroupStyle>,
 758    drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
 759    group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
 760    drop_listeners: SmallVec<[(TypeId, Box<DropListener<V>>); 2]>,
 761}
 762
 763impl<V> StatelessInteraction<V> {
 764    pub fn into_stateful(self, id: impl Into<ElementId>) -> StatefulInteraction<V> {
 765        StatefulInteraction {
 766            id: id.into(),
 767            stateless: self,
 768            click_listeners: SmallVec::new(),
 769            drag_listener: None,
 770            hover_listener: None,
 771            tooltip_builder: None,
 772            active_style: StyleRefinement::default(),
 773            group_active_style: None,
 774        }
 775    }
 776}
 777
 778pub struct GroupStyle {
 779    pub group: SharedString,
 780    pub style: StyleRefinement,
 781}
 782
 783#[derive(Default)]
 784pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
 785
 786impl GroupBounds {
 787    pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
 788        cx.default_global::<Self>()
 789            .0
 790            .get(name)
 791            .and_then(|bounds_stack| bounds_stack.last())
 792            .cloned()
 793    }
 794
 795    pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
 796        cx.default_global::<Self>()
 797            .0
 798            .entry(name)
 799            .or_default()
 800            .push(bounds);
 801    }
 802
 803    pub fn pop(name: &SharedString, cx: &mut AppContext) {
 804        cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
 805    }
 806}
 807
 808#[derive(Copy, Clone, Default, Eq, PartialEq)]
 809struct ActiveState {
 810    pub group: bool,
 811    pub element: bool,
 812}
 813
 814impl ActiveState {
 815    pub fn is_none(&self) -> bool {
 816        !self.group && !self.element
 817    }
 818}
 819
 820#[derive(Default)]
 821pub struct InteractiveElementState {
 822    active_state: Arc<Mutex<ActiveState>>,
 823    hover_state: Arc<Mutex<bool>>,
 824    pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
 825    scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
 826    active_tooltip: Arc<Mutex<Option<AnyView>>>,
 827}
 828
 829impl InteractiveElementState {
 830    pub fn scroll_offset(&self) -> Option<Point<Pixels>> {
 831        self.scroll_offset
 832            .as_ref()
 833            .map(|offset| offset.lock().clone())
 834    }
 835}
 836
 837impl<V> Default for StatelessInteraction<V> {
 838    fn default() -> Self {
 839        Self {
 840            dispatch_context: DispatchContext::default(),
 841            mouse_down_listeners: SmallVec::new(),
 842            mouse_up_listeners: SmallVec::new(),
 843            mouse_move_listeners: SmallVec::new(),
 844            scroll_wheel_listeners: SmallVec::new(),
 845            key_listeners: SmallVec::new(),
 846            hover_style: StyleRefinement::default(),
 847            group_hover_style: None,
 848            drag_over_styles: SmallVec::new(),
 849            group_drag_over_styles: SmallVec::new(),
 850            drop_listeners: SmallVec::new(),
 851        }
 852    }
 853}
 854
 855impl<V: 'static> ElementInteraction<V> for StatelessInteraction<V> {
 856    fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
 857        None
 858    }
 859
 860    fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
 861        None
 862    }
 863
 864    fn as_stateless(&self) -> &StatelessInteraction<V> {
 865        self
 866    }
 867
 868    fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
 869        self
 870    }
 871}
 872
 873#[derive(Clone, Debug, Eq, PartialEq)]
 874pub struct KeyDownEvent {
 875    pub keystroke: Keystroke,
 876    pub is_held: bool,
 877}
 878
 879#[derive(Clone, Debug)]
 880pub struct KeyUpEvent {
 881    pub keystroke: Keystroke,
 882}
 883
 884#[derive(Clone, Debug, Default)]
 885pub struct ModifiersChangedEvent {
 886    pub modifiers: Modifiers,
 887}
 888
 889impl Deref for ModifiersChangedEvent {
 890    type Target = Modifiers;
 891
 892    fn deref(&self) -> &Self::Target {
 893        &self.modifiers
 894    }
 895}
 896
 897/// The phase of a touch motion event.
 898/// Based on the winit enum of the same name.
 899#[derive(Clone, Copy, Debug)]
 900pub enum TouchPhase {
 901    Started,
 902    Moved,
 903    Ended,
 904}
 905
 906#[derive(Clone, Debug, Default)]
 907pub struct MouseDownEvent {
 908    pub button: MouseButton,
 909    pub position: Point<Pixels>,
 910    pub modifiers: Modifiers,
 911    pub click_count: usize,
 912}
 913
 914#[derive(Clone, Debug, Default)]
 915pub struct MouseUpEvent {
 916    pub button: MouseButton,
 917    pub position: Point<Pixels>,
 918    pub modifiers: Modifiers,
 919    pub click_count: usize,
 920}
 921
 922#[derive(Clone, Debug, Default)]
 923pub struct ClickEvent {
 924    pub down: MouseDownEvent,
 925    pub up: MouseUpEvent,
 926}
 927
 928pub struct Drag<S, R, V, E>
 929where
 930    R: Fn(&mut V, &mut ViewContext<V>) -> E,
 931    V: 'static,
 932    E: Component<()>,
 933{
 934    pub state: S,
 935    pub render_drag_handle: R,
 936    view_type: PhantomData<V>,
 937}
 938
 939impl<S, R, V, E> Drag<S, R, V, E>
 940where
 941    R: Fn(&mut V, &mut ViewContext<V>) -> E,
 942    V: 'static,
 943    E: Component<()>,
 944{
 945    pub fn new(state: S, render_drag_handle: R) -> Self {
 946        Drag {
 947            state,
 948            render_drag_handle,
 949            view_type: PhantomData,
 950        }
 951    }
 952}
 953
 954// impl<S, R, V, E> Render for Drag<S, R, V, E> {
 955//     // fn render(&mut self, cx: ViewContext<Self>) ->
 956// }
 957
 958#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
 959pub enum MouseButton {
 960    Left,
 961    Right,
 962    Middle,
 963    Navigate(NavigationDirection),
 964}
 965
 966impl MouseButton {
 967    pub fn all() -> Vec<Self> {
 968        vec![
 969            MouseButton::Left,
 970            MouseButton::Right,
 971            MouseButton::Middle,
 972            MouseButton::Navigate(NavigationDirection::Back),
 973            MouseButton::Navigate(NavigationDirection::Forward),
 974        ]
 975    }
 976}
 977
 978impl Default for MouseButton {
 979    fn default() -> Self {
 980        Self::Left
 981    }
 982}
 983
 984#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
 985pub enum NavigationDirection {
 986    Back,
 987    Forward,
 988}
 989
 990impl Default for NavigationDirection {
 991    fn default() -> Self {
 992        Self::Back
 993    }
 994}
 995
 996#[derive(Clone, Debug, Default)]
 997pub struct MouseMoveEvent {
 998    pub position: Point<Pixels>,
 999    pub pressed_button: Option<MouseButton>,
1000    pub modifiers: Modifiers,
1001}
1002
1003#[derive(Clone, Debug)]
1004pub struct ScrollWheelEvent {
1005    pub position: Point<Pixels>,
1006    pub delta: ScrollDelta,
1007    pub modifiers: Modifiers,
1008    pub touch_phase: TouchPhase,
1009}
1010
1011impl Deref for ScrollWheelEvent {
1012    type Target = Modifiers;
1013
1014    fn deref(&self) -> &Self::Target {
1015        &self.modifiers
1016    }
1017}
1018
1019#[derive(Clone, Copy, Debug)]
1020pub enum ScrollDelta {
1021    Pixels(Point<Pixels>),
1022    Lines(Point<f32>),
1023}
1024
1025impl Default for ScrollDelta {
1026    fn default() -> Self {
1027        Self::Lines(Default::default())
1028    }
1029}
1030
1031impl ScrollDelta {
1032    pub fn precise(&self) -> bool {
1033        match self {
1034            ScrollDelta::Pixels(_) => true,
1035            ScrollDelta::Lines(_) => false,
1036        }
1037    }
1038
1039    pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
1040        match self {
1041            ScrollDelta::Pixels(delta) => *delta,
1042            ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
1043        }
1044    }
1045}
1046
1047#[derive(Clone, Debug, Default)]
1048pub struct MouseExitEvent {
1049    pub position: Point<Pixels>,
1050    pub pressed_button: Option<MouseButton>,
1051    pub modifiers: Modifiers,
1052}
1053
1054impl Deref for MouseExitEvent {
1055    type Target = Modifiers;
1056
1057    fn deref(&self) -> &Self::Target {
1058        &self.modifiers
1059    }
1060}
1061
1062#[derive(Debug, Clone, Default)]
1063pub struct ExternalPaths(pub(crate) SmallVec<[PathBuf; 2]>);
1064
1065impl Render for ExternalPaths {
1066    type Element = Div<Self>;
1067
1068    fn render(&mut self, _: &mut ViewContext<Self>) -> Self::Element {
1069        div() // Intentionally left empty because the platform will render icons for the dragged files
1070    }
1071}
1072
1073#[derive(Debug, Clone)]
1074pub enum FileDropEvent {
1075    Entered {
1076        position: Point<Pixels>,
1077        files: ExternalPaths,
1078    },
1079    Pending {
1080        position: Point<Pixels>,
1081    },
1082    Submit {
1083        position: Point<Pixels>,
1084    },
1085    Exited,
1086}
1087
1088#[derive(Clone, Debug)]
1089pub enum InputEvent {
1090    KeyDown(KeyDownEvent),
1091    KeyUp(KeyUpEvent),
1092    ModifiersChanged(ModifiersChangedEvent),
1093    MouseDown(MouseDownEvent),
1094    MouseUp(MouseUpEvent),
1095    MouseMove(MouseMoveEvent),
1096    MouseExited(MouseExitEvent),
1097    ScrollWheel(ScrollWheelEvent),
1098    FileDrop(FileDropEvent),
1099}
1100
1101impl InputEvent {
1102    pub fn position(&self) -> Option<Point<Pixels>> {
1103        match self {
1104            InputEvent::KeyDown { .. } => None,
1105            InputEvent::KeyUp { .. } => None,
1106            InputEvent::ModifiersChanged { .. } => None,
1107            InputEvent::MouseDown(event) => Some(event.position),
1108            InputEvent::MouseUp(event) => Some(event.position),
1109            InputEvent::MouseMove(event) => Some(event.position),
1110            InputEvent::MouseExited(event) => Some(event.position),
1111            InputEvent::ScrollWheel(event) => Some(event.position),
1112            InputEvent::FileDrop(FileDropEvent::Exited) => None,
1113            InputEvent::FileDrop(
1114                FileDropEvent::Entered { position, .. }
1115                | FileDropEvent::Pending { position, .. }
1116                | FileDropEvent::Submit { position, .. },
1117            ) => Some(*position),
1118        }
1119    }
1120
1121    pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
1122        match self {
1123            InputEvent::KeyDown { .. } => None,
1124            InputEvent::KeyUp { .. } => None,
1125            InputEvent::ModifiersChanged { .. } => None,
1126            InputEvent::MouseDown(event) => Some(event),
1127            InputEvent::MouseUp(event) => Some(event),
1128            InputEvent::MouseMove(event) => Some(event),
1129            InputEvent::MouseExited(event) => Some(event),
1130            InputEvent::ScrollWheel(event) => Some(event),
1131            InputEvent::FileDrop(event) => Some(event),
1132        }
1133    }
1134
1135    pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> {
1136        match self {
1137            InputEvent::KeyDown(event) => Some(event),
1138            InputEvent::KeyUp(event) => Some(event),
1139            InputEvent::ModifiersChanged(event) => Some(event),
1140            InputEvent::MouseDown(_) => None,
1141            InputEvent::MouseUp(_) => None,
1142            InputEvent::MouseMove(_) => None,
1143            InputEvent::MouseExited(_) => None,
1144            InputEvent::ScrollWheel(_) => None,
1145            InputEvent::FileDrop(_) => None,
1146        }
1147    }
1148}
1149
1150pub struct FocusEvent {
1151    pub blurred: Option<FocusHandle>,
1152    pub focused: Option<FocusHandle>,
1153}
1154
1155pub type MouseDownListener<V> = Box<
1156    dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
1157>;
1158pub type MouseUpListener<V> = Box<
1159    dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
1160>;
1161
1162pub type MouseMoveListener<V> = Box<
1163    dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
1164>;
1165
1166pub type ScrollWheelListener<V> = Box<
1167    dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
1168        + 'static,
1169>;
1170
1171pub type ClickListener<V> = Box<dyn Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static>;
1172
1173pub(crate) type DragListener<V> =
1174    Box<dyn Fn(&mut V, Point<Pixels>, &mut ViewContext<V>) -> AnyDrag + 'static>;
1175
1176pub(crate) type HoverListener<V> = Box<dyn Fn(&mut V, bool, &mut ViewContext<V>) + 'static>;
1177
1178pub(crate) type TooltipBuilder<V> = Arc<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static>;
1179
1180pub type KeyListener<V> = Box<
1181    dyn Fn(
1182            &mut V,
1183            &dyn Any,
1184            &[&DispatchContext],
1185            DispatchPhase,
1186            &mut ViewContext<V>,
1187        ) -> Option<Box<dyn Action>>
1188        + 'static,
1189>;