Skip to main content

gpui_ce_motion/
gesture_native.rs

1//! Native input adapter with a persistent owner and frame-scoped listeners.
2use crate::{
3    Drag, DragBounds, DragEvent, GesturePhase, GestureTargets, InView, Interaction, LayoutRect,
4    Motion, PinchGesture, Target,
5};
6
7use crate::{MotionAnchor, MotionState};
8
9use gpui::{
10    AnyElement, App, Bounds, DispatchPhase, Div, Element, ElementId, EntityId, FocusHandle,
11    GlobalElementId, Hitbox, HitboxBehavior, InspectorElementId, IntoElement, KeyDownEvent,
12    KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, MouseExitEvent, MouseMoveEvent,
13    MouseUpEvent, PinchEvent, Pixels, Stateful, TouchPhase, Window, prelude::*, px,
14};
15
16use std::{
17    cell::RefCell,
18    rc::Rc,
19    time::{Duration, Instant},
20};
21
22type DragCallback = Rc<dyn Fn(DragEvent, &mut Window, &mut App)>;
23type PinchCallback = Rc<dyn Fn(PinchGesture, &mut Window, &mut App)>;
24
25#[derive(Default)]
26struct Data {
27    interaction: Interaction,
28    motion: MotionState,
29    drag: Option<Drag>,
30    focus: Option<FocusHandle>,
31    visibility: Option<InView>,
32    epoch: Option<Instant>,
33    owner: Option<EntityId>,
34    pointer: bool,
35    key: Option<String>,
36    presented: Target,
37    constraints: Option<MotionAnchor>,
38    view_root: Option<MotionAnchor>,
39    view_margins: [f64; 4],
40    enabled: bool,
41    touch: bool,
42    pointer_origin: [f64; 2],
43    last_pointer: [f64; 2],
44    last_event: Duration,
45    drag_callback: Option<DragCallback>,
46    pending_drag: Option<DragEvent>,
47    pinch_callback: Option<PinchCallback>,
48    pinch_active: bool,
49    pinch_scale: f64,
50    pinch_center: [f64; 2],
51    pending_pinch: Option<PinchGesture>,
52}
53
54impl Data {
55    fn now(&self, cx: &App) -> Duration {
56        self.epoch.map_or(Duration::ZERO, |epoch| {
57            cx.background_executor()
58                .now()
59                .saturating_duration_since(epoch)
60        })
61    }
62
63    fn cancel(&mut self, cx: &App) {
64        let now = self.now(cx);
65
66        if self.pointer {
67            self.record(GesturePhase::Cancelled, self.last_pointer, now);
68        }
69
70        if self.pinch_active {
71            self.pending_pinch = Some(PinchGesture {
72                phase: GesturePhase::Cancelled,
73                center: self.pinch_center,
74                scale: self.pinch_scale,
75                delta: 0.0,
76            });
77            self.pinch_active = false;
78        }
79
80        self.touch = false;
81
82        if let Some(drag) = &mut self.drag {
83            drag.cancel(now);
84        }
85
86        self.pointer = false;
87        self.key = None;
88        self.interaction.pressed = false;
89        self.interaction.dragging = false;
90    }
91
92    fn record(&mut self, phase: GesturePhase, point: [f64; 2], now: Duration) {
93        if self.drag_callback.is_none() {
94            return;
95        }
96
97        let dt = now.saturating_sub(self.last_event).as_secs_f64();
98        let delta = std::array::from_fn(|i| point[i] - self.last_pointer[i]);
99
100        self.pending_drag = Some(DragEvent {
101            phase,
102            point,
103            delta,
104            offset: std::array::from_fn(|i| point[i] - self.pointer_origin[i]),
105            velocity: delta.map(|v| if dt > 0.0 { v / dt } else { 0.0 }),
106            locked_axis: self.drag.as_ref().and_then(Drag::locked_axis),
107        });
108        self.last_pointer = point;
109        self.last_event = now;
110    }
111
112    fn begin(&mut self, pointer: [f64; 2], window: &mut Window, cx: &mut App) -> bool {
113        if !self.enabled || self.pointer || !window.is_window_active() {
114            return false;
115        }
116
117        self.pointer = true;
118        self.touch = false;
119        self.key = None;
120        self.interaction.pressed = true;
121        let now = self.now(cx);
122
123        self.pointer_origin = pointer;
124        self.last_pointer = pointer;
125        self.last_event = now;
126
127        if let Some(drag) = &mut self.drag {
128            drag.begin(pointer, now);
129            self.interaction.dragging = true;
130        }
131
132        self.record(GesturePhase::Started, pointer, now);
133
134        if let Some(focus) = &self.focus {
135            focus.focus(window, cx);
136        }
137
138        self.notify(cx);
139
140        true
141    }
142
143    fn notify(&self, cx: &mut App) {
144        if let Some(owner) = self.owner {
145            cx.notify(owner);
146        }
147    }
148}
149
150/// Retain one handle per logical control. Clones share input/motion state.
151/// Mouse release outside the hitbox, lost button, Escape, blur and deactivation
152/// clear ownership. Pinch and supported native touch gestures are opt-in; raw
153/// multi-touch availability depends on the backend/platform.
154#[derive(Clone, Default)]
155pub struct GestureState(Rc<RefCell<Data>>);
156
157impl GestureState {
158    /// Adds bounded native pointer/arrow-key dragging. Configure once in the view.
159    pub fn with_drag(self, drag: Drag) -> Self {
160        self.0.borrow_mut().drag = Some(drag);
161
162        self
163    }
164
165    /// Measures drag bounds against a retained native container anchor. Resize
166    /// changes retarget from the current presentation rather than resetting it.
167    pub fn constraints(self, container: MotionAnchor) -> Self {
168        self.0.borrow_mut().constraints = Some(container);
169
170        self
171    }
172
173    /// Reports structured pointer start/move/end/cancel events outside state borrows.
174    /// Also enables a host-controlled pan surface without `with_drag`.
175    pub fn on_drag(self, callback: impl Fn(DragEvent, &mut Window, &mut App) + 'static) -> Self {
176        self.0.borrow_mut().drag_callback = Some(Rc::new(callback));
177
178        self
179    }
180
181    /// Reports native pinch gestures without assuming ordinary elements can scale.
182    pub fn on_pinch(
183        self,
184        callback: impl Fn(PinchGesture, &mut Window, &mut App) + 'static,
185    ) -> Self {
186        self.0.borrow_mut().pinch_callback = Some(Rc::new(callback));
187
188        self
189    }
190
191    /// Starts from a separate handle or scrubber. Available after the target's
192    /// first layout; focus, cancellation and outside release remain target-owned.
193    pub fn start_drag(&self, pointer: [f64; 2], window: &mut Window, cx: &mut App) -> bool {
194        assert!(pointer.iter().all(|v| v.is_finite()), "invalid pointer");
195        let started = self.0.borrow_mut().begin(pointer, window, cx);
196
197        emit_pending(&self.0, window, cx);
198
199        started
200    }
201
202    /// Uses another measured viewport for in-view checks, intersected with normal
203    /// ancestor clipping. Missing measurements preserve the previous visibility.
204    pub fn view_root(self, root: MotionAnchor) -> Self {
205        self.0.borrow_mut().view_root = Some(root);
206
207        self
208    }
209
210    /// Insets top/right/bottom/left visibility edges; negative values expand them.
211    pub fn view_margins(self, margins: [f64; 4]) -> Self {
212        assert!(
213            margins.iter().all(|v| v.is_finite() && v.abs() <= 1e12),
214            "invalid viewport margins"
215        );
216        self.0.borrow_mut().view_margins = margins;
217
218        self
219    }
220
221    /// Enables automatically measured clipped viewport visibility. Configure once.
222    pub fn in_view(self, amount: f64, once: bool) -> Self {
223        self.0.borrow_mut().visibility = Some(InView::new(amount, once));
224
225        self
226    }
227
228    /// Current resolved interaction flags, supplied automatically by the element.
229    pub fn interaction(&self) -> Interaction {
230        self.0.borrow().interaction
231    }
232
233    /// Last presented target, including drag displacement.
234    pub fn presented(&self) -> Target {
235        self.0.borrow().presented
236    }
237
238    /// The stable focus handle, available after the first layout.
239    pub fn focus_handle(&self) -> Option<FocusHandle> {
240        self.0.borrow().focus.clone()
241    }
242
243    /// Cancels pointer/key state and bounded dragging, notifying the owning view.
244    pub fn cancel(&self, cx: &mut App) {
245        let mut data = self.0.borrow_mut();
246
247        data.cancel(cx);
248        data.notify(cx);
249    }
250
251    /// Wraps a styled native control and automatically binds hover/press/focus,
252    /// viewport and optional drag. Supply role, label and click behavior on the
253    /// child. The adapter supplies a stable tab-stop focus handle. Configure
254    /// layout styles before binding; x/y are owned by this adapter, not a second
255    /// `LayoutGroup` on the same control. Native activation remains CE's behavior.
256    ///
257    /// ```
258    /// use gpui::{div, prelude::*, Role};
259    /// use gpui_ce_motion::{GestureState, GestureTargets, Motion, Target};
260    /// let state = GestureState::default(); // persistent view field
261    /// let control = state.bind("motion-button", div().id("button").role(Role::Button).child("Press"),
262    ///     Motion::new(Target::default()), GestureTargets::default().while_press(Target::default().opacity(0.5)));
263    /// ```
264    pub fn bind<E: IntoElement + StatefulInteractiveElement + Styled + 'static>(
265        &self,
266        id: impl Into<ElementId>,
267        child: E,
268        motion: Motion,
269        targets: GestureTargets,
270    ) -> GestureElement<E> {
271        GestureElement {
272            id: id.into(),
273            child: Some(child),
274            state: self.clone(),
275            motion,
276            targets,
277            enabled: true,
278            drag_listener: true,
279        }
280    }
281}
282
283/// Frame-scoped input/motion wrapper produced by `GestureState::bind`.
284pub struct GestureElement<E = Stateful<Div>> {
285    id: ElementId,
286    child: Option<E>,
287    state: GestureState,
288    motion: Motion,
289    targets: GestureTargets,
290    enabled: bool,
291    drag_listener: bool,
292}
293
294impl<E> GestureElement<E> {
295    /// Disables automatic target dragging so a separate handle can call `start_drag`.
296    pub fn drag_listener(mut self, enabled: bool) -> Self {
297        self.drag_listener = enabled;
298
299        self
300    }
301
302    /// Disables native input bindings and keyboard tab stops. Cancels a held
303    /// gesture on the next render; the owner restores focus if disabling the
304    /// focused control and supplies disabled accessibility metadata on the child
305    /// using its CE version’s APIs. Visibility observation remains available.
306    pub fn enabled(mut self, enabled: bool) -> Self {
307        self.enabled = enabled;
308
309        self
310    }
311}
312
313impl<E: IntoElement + StatefulInteractiveElement + Styled + 'static> IntoElement
314    for GestureElement<E>
315{
316    type Element = Self;
317
318    fn into_element(self) -> Self {
319        self
320    }
321}
322
323impl<E: IntoElement + StatefulInteractiveElement + Styled + 'static> Element for GestureElement<E> {
324    type RequestLayoutState = AnyElement;
325    type PrepaintState = Hitbox;
326
327    fn id(&self) -> Option<ElementId> {
328        Some(self.id.clone())
329    }
330
331    fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
332        None
333    }
334
335    fn request_layout(
336        &mut self,
337        _: Option<&GlobalElementId>,
338        _: Option<&InspectorElementId>,
339        w: &mut Window,
340        cx: &mut App,
341    ) -> (LayoutId, AnyElement) {
342        let mut data = self.state.0.borrow_mut();
343
344        data.owner = Some(w.current_view());
345        data.enabled = self.enabled;
346        data.epoch
347            .get_or_insert_with(|| cx.background_executor().now());
348        let focus = data
349            .focus
350            .get_or_insert_with(|| cx.focus_handle().tab_stop(true))
351            .clone()
352            .tab_stop(self.enabled);
353
354        let focused = self.enabled && focus.is_focused(w);
355
356        if !self.enabled
357            || !w.is_window_active()
358            || (!focused && (data.key.is_some() || data.pointer || data.pinch_active))
359        {
360            data.cancel(cx);
361        }
362
363        if !self.enabled || !w.is_window_active() {
364            data.interaction.hovered = false;
365        }
366
367        data.interaction.focused = focused;
368        let now = data.now(cx);
369        let target = self.targets.resolve(self.motion.target(), data.interaction);
370
371        let frame = data
372            .motion
373            .sample(self.motion.with_target(target), now, cx.reduce_motion());
374
375        let mut target = frame.target();
376        let mut active = frame.is_animating();
377
378        if let Some(drag) = &mut data.drag {
379            let offset = drag.sample(now, cx.reduce_motion());
380
381            active |= offset.is_animating();
382            let finite = |a: f32, b: f32| {
383                (f64::from(a) + f64::from(b)).clamp(f64::from(f32::MIN), f64::from(f32::MAX)) as f32
384            };
385
386            target = target
387                .x(finite(target.x_px(), offset.target().x_px()))
388                .y(finite(target.y_px(), offset.target().y_px()));
389        }
390
391        data.presented = target;
392
393        if active {
394            w.request_animation_frame();
395        }
396
397        let child = frame
398            .apply(
399                self.child
400                    .take()
401                    .expect("one layout")
402                    .track_focus(&focus)
403                    .tab_index(0),
404            )
405            .left(px(target.x_px()))
406            .top(px(target.y_px()));
407
408        let pending = data.pending_drag.is_some() || data.pending_pinch.is_some();
409
410        drop(data);
411
412        if pending {
413            let shared = self.state.0.clone();
414
415            w.defer(cx, move |w, cx| emit_pending(&shared, w, cx));
416        }
417
418        let mut child = child.into_any_element();
419        let layout = child.request_layout(w, cx);
420
421        (layout, child)
422    }
423
424    fn prepaint(
425        &mut self,
426        _: Option<&GlobalElementId>,
427        _: Option<&InspectorElementId>,
428        bounds: Bounds<Pixels>,
429        child: &mut AnyElement,
430        w: &mut Window,
431        cx: &mut App,
432    ) -> Hitbox {
433        let hitbox = w.insert_hitbox(bounds, HitboxBehavior::Normal);
434        let mut data = self.state.0.borrow_mut();
435
436        let rect = |r: Bounds<Pixels>| {
437            LayoutRect::new(
438                [f64::from(r.origin.x), f64::from(r.origin.y)],
439                [f64::from(r.size.width), f64::from(r.size.height)],
440            )
441            .expect("finite native bounds")
442        };
443
444        let now = data.now(cx);
445
446        if let Some(container) = data.constraints.as_ref().and_then(MotionAnchor::rect) {
447            let presented = data.presented;
448
449            let base = LayoutRect::new(
450                [
451                    f64::from(bounds.origin.x) - f64::from(presented.x_px()),
452                    f64::from(bounds.origin.y) - f64::from(presented.y_px()),
453                ],
454                [f64::from(bounds.size.width), f64::from(bounds.size.height)],
455            )
456            .expect("finite bounds");
457
458            let limits = DragBounds::within(container, base);
459
460            if let Some(drag) = &mut data.drag {
461                let previous = drag.presentation().0;
462
463                drag.set_bounds(limits.min(), limits.max(), now);
464                let frame = drag.sample(now, cx.reduce_motion());
465
466                if frame.is_animating() || previous != drag.presentation().0 {
467                    w.request_animation_frame();
468                }
469            }
470        }
471
472        let clip = rect(w.content_mask().bounds);
473
474        let viewport = data
475            .view_root
476            .as_ref()
477            .map_or(Some(clip), MotionAnchor::rect)
478            .map(|root| root.inset(data.view_margins).intersection(clip));
479
480        if let (Some(visibility), Some(viewport)) = (&mut data.visibility, viewport) {
481            let viewport = Bounds {
482                origin: gpui::point(
483                    px(viewport.origin()[0] as f32),
484                    px(viewport.origin()[1] as f32),
485                ),
486                size: gpui::size(px(viewport.size()[0] as f32), px(viewport.size()[1] as f32)),
487            };
488
489            let rect = |r: Bounds<Pixels>| {
490                [
491                    f64::from(r.origin.x),
492                    f64::from(r.origin.y),
493                    f64::from(r.size.width),
494                    f64::from(r.size.height),
495                ]
496            };
497
498            if visibility.update(rect(viewport), rect(bounds)) {
499                data.interaction.in_view = visibility.is_visible();
500                let owner = data.owner.expect("owner");
501
502                w.defer(cx, move |_, cx| cx.notify(owner));
503            }
504        }
505
506        drop(data);
507        child.prepaint(w, cx);
508
509        hitbox
510    }
511
512    fn paint(
513        &mut self,
514        _: Option<&GlobalElementId>,
515        _: Option<&InspectorElementId>,
516        _: Bounds<Pixels>,
517        child: &mut AnyElement,
518        hitbox: &mut Hitbox,
519        w: &mut Window,
520        cx: &mut App,
521    ) {
522        if !self.enabled {
523            let region = hitbox.clone();
524
525            w.on_mouse_event(move |_: &MouseDownEvent, phase, w, cx| {
526                if phase == DispatchPhase::Capture && region.is_hovered(w) {
527                    cx.stop_propagation();
528                }
529            });
530            let region = hitbox.clone();
531
532            w.on_mouse_event(move |_: &MouseUpEvent, phase, w, cx| {
533                if phase == DispatchPhase::Capture && region.is_hovered(w) {
534                    cx.stop_propagation();
535                }
536            });
537            let region = hitbox.clone();
538
539            w.on_mouse_event(move |_: &MouseMoveEvent, phase, w, cx| {
540                if phase == DispatchPhase::Capture && region.is_hovered(w) {
541                    cx.stop_propagation();
542                }
543            });
544            let focus = self.state.focus_handle();
545
546            w.on_key_event(move |_: &KeyDownEvent, phase, w, cx| {
547                if phase == DispatchPhase::Capture
548                    && focus.as_ref().is_some_and(|f| f.is_focused(w))
549                {
550                    cx.stop_propagation();
551                }
552            });
553            let focus = self.state.focus_handle();
554
555            w.on_key_event(move |_: &KeyUpEvent, phase, w, cx| {
556                if phase == DispatchPhase::Capture
557                    && focus.as_ref().is_some_and(|f| f.is_focused(w))
558                {
559                    cx.stop_propagation();
560                }
561            });
562            child.paint(w, cx);
563
564            return;
565        }
566
567        let shared = self.state.0.clone();
568        let region = hitbox.clone();
569
570        let automatic_drag = self.drag_listener;
571
572        w.on_mouse_event(move |event: &MouseDownEvent, phase, w, cx| {
573            if phase == DispatchPhase::Bubble
574                && event.button == MouseButton::Left
575                && region.is_hovered(w)
576                && automatic_drag
577            {
578                let mut data = shared.borrow_mut();
579
580                if data.begin(
581                    [f64::from(event.position.x), f64::from(event.position.y)],
582                    w,
583                    cx,
584                ) && (data.drag.is_some() || data.drag_callback.is_some())
585                {
586                    cx.stop_propagation();
587                }
588
589                drop(data);
590                emit_pending(&shared, w, cx);
591            }
592        });
593        let shared = self.state.0.clone();
594        let region = hitbox.clone();
595
596        w.on_mouse_event(move |event: &MouseMoveEvent, phase, w, cx| {
597            if phase != DispatchPhase::Capture {
598                return;
599            }
600
601            let mut data = shared.borrow_mut();
602            let hover = region.is_hovered(w);
603            let changed = data.interaction.hovered != hover;
604
605            data.interaction.hovered = hover;
606
607            if data.pointer && !data.touch {
608                let now = data.now(cx);
609
610                if event.pressed_button != Some(MouseButton::Left) {
611                    data.cancel(cx);
612                } else if let Some(drag) = &mut data.drag {
613                    drag.move_to(
614                        [f64::from(event.position.x), f64::from(event.position.y)],
615                        now,
616                    );
617                }
618
619                if data.pointer {
620                    data.record(
621                        GesturePhase::Moved,
622                        [f64::from(event.position.x), f64::from(event.position.y)],
623                        now,
624                    );
625                }
626
627                data.notify(cx);
628            } else if changed {
629                data.notify(cx);
630            }
631
632            drop(data);
633            emit_pending(&shared, w, cx);
634        });
635        let shared = self.state.0.clone();
636
637        w.on_mouse_event(move |event: &MouseUpEvent, phase, w, cx| {
638            if phase == DispatchPhase::Capture && event.button == MouseButton::Left {
639                let mut data = shared.borrow_mut();
640
641                if !data.pointer || data.touch {
642                    return;
643                }
644
645                let now = data.now(cx);
646
647                if let Some(drag) = &mut data.drag {
648                    drag.end(now);
649                }
650
651                data.record(
652                    GesturePhase::Ended,
653                    [f64::from(event.position.x), f64::from(event.position.y)],
654                    now,
655                );
656                data.pointer = false;
657                data.interaction.pressed = false;
658                data.interaction.dragging = false;
659                data.notify(cx);
660                drop(data);
661                emit_pending(&shared, w, cx);
662            }
663        });
664        let shared = self.state.0.clone();
665
666        w.on_mouse_event(move |_: &MouseExitEvent, phase, _, cx| {
667            if phase == DispatchPhase::Capture {
668                let mut data = shared.borrow_mut();
669
670                if data.interaction.hovered {
671                    data.interaction.hovered = false;
672                    data.notify(cx);
673                }
674            }
675        });
676        let shared = self.state.0.clone();
677
678        w.on_key_event(move |event: &KeyDownEvent, phase, w, cx| {
679            if phase != DispatchPhase::Capture {
680                return;
681            }
682
683            let mut data = shared.borrow_mut();
684
685            if !data.focus.as_ref().is_some_and(|f| f.is_focused(w)) {
686                return;
687            }
688
689            let key = event.keystroke.key.as_str();
690            let now = data.now(cx);
691
692            if key == "escape" {
693                data.cancel(cx);
694                data.notify(cx);
695                drop(data);
696                emit_pending(&shared, w, cx);
697            } else if ["enter", "space"].contains(&key) && !event.is_held && !data.pointer {
698                data.key = Some(key.to_owned());
699                data.interaction.pressed = true;
700                data.notify(cx);
701            } else if let Some(delta) = match key {
702                "left" => Some([-10., 0.]),
703                "right" => Some([10., 0.]),
704                "up" => Some([0., -10.]),
705                "down" => Some([0., 10.]),
706                _ => None,
707            } && let Some(drag) = &mut data.drag
708            {
709                drag.move_by(delta, now);
710                data.notify(cx);
711                cx.stop_propagation();
712            }
713        });
714        let shared = self.state.0.clone();
715
716        w.on_key_event(move |event: &KeyUpEvent, phase, _, cx| {
717            if phase == DispatchPhase::Capture {
718                let mut data = shared.borrow_mut();
719
720                if data.key.as_deref() == Some(event.keystroke.key.as_str()) {
721                    data.key = None;
722                    data.interaction.pressed = data.pointer;
723                    data.notify(cx);
724                }
725            }
726        });
727        let shared = self.state.0.clone();
728        let region = hitbox.clone();
729
730        w.on_mouse_event(move |event: &PinchEvent, phase, w, cx| {
731            if phase != DispatchPhase::Bubble || !event.delta.is_finite() {
732                return;
733            }
734
735            let mut data = shared.borrow_mut();
736
737            if data.pinch_callback.is_none() {
738                return;
739            }
740
741            if event.phase == TouchPhase::Started {
742                if !region.is_hovered(w) {
743                    return;
744                }
745
746                data.pinch_active = true;
747                data.pinch_scale = 1.0;
748
749                if let Some(focus) = &data.focus {
750                    focus.focus(w, cx);
751                }
752            }
753
754            if !data.pinch_active {
755                return;
756            }
757
758            data.pinch_scale = (data.pinch_scale * (1.0 + f64::from(event.delta))).clamp(0.0, 1e6);
759            data.pinch_center = [f64::from(event.position.x), f64::from(event.position.y)];
760            let phase = match event.phase {
761                TouchPhase::Started => GesturePhase::Started,
762                TouchPhase::Moved => GesturePhase::Moved,
763                TouchPhase::Ended => GesturePhase::Ended,
764                TouchPhase::Cancelled => GesturePhase::Cancelled,
765            };
766
767            data.pending_pinch = Some(PinchGesture {
768                phase,
769                center: data.pinch_center,
770                scale: data.pinch_scale,
771                delta: f64::from(event.delta),
772            });
773
774            if matches!(event.phase, TouchPhase::Ended | TouchPhase::Cancelled) {
775                data.pinch_active = false;
776            }
777
778            data.notify(cx);
779            drop(data);
780            emit_pending(&shared, w, cx);
781            cx.stop_propagation();
782        });
783        // Register key listeners on this wrapper before child paint selects a descendant node.
784        child.paint(w, cx);
785    }
786}
787
788fn emit_pending(shared: &Rc<RefCell<Data>>, window: &mut Window, cx: &mut App) {
789    let (drag, drag_callback, pinch, pinch_callback) = {
790        let mut data = shared.borrow_mut();
791
792        (
793            data.pending_drag.take(),
794            data.drag_callback.clone(),
795            data.pending_pinch.take(),
796            data.pinch_callback.clone(),
797        )
798    };
799
800    if let (Some(event), Some(callback)) = (drag, drag_callback) {
801        callback(event, window, cx);
802    }
803
804    if let (Some(event), Some(callback)) = (pinch, pinch_callback) {
805        callback(event, window, cx);
806    }
807}