Skip to main content

cranpose_ui/modifier/
pointer_input.rs

1use super::{inspector_metadata, Modifier, PointerEvent};
2use cranpose_core::hash::default;
3use cranpose_foundation::{
4    impl_pointer_input_node, DelegatableNode, ModifierNode, ModifierNodeContext,
5    ModifierNodeElement, NodeCapabilities, NodeState, PointerInputNode,
6};
7use cranpose_ui_graphics::Size;
8use futures_task::{waker, ArcWake};
9use std::any::TypeId;
10use std::cell::{Cell, RefCell};
11use std::collections::{HashMap, VecDeque};
12use std::fmt;
13use std::future::Future;
14use std::hash::{Hash, Hasher};
15use std::pin::Pin;
16use std::rc::Rc;
17use std::sync::Arc;
18use std::task::{Context, Poll, Waker};
19
20impl Modifier {
21    pub fn pointer_input<K, F, Fut>(self, key: K, handler: F) -> Self
22    where
23        K: Hash + 'static,
24        F: Fn(PointerInputScope) -> Fut + 'static,
25        Fut: Future<Output = ()> + 'static,
26    {
27        let element =
28            PointerInputElement::new(vec![KeyToken::new(&key)], pointer_input_handler(handler));
29        let key_count = element.key_count();
30        let handler_id = element.handler_id();
31        self.then(
32            Self::with_element(element).with_inspector_metadata(inspector_metadata(
33                "pointerInput",
34                move |info| {
35                    info.add_property("keyCount", key_count.to_string());
36                    info.add_property("handlerId", handler_id.to_string());
37                },
38            )),
39        )
40    }
41}
42
43fn pointer_input_handler<F, Fut>(handler: F) -> PointerInputHandler
44where
45    F: Fn(PointerInputScope) -> Fut + 'static,
46    Fut: Future<Output = ()> + 'static,
47{
48    Rc::new(move |scope| Box::pin(handler(scope.clone())))
49}
50
51type PointerInputFuture = Pin<Box<dyn Future<Output = ()>>>;
52type PointerInputHandler = Rc<dyn Fn(PointerInputScope) -> PointerInputFuture>;
53
54#[derive(Clone, Copy, Debug, PartialEq, Eq)]
55pub(crate) enum PointerInputTaskOwner {
56    App(crate::render_state::AppContextId),
57}
58
59pub(crate) struct PointerInputTaskRegistry {
60    tasks: RefCell<HashMap<u64, Rc<PointerInputTaskInner>>>,
61}
62
63impl PointerInputTaskRegistry {
64    pub(crate) fn new() -> Self {
65        Self {
66            tasks: RefCell::new(HashMap::new()),
67        }
68    }
69
70    pub(crate) fn insert(&self, task_id: u64, task: Rc<PointerInputTaskInner>) {
71        self.tasks.borrow_mut().insert(task_id, task);
72    }
73
74    pub(crate) fn remove(&self, task_id: u64) {
75        self.tasks.borrow_mut().remove(&task_id);
76    }
77
78    pub(crate) fn request_poll(&self, task_id: u64, owner: PointerInputTaskOwner) {
79        if let Some(task) = self.tasks.borrow().get(&task_id).cloned() {
80            task.request_poll(owner, task_id);
81        }
82    }
83}
84
85#[derive(Clone)]
86struct PointerInputElement {
87    keys: Vec<KeyToken>,
88    handler: PointerInputHandler,
89    handler_id: u64,
90}
91
92impl PointerInputElement {
93    fn new(keys: Vec<KeyToken>, handler: PointerInputHandler) -> Self {
94        let handler_id = pointer_handler_identity(&handler);
95        Self {
96            keys,
97            handler,
98            handler_id,
99        }
100    }
101
102    fn key_count(&self) -> usize {
103        self.keys.len()
104    }
105
106    fn handler_id(&self) -> u64 {
107        self.handler_id
108    }
109}
110
111fn pointer_handler_identity(handler: &PointerInputHandler) -> u64 {
112    Rc::as_ptr(handler) as *const () as usize as u64
113}
114
115impl fmt::Debug for PointerInputElement {
116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117        f.debug_struct("PointerInputElement")
118            .field("keys", &self.keys)
119            .field("handler", &Rc::as_ptr(&self.handler))
120            .field("handler_id", &self.handler_id)
121            .finish()
122    }
123}
124
125impl PartialEq for PointerInputElement {
126    fn eq(&self, other: &Self) -> bool {
127        // Only compare keys, not handler_id. In Compose, elements are equal if their
128        // keys match, even if the handler closure is recreated on recomposition.
129        // This ensures nodes are reused instead of being dropped and recreated.
130        self.keys == other.keys
131    }
132}
133
134impl Eq for PointerInputElement {}
135
136impl Hash for PointerInputElement {
137    fn hash<H: Hasher>(&self, state: &mut H) {
138        // Only hash keys, not handler_id. This ensures stable hashing across
139        // recompositions when the closure is recreated but keys remain the same.
140        self.keys.hash(state);
141    }
142}
143
144impl ModifierNodeElement for PointerInputElement {
145    type Node = SuspendingPointerInputNode;
146
147    fn create(&self) -> Self::Node {
148        SuspendingPointerInputNode::new(self.keys.clone(), self.handler.clone())
149    }
150
151    fn update(&self, node: &mut Self::Node) {
152        node.update(self.keys.clone(), self.handler.clone());
153    }
154
155    fn capabilities(&self) -> NodeCapabilities {
156        NodeCapabilities::POINTER_INPUT
157    }
158}
159
160#[derive(Clone)]
161pub struct PointerInputScope {
162    state: Rc<PointerInputScopeState>,
163}
164
165impl PointerInputScope {
166    fn new(state: Rc<PointerInputScopeState>) -> Self {
167        Self { state }
168    }
169
170    /// The size of the layout node this handler is attached to, in the same
171    /// local coordinate space as the [`PointerEvent`] positions the scope
172    /// delivers (origin at the node's top-left).
173    ///
174    /// The layout pass publishes the node's resolved size every pass, so this
175    /// is current from the first laid-out frame onwards — including before any
176    /// pointer event has arrived. It is `0x0` only while the node has never
177    /// been laid out.
178    pub fn size(&self) -> Size {
179        self.state.size.get()
180    }
181
182    pub async fn await_pointer_event_scope<R, F, Fut>(&self, block: F) -> R
183    where
184        F: FnOnce(AwaitPointerEventScope) -> Fut,
185        Fut: Future<Output = R>,
186    {
187        let scope = AwaitPointerEventScope {
188            state: self.state.clone(),
189        };
190        block(scope).await
191    }
192}
193
194#[derive(Clone)]
195pub struct AwaitPointerEventScope {
196    state: Rc<PointerInputScopeState>,
197}
198
199impl AwaitPointerEventScope {
200    /// The size of the layout node this handler is attached to. See
201    /// [`PointerInputScope::size`].
202    pub fn size(&self) -> Size {
203        self.state.size.get()
204    }
205
206    pub async fn await_pointer_event(&self) -> PointerEvent {
207        NextPointerEvent {
208            state: self.state.clone(),
209        }
210        .await
211    }
212
213    pub async fn with_timeout_or_null<R, F, Fut>(&self, _time_millis: u64, block: F) -> Option<R>
214    where
215        F: FnOnce(&AwaitPointerEventScope) -> Fut,
216        Fut: Future<Output = R>,
217    {
218        Some(block(self).await)
219    }
220
221    pub async fn with_timeout<R, F, Fut>(&self, _time_millis: u64, block: F) -> R
222    where
223        F: FnOnce(&AwaitPointerEventScope) -> Fut,
224        Fut: Future<Output = R>,
225    {
226        block(self).await
227    }
228}
229
230struct NextPointerEvent {
231    state: Rc<PointerInputScopeState>,
232}
233
234impl Future for NextPointerEvent {
235    type Output = PointerEvent;
236
237    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
238        self.state.poll_event(cx)
239    }
240}
241
242struct PointerInputScopeState {
243    events: RefCell<VecDeque<PointerEvent>>,
244    waiting: RefCell<Option<Waker>>,
245    /// Shared with the owning [`SuspendingPointerInputNode`] (and therefore
246    /// with every scope the node ever hands out): the layout pass publishes the
247    /// node's resolved size into this cell, so `scope.size()` reports live
248    /// dimensions rather than the `0x0` a per-scope cell would be stuck at.
249    size: Rc<Cell<Size>>,
250}
251
252impl PointerInputScopeState {
253    fn new(size: Rc<Cell<Size>>) -> Self {
254        Self {
255            events: RefCell::new(VecDeque::new()),
256            waiting: RefCell::new(None),
257            size,
258        }
259    }
260
261    fn push_event(&self, event: PointerEvent) {
262        self.events.borrow_mut().push_back(event);
263        let waker = {
264            let mut waiting = self.waiting.borrow_mut();
265            waiting.take()
266        };
267        if let Some(waker) = waker {
268            waker.wake();
269        }
270    }
271
272    fn poll_event(&self, cx: &mut Context<'_>) -> Poll<PointerEvent> {
273        if let Some(event) = self.events.borrow_mut().pop_front() {
274            Poll::Ready(event)
275        } else {
276            self.waiting.replace(Some(cx.waker().clone()));
277            Poll::Pending
278        }
279    }
280}
281
282struct PointerEventDispatcher {
283    state: Rc<RefCell<Option<Rc<PointerInputScopeState>>>>,
284    handler: Rc<dyn Fn(PointerEvent)>,
285}
286
287impl PointerEventDispatcher {
288    fn new() -> Self {
289        let state = Rc::new(RefCell::new(None::<Rc<PointerInputScopeState>>));
290        let state_for_handler = state.clone();
291        let handler = Rc::new(move |event: PointerEvent| {
292            if let Some(inner) = state_for_handler.borrow().as_ref() {
293                inner.push_event(event);
294            }
295        });
296        Self { state, handler }
297    }
298
299    fn handler(&self) -> Rc<dyn Fn(PointerEvent)> {
300        self.handler.clone()
301    }
302
303    fn set_state(&self, state: Option<Rc<PointerInputScopeState>>) {
304        *self.state.borrow_mut() = state;
305    }
306}
307
308struct PointerInputTask {
309    id: u64,
310    owner: PointerInputTaskOwner,
311    inner: Rc<PointerInputTaskInner>,
312}
313
314impl PointerInputTask {
315    fn new(future: PointerInputFuture) -> Self {
316        let inner = Rc::new(PointerInputTaskInner::new(future));
317        let id = Rc::as_ptr(&inner) as usize as u64;
318        let owner = crate::render_state::register_pointer_input_task(id, inner.clone());
319        Self { id, owner, inner }
320    }
321
322    fn poll(&self) {
323        self.inner.poll(self.owner, self.id);
324    }
325
326    fn cancel(self) {
327        self.inner.cancel();
328        crate::render_state::remove_pointer_input_task(self.owner, self.id);
329    }
330}
331
332impl Drop for PointerInputTask {
333    fn drop(&mut self) {
334        self.inner.cancel();
335        crate::render_state::remove_pointer_input_task(self.owner, self.id);
336    }
337}
338
339pub(crate) struct PointerInputTaskInner {
340    future: RefCell<Option<PointerInputFuture>>,
341    is_polling: Cell<bool>,
342    needs_poll: Cell<bool>,
343}
344
345impl PointerInputTaskInner {
346    fn new(future: PointerInputFuture) -> Self {
347        Self {
348            future: RefCell::new(Some(future)),
349            is_polling: Cell::new(false),
350            needs_poll: Cell::new(false),
351        }
352    }
353
354    fn cancel(&self) {
355        self.future.borrow_mut().take();
356    }
357
358    fn request_poll(&self, owner: PointerInputTaskOwner, task_id: u64) {
359        if self.is_polling.get() {
360            self.needs_poll.set(true);
361        } else {
362            self.poll(owner, task_id);
363        }
364    }
365
366    fn poll(&self, owner: PointerInputTaskOwner, task_id: u64) {
367        if self.is_polling.replace(true) {
368            self.needs_poll.set(true);
369            return;
370        }
371        loop {
372            self.needs_poll.set(false);
373            let waker = waker(Arc::new(PointerInputTaskWaker { task_id, owner }));
374            let mut cx = Context::from_waker(&waker);
375            let mut future_slot = self.future.borrow_mut();
376            if let Some(future) = future_slot.as_mut() {
377                let poll_result = future.as_mut().poll(&mut cx);
378                if poll_result.is_ready() {
379                    future_slot.take();
380                }
381            }
382            if !self.needs_poll.get() {
383                break;
384            }
385        }
386        self.is_polling.set(false);
387    }
388}
389
390struct PointerInputTaskWaker {
391    task_id: u64,
392    owner: PointerInputTaskOwner,
393}
394
395impl ArcWake for PointerInputTaskWaker {
396    fn wake_by_ref(arc_self: &Arc<Self>) {
397        crate::render_state::request_pointer_input_task_poll(arc_self.owner, arc_self.task_id);
398    }
399}
400
401pub struct SuspendingPointerInputNode {
402    keys: Vec<KeyToken>,
403    handler: PointerInputHandler,
404    dispatcher: PointerEventDispatcher,
405    task: Option<PointerInputTask>,
406    /// The node's resolved layout size, published by the layout pass through
407    /// [`PointerInputNode::layout_size_sink`]. Lives on the node rather than on
408    /// the scope state so it survives handler restarts (a key change recreates
409    /// the scope but not the node, and the size has not changed).
410    layout_size: Rc<Cell<Size>>,
411    state: NodeState,
412}
413
414impl SuspendingPointerInputNode {
415    fn new(keys: Vec<KeyToken>, handler: PointerInputHandler) -> Self {
416        Self {
417            keys,
418            handler,
419            dispatcher: PointerEventDispatcher::new(),
420            task: None,
421            layout_size: Rc::new(Cell::new(Size {
422                width: 0.0,
423                height: 0.0,
424            })),
425            state: NodeState::new(),
426        }
427    }
428
429    fn update(&mut self, keys: Vec<KeyToken>, handler: PointerInputHandler) {
430        // Only restart if keys changed - not if handler Rc pointer changed.
431        // In Compose, closures are recreated every composition but the task should
432        // continue running as long as the keys are the same. This matches Jetpack
433        // Compose behavior where rememberUpdatedState keeps the task alive.
434        let should_restart = self.keys != keys;
435        self.keys = keys;
436        self.handler = handler; // Update handler even if not restarting
437        if should_restart {
438            self.restart();
439        }
440    }
441
442    fn restart(&mut self) {
443        self.cancel();
444        self.start();
445    }
446
447    fn start(&mut self) {
448        let state = Rc::new(PointerInputScopeState::new(self.layout_size.clone()));
449        self.dispatcher.set_state(Some(state.clone()));
450        let scope = PointerInputScope::new(state);
451        let future = (self.handler)(scope);
452        let task = PointerInputTask::new(future);
453        task.poll();
454        self.task = Some(task);
455    }
456
457    fn cancel(&mut self) {
458        if let Some(task) = self.task.take() {
459            task.cancel();
460        }
461        self.dispatcher.set_state(None);
462    }
463}
464
465impl Drop for SuspendingPointerInputNode {
466    fn drop(&mut self) {
467        self.cancel();
468    }
469}
470
471impl ModifierNode for SuspendingPointerInputNode {
472    fn on_attach(&mut self, _context: &mut dyn ModifierNodeContext) {
473        self.start();
474    }
475
476    fn on_detach(&mut self) {
477        self.cancel();
478    }
479
480    fn on_reset(&mut self) {
481        // Don't restart on reset - only restart when keys/handler actually change
482        // (which is handled by update() method). Restarting here would kill the
483        // active task and lose its registered waker, preventing events from being delivered.
484    }
485
486    // Capability-driven implementation using helper macro
487    impl_pointer_input_node!();
488}
489
490impl DelegatableNode for SuspendingPointerInputNode {
491    fn node_state(&self) -> &NodeState {
492        &self.state
493    }
494}
495
496impl PointerInputNode for SuspendingPointerInputNode {
497    fn pointer_input_handler(&self) -> Option<Rc<dyn Fn(PointerEvent)>> {
498        Some(self.dispatcher.handler())
499    }
500
501    fn layout_size_sink(&self) -> Option<Rc<Cell<Size>>> {
502        Some(self.layout_size.clone())
503    }
504}
505
506#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
507struct KeyToken {
508    type_id: TypeId,
509    hash: u64,
510}
511
512impl KeyToken {
513    fn new<T: Hash + 'static>(value: &T) -> Self {
514        let mut hasher = default::new();
515        value.hash(&mut hasher);
516        Self {
517            type_id: TypeId::of::<T>(),
518            hash: hasher.finish(),
519        }
520    }
521}