Skip to main content

cranpose_ui/modifier/
pointer_input.rs

1use super::{inspector_metadata, Modifier, PointerEvent};
2use cranpose_foundation::{
3    impl_pointer_input_node, DelegatableNode, ModifierNode, ModifierNodeContext,
4    ModifierNodeElement, NodeCapabilities, NodeState, PointerInputNode,
5};
6use cranpose_ui_graphics::Size;
7use futures_task::{waker, ArcWake};
8use std::any::TypeId;
9use std::cell::{Cell, RefCell};
10use std::collections::{hash_map::DefaultHasher, HashMap, VecDeque};
11use std::fmt;
12use std::future::Future;
13use std::hash::{Hash, Hasher};
14use std::pin::Pin;
15use std::rc::Rc;
16use std::sync::atomic::{AtomicU64, Ordering};
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
54thread_local! {
55    static POINTER_INPUT_TASKS: RefCell<HashMap<u64, Rc<PointerInputTaskInner>>> = RefCell::new(HashMap::new());
56}
57
58#[derive(Clone)]
59struct PointerInputElement {
60    keys: Vec<KeyToken>,
61    handler: PointerInputHandler,
62    handler_id: u64,
63}
64
65impl PointerInputElement {
66    fn new(keys: Vec<KeyToken>, handler: PointerInputHandler) -> Self {
67        static NEXT_HANDLER_ID: AtomicU64 = AtomicU64::new(1);
68        let handler_id = NEXT_HANDLER_ID.fetch_add(1, Ordering::Relaxed);
69        Self {
70            keys,
71            handler,
72            handler_id,
73        }
74    }
75
76    fn key_count(&self) -> usize {
77        self.keys.len()
78    }
79
80    fn handler_id(&self) -> u64 {
81        self.handler_id
82    }
83}
84
85impl fmt::Debug for PointerInputElement {
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        f.debug_struct("PointerInputElement")
88            .field("keys", &self.keys)
89            .field("handler", &Rc::as_ptr(&self.handler))
90            .field("handler_id", &self.handler_id)
91            .finish()
92    }
93}
94
95impl PartialEq for PointerInputElement {
96    fn eq(&self, other: &Self) -> bool {
97        // Only compare keys, not handler_id. In Compose, elements are equal if their
98        // keys match, even if the handler closure is recreated on recomposition.
99        // This ensures nodes are reused instead of being dropped and recreated.
100        self.keys == other.keys
101    }
102}
103
104impl Eq for PointerInputElement {}
105
106impl Hash for PointerInputElement {
107    fn hash<H: Hasher>(&self, state: &mut H) {
108        // Only hash keys, not handler_id. This ensures stable hashing across
109        // recompositions when the closure is recreated but keys remain the same.
110        self.keys.hash(state);
111    }
112}
113
114impl ModifierNodeElement for PointerInputElement {
115    type Node = SuspendingPointerInputNode;
116
117    fn create(&self) -> Self::Node {
118        SuspendingPointerInputNode::new(self.keys.clone(), self.handler.clone())
119    }
120
121    fn update(&self, node: &mut Self::Node) {
122        node.update(self.keys.clone(), self.handler.clone());
123    }
124
125    fn capabilities(&self) -> NodeCapabilities {
126        NodeCapabilities::POINTER_INPUT
127    }
128}
129
130#[derive(Clone)]
131pub struct PointerInputScope {
132    state: Rc<PointerInputScopeState>,
133}
134
135impl PointerInputScope {
136    fn new(state: Rc<PointerInputScopeState>) -> Self {
137        Self { state }
138    }
139
140    pub fn size(&self) -> Size {
141        self.state.size.get()
142    }
143
144    pub async fn await_pointer_event_scope<R, F, Fut>(&self, block: F) -> R
145    where
146        F: FnOnce(AwaitPointerEventScope) -> Fut,
147        Fut: Future<Output = R>,
148    {
149        let scope = AwaitPointerEventScope {
150            state: self.state.clone(),
151        };
152        block(scope).await
153    }
154}
155
156#[derive(Clone)]
157pub struct AwaitPointerEventScope {
158    state: Rc<PointerInputScopeState>,
159}
160
161impl AwaitPointerEventScope {
162    pub fn size(&self) -> Size {
163        self.state.size.get()
164    }
165
166    pub async fn await_pointer_event(&self) -> PointerEvent {
167        NextPointerEvent {
168            state: self.state.clone(),
169        }
170        .await
171    }
172
173    pub async fn with_timeout_or_null<R, F, Fut>(&self, _time_millis: u64, block: F) -> Option<R>
174    where
175        F: FnOnce(&AwaitPointerEventScope) -> Fut,
176        Fut: Future<Output = R>,
177    {
178        Some(block(self).await)
179    }
180
181    pub async fn with_timeout<R, F, Fut>(&self, _time_millis: u64, block: F) -> R
182    where
183        F: FnOnce(&AwaitPointerEventScope) -> Fut,
184        Fut: Future<Output = R>,
185    {
186        block(self).await
187    }
188}
189
190struct NextPointerEvent {
191    state: Rc<PointerInputScopeState>,
192}
193
194impl Future for NextPointerEvent {
195    type Output = PointerEvent;
196
197    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
198        self.state.poll_event(cx)
199    }
200}
201
202struct PointerInputScopeState {
203    events: RefCell<VecDeque<PointerEvent>>,
204    waiting: RefCell<Option<Waker>>,
205    size: Cell<Size>,
206}
207
208impl PointerInputScopeState {
209    fn new() -> Self {
210        Self {
211            events: RefCell::new(VecDeque::new()),
212            waiting: RefCell::new(None),
213            size: Cell::new(Size {
214                width: 0.0,
215                height: 0.0,
216            }),
217        }
218    }
219
220    fn push_event(&self, event: PointerEvent) {
221        self.events.borrow_mut().push_back(event);
222        let waker = {
223            let mut waiting = self.waiting.borrow_mut();
224            waiting.take()
225        };
226        if let Some(waker) = waker {
227            waker.wake();
228        }
229    }
230
231    fn poll_event(&self, cx: &mut Context<'_>) -> Poll<PointerEvent> {
232        if let Some(event) = self.events.borrow_mut().pop_front() {
233            Poll::Ready(event)
234        } else {
235            self.waiting.replace(Some(cx.waker().clone()));
236            Poll::Pending
237        }
238    }
239}
240
241struct PointerEventDispatcher {
242    state: Rc<RefCell<Option<Rc<PointerInputScopeState>>>>,
243    handler: Rc<dyn Fn(PointerEvent)>,
244}
245
246impl PointerEventDispatcher {
247    fn new() -> Self {
248        let state = Rc::new(RefCell::new(None::<Rc<PointerInputScopeState>>));
249        let state_for_handler = state.clone();
250        let handler = Rc::new(move |event: PointerEvent| {
251            if let Some(inner) = state_for_handler.borrow().as_ref() {
252                inner.push_event(event);
253            }
254        });
255        Self { state, handler }
256    }
257
258    fn handler(&self) -> Rc<dyn Fn(PointerEvent)> {
259        self.handler.clone()
260    }
261
262    fn set_state(&self, state: Option<Rc<PointerInputScopeState>>) {
263        *self.state.borrow_mut() = state;
264    }
265}
266
267struct PointerInputTask {
268    id: u64,
269    inner: Rc<PointerInputTaskInner>,
270}
271
272impl PointerInputTask {
273    fn new(future: PointerInputFuture) -> Self {
274        static NEXT_TASK_ID: AtomicU64 = AtomicU64::new(1);
275        let id = NEXT_TASK_ID.fetch_add(1, Ordering::Relaxed);
276        let inner = Rc::new(PointerInputTaskInner::new(future));
277        POINTER_INPUT_TASKS.with(|registry| {
278            registry.borrow_mut().insert(id, inner.clone());
279        });
280        Self { id, inner }
281    }
282
283    fn poll(&self) {
284        self.inner.poll(self.id);
285    }
286
287    fn cancel(self) {
288        self.inner.cancel();
289        POINTER_INPUT_TASKS.with(|registry| {
290            registry.borrow_mut().remove(&self.id);
291        });
292    }
293}
294
295impl Drop for PointerInputTask {
296    fn drop(&mut self) {
297        // Don't remove from registry here! The registry holds a strong Rc<PointerInputTaskInner>,
298        // so the inner will stay alive even if this PointerInputTask wrapper is dropped.
299        // This is intentional - tasks created by temporary modifier chains (used for slice collection)
300        // will have their PointerInputTask dropped, but the inner task needs to stay alive in the
301        // registry so that wakers can still find and wake it.
302        // Tasks are only removed from the registry when explicitly cancelled via cancel().
303    }
304}
305
306struct PointerInputTaskInner {
307    future: RefCell<Option<PointerInputFuture>>,
308    is_polling: Cell<bool>,
309    needs_poll: Cell<bool>,
310}
311
312impl PointerInputTaskInner {
313    fn new(future: PointerInputFuture) -> Self {
314        Self {
315            future: RefCell::new(Some(future)),
316            is_polling: Cell::new(false),
317            needs_poll: Cell::new(false),
318        }
319    }
320
321    fn cancel(&self) {
322        self.future.borrow_mut().take();
323    }
324
325    fn request_poll(&self, task_id: u64) {
326        if self.is_polling.get() {
327            self.needs_poll.set(true);
328        } else {
329            self.poll(task_id);
330        }
331    }
332
333    fn poll(&self, task_id: u64) {
334        if self.is_polling.replace(true) {
335            self.needs_poll.set(true);
336            return;
337        }
338        loop {
339            self.needs_poll.set(false);
340            let waker = waker(Arc::new(PointerInputTaskWaker { task_id }));
341            let mut cx = Context::from_waker(&waker);
342            let mut future_slot = self.future.borrow_mut();
343            if let Some(future) = future_slot.as_mut() {
344                let poll_result = future.as_mut().poll(&mut cx);
345                if poll_result.is_ready() {
346                    future_slot.take();
347                }
348            }
349            if !self.needs_poll.get() {
350                break;
351            }
352        }
353        self.is_polling.set(false);
354    }
355}
356
357struct PointerInputTaskWaker {
358    task_id: u64,
359}
360
361impl ArcWake for PointerInputTaskWaker {
362    fn wake_by_ref(arc_self: &Arc<Self>) {
363        POINTER_INPUT_TASKS.with(|registry| {
364            if let Some(task) = registry.borrow().get(&arc_self.task_id).cloned() {
365                task.request_poll(arc_self.task_id);
366            }
367        });
368    }
369}
370
371pub struct SuspendingPointerInputNode {
372    keys: Vec<KeyToken>,
373    handler: PointerInputHandler,
374    dispatcher: PointerEventDispatcher,
375    task: Option<PointerInputTask>,
376    state: NodeState,
377}
378
379impl SuspendingPointerInputNode {
380    fn new(keys: Vec<KeyToken>, handler: PointerInputHandler) -> Self {
381        Self {
382            keys,
383            handler,
384            dispatcher: PointerEventDispatcher::new(),
385            task: None,
386            state: NodeState::new(),
387        }
388    }
389
390    fn update(&mut self, keys: Vec<KeyToken>, handler: PointerInputHandler) {
391        // Only restart if keys changed - not if handler Rc pointer changed.
392        // In Compose, closures are recreated every composition but the task should
393        // continue running as long as the keys are the same. This matches Jetpack
394        // Compose behavior where rememberUpdatedState keeps the task alive.
395        let should_restart = self.keys != keys;
396        self.keys = keys;
397        self.handler = handler; // Update handler even if not restarting
398        if should_restart {
399            self.restart();
400        }
401    }
402
403    fn restart(&mut self) {
404        self.cancel();
405        self.start();
406    }
407
408    fn start(&mut self) {
409        let state = Rc::new(PointerInputScopeState::new());
410        self.dispatcher.set_state(Some(state.clone()));
411        let scope = PointerInputScope::new(state);
412        let future = (self.handler)(scope);
413        let task = PointerInputTask::new(future);
414        task.poll();
415        self.task = Some(task);
416    }
417
418    fn cancel(&mut self) {
419        if let Some(task) = self.task.take() {
420            task.cancel();
421        }
422        self.dispatcher.set_state(None);
423    }
424}
425
426impl Drop for SuspendingPointerInputNode {
427    fn drop(&mut self) {
428        // Cleanup happens automatically when task field is dropped
429    }
430}
431
432impl ModifierNode for SuspendingPointerInputNode {
433    fn on_attach(&mut self, _context: &mut dyn ModifierNodeContext) {
434        self.start();
435    }
436
437    fn on_detach(&mut self) {
438        self.cancel();
439    }
440
441    fn on_reset(&mut self) {
442        // Don't restart on reset - only restart when keys/handler actually change
443        // (which is handled by update() method). Restarting here would kill the
444        // active task and lose its registered waker, preventing events from being delivered.
445    }
446
447    // Capability-driven implementation using helper macro
448    impl_pointer_input_node!();
449}
450
451impl DelegatableNode for SuspendingPointerInputNode {
452    fn node_state(&self) -> &NodeState {
453        &self.state
454    }
455}
456
457impl PointerInputNode for SuspendingPointerInputNode {
458    fn pointer_input_handler(&self) -> Option<Rc<dyn Fn(PointerEvent)>> {
459        Some(self.dispatcher.handler())
460    }
461}
462
463#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
464struct KeyToken {
465    type_id: TypeId,
466    hash: u64,
467}
468
469impl KeyToken {
470    fn new<T: Hash + 'static>(value: &T) -> Self {
471        let mut hasher = DefaultHasher::new();
472        value.hash(&mut hasher);
473        Self {
474            type_id: TypeId::of::<T>(),
475            hash: hasher.finish(),
476        }
477    }
478}