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    pub fn size(&self) -> Size {
171        self.state.size.get()
172    }
173
174    pub async fn await_pointer_event_scope<R, F, Fut>(&self, block: F) -> R
175    where
176        F: FnOnce(AwaitPointerEventScope) -> Fut,
177        Fut: Future<Output = R>,
178    {
179        let scope = AwaitPointerEventScope {
180            state: self.state.clone(),
181        };
182        block(scope).await
183    }
184}
185
186#[derive(Clone)]
187pub struct AwaitPointerEventScope {
188    state: Rc<PointerInputScopeState>,
189}
190
191impl AwaitPointerEventScope {
192    pub fn size(&self) -> Size {
193        self.state.size.get()
194    }
195
196    pub async fn await_pointer_event(&self) -> PointerEvent {
197        NextPointerEvent {
198            state: self.state.clone(),
199        }
200        .await
201    }
202
203    pub async fn with_timeout_or_null<R, F, Fut>(&self, _time_millis: u64, block: F) -> Option<R>
204    where
205        F: FnOnce(&AwaitPointerEventScope) -> Fut,
206        Fut: Future<Output = R>,
207    {
208        Some(block(self).await)
209    }
210
211    pub async fn with_timeout<R, F, Fut>(&self, _time_millis: u64, block: F) -> R
212    where
213        F: FnOnce(&AwaitPointerEventScope) -> Fut,
214        Fut: Future<Output = R>,
215    {
216        block(self).await
217    }
218}
219
220struct NextPointerEvent {
221    state: Rc<PointerInputScopeState>,
222}
223
224impl Future for NextPointerEvent {
225    type Output = PointerEvent;
226
227    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
228        self.state.poll_event(cx)
229    }
230}
231
232struct PointerInputScopeState {
233    events: RefCell<VecDeque<PointerEvent>>,
234    waiting: RefCell<Option<Waker>>,
235    size: Cell<Size>,
236}
237
238impl PointerInputScopeState {
239    fn new() -> Self {
240        Self {
241            events: RefCell::new(VecDeque::new()),
242            waiting: RefCell::new(None),
243            size: Cell::new(Size {
244                width: 0.0,
245                height: 0.0,
246            }),
247        }
248    }
249
250    fn push_event(&self, event: PointerEvent) {
251        self.events.borrow_mut().push_back(event);
252        let waker = {
253            let mut waiting = self.waiting.borrow_mut();
254            waiting.take()
255        };
256        if let Some(waker) = waker {
257            waker.wake();
258        }
259    }
260
261    fn poll_event(&self, cx: &mut Context<'_>) -> Poll<PointerEvent> {
262        if let Some(event) = self.events.borrow_mut().pop_front() {
263            Poll::Ready(event)
264        } else {
265            self.waiting.replace(Some(cx.waker().clone()));
266            Poll::Pending
267        }
268    }
269}
270
271struct PointerEventDispatcher {
272    state: Rc<RefCell<Option<Rc<PointerInputScopeState>>>>,
273    handler: Rc<dyn Fn(PointerEvent)>,
274}
275
276impl PointerEventDispatcher {
277    fn new() -> Self {
278        let state = Rc::new(RefCell::new(None::<Rc<PointerInputScopeState>>));
279        let state_for_handler = state.clone();
280        let handler = Rc::new(move |event: PointerEvent| {
281            if let Some(inner) = state_for_handler.borrow().as_ref() {
282                inner.push_event(event);
283            }
284        });
285        Self { state, handler }
286    }
287
288    fn handler(&self) -> Rc<dyn Fn(PointerEvent)> {
289        self.handler.clone()
290    }
291
292    fn set_state(&self, state: Option<Rc<PointerInputScopeState>>) {
293        *self.state.borrow_mut() = state;
294    }
295}
296
297struct PointerInputTask {
298    id: u64,
299    owner: PointerInputTaskOwner,
300    inner: Rc<PointerInputTaskInner>,
301}
302
303impl PointerInputTask {
304    fn new(future: PointerInputFuture) -> Self {
305        let inner = Rc::new(PointerInputTaskInner::new(future));
306        let id = Rc::as_ptr(&inner) as usize as u64;
307        let owner = crate::render_state::register_pointer_input_task(id, inner.clone());
308        Self { id, owner, inner }
309    }
310
311    fn poll(&self) {
312        self.inner.poll(self.owner, self.id);
313    }
314
315    fn cancel(self) {
316        self.inner.cancel();
317        crate::render_state::remove_pointer_input_task(self.owner, self.id);
318    }
319}
320
321impl Drop for PointerInputTask {
322    fn drop(&mut self) {
323        self.inner.cancel();
324        crate::render_state::remove_pointer_input_task(self.owner, self.id);
325    }
326}
327
328pub(crate) struct PointerInputTaskInner {
329    future: RefCell<Option<PointerInputFuture>>,
330    is_polling: Cell<bool>,
331    needs_poll: Cell<bool>,
332}
333
334impl PointerInputTaskInner {
335    fn new(future: PointerInputFuture) -> Self {
336        Self {
337            future: RefCell::new(Some(future)),
338            is_polling: Cell::new(false),
339            needs_poll: Cell::new(false),
340        }
341    }
342
343    fn cancel(&self) {
344        self.future.borrow_mut().take();
345    }
346
347    fn request_poll(&self, owner: PointerInputTaskOwner, task_id: u64) {
348        if self.is_polling.get() {
349            self.needs_poll.set(true);
350        } else {
351            self.poll(owner, task_id);
352        }
353    }
354
355    fn poll(&self, owner: PointerInputTaskOwner, task_id: u64) {
356        if self.is_polling.replace(true) {
357            self.needs_poll.set(true);
358            return;
359        }
360        loop {
361            self.needs_poll.set(false);
362            let waker = waker(Arc::new(PointerInputTaskWaker { task_id, owner }));
363            let mut cx = Context::from_waker(&waker);
364            let mut future_slot = self.future.borrow_mut();
365            if let Some(future) = future_slot.as_mut() {
366                let poll_result = future.as_mut().poll(&mut cx);
367                if poll_result.is_ready() {
368                    future_slot.take();
369                }
370            }
371            if !self.needs_poll.get() {
372                break;
373            }
374        }
375        self.is_polling.set(false);
376    }
377}
378
379struct PointerInputTaskWaker {
380    task_id: u64,
381    owner: PointerInputTaskOwner,
382}
383
384impl ArcWake for PointerInputTaskWaker {
385    fn wake_by_ref(arc_self: &Arc<Self>) {
386        crate::render_state::request_pointer_input_task_poll(arc_self.owner, arc_self.task_id);
387    }
388}
389
390pub struct SuspendingPointerInputNode {
391    keys: Vec<KeyToken>,
392    handler: PointerInputHandler,
393    dispatcher: PointerEventDispatcher,
394    task: Option<PointerInputTask>,
395    state: NodeState,
396}
397
398impl SuspendingPointerInputNode {
399    fn new(keys: Vec<KeyToken>, handler: PointerInputHandler) -> Self {
400        Self {
401            keys,
402            handler,
403            dispatcher: PointerEventDispatcher::new(),
404            task: None,
405            state: NodeState::new(),
406        }
407    }
408
409    fn update(&mut self, keys: Vec<KeyToken>, handler: PointerInputHandler) {
410        // Only restart if keys changed - not if handler Rc pointer changed.
411        // In Compose, closures are recreated every composition but the task should
412        // continue running as long as the keys are the same. This matches Jetpack
413        // Compose behavior where rememberUpdatedState keeps the task alive.
414        let should_restart = self.keys != keys;
415        self.keys = keys;
416        self.handler = handler; // Update handler even if not restarting
417        if should_restart {
418            self.restart();
419        }
420    }
421
422    fn restart(&mut self) {
423        self.cancel();
424        self.start();
425    }
426
427    fn start(&mut self) {
428        let state = Rc::new(PointerInputScopeState::new());
429        self.dispatcher.set_state(Some(state.clone()));
430        let scope = PointerInputScope::new(state);
431        let future = (self.handler)(scope);
432        let task = PointerInputTask::new(future);
433        task.poll();
434        self.task = Some(task);
435    }
436
437    fn cancel(&mut self) {
438        if let Some(task) = self.task.take() {
439            task.cancel();
440        }
441        self.dispatcher.set_state(None);
442    }
443}
444
445impl Drop for SuspendingPointerInputNode {
446    fn drop(&mut self) {
447        self.cancel();
448    }
449}
450
451impl ModifierNode for SuspendingPointerInputNode {
452    fn on_attach(&mut self, _context: &mut dyn ModifierNodeContext) {
453        self.start();
454    }
455
456    fn on_detach(&mut self) {
457        self.cancel();
458    }
459
460    fn on_reset(&mut self) {
461        // Don't restart on reset - only restart when keys/handler actually change
462        // (which is handled by update() method). Restarting here would kill the
463        // active task and lose its registered waker, preventing events from being delivered.
464    }
465
466    // Capability-driven implementation using helper macro
467    impl_pointer_input_node!();
468}
469
470impl DelegatableNode for SuspendingPointerInputNode {
471    fn node_state(&self) -> &NodeState {
472        &self.state
473    }
474}
475
476impl PointerInputNode for SuspendingPointerInputNode {
477    fn pointer_input_handler(&self) -> Option<Rc<dyn Fn(PointerEvent)>> {
478        Some(self.dispatcher.handler())
479    }
480}
481
482#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
483struct KeyToken {
484    type_id: TypeId,
485    hash: u64,
486}
487
488impl KeyToken {
489    fn new<T: Hash + 'static>(value: &T) -> Self {
490        let mut hasher = default::new();
491        value.hash(&mut hasher);
492        Self {
493            type_id: TypeId::of::<T>(),
494            hash: hasher.finish(),
495        }
496    }
497}