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