Skip to main content

cranpose_ui/modifier/
pointer_input.rs

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