interactive.rs

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