Skip to main content

cranpose_ui/modifier/
pointer_input.rs

1use std::{
2    any::TypeId,
3    cell::{Cell, RefCell},
4    collections::{HashMap, 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::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.clone())))
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::new()),
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#[derive(Clone)]
178pub struct PointerInputScope {
179    state: Rc<PointerInputScopeState>,
180}
181
182impl PointerInputScope {
183    fn new(state: Rc<PointerInputScopeState>) -> Self {
184        Self { state }
185    }
186
187    /// The size of the layout node this handler is attached to, in the same
188    /// local coordinate space as the [`PointerEvent`] positions the scope
189    /// delivers (origin at the node's top-left).
190    ///
191    /// The layout pass publishes the node's resolved size every pass, so this
192    /// is current from the first laid-out frame onwards — including before any
193    /// pointer event has arrived. It is `0x0` only while the node has never
194    /// been laid out.
195    pub fn size(&self) -> Size {
196        self.state.size.get()
197    }
198
199    pub async fn await_pointer_event_scope<R, F, Fut>(&self, block: F) -> R
200    where
201        F: FnOnce(AwaitPointerEventScope) -> Fut,
202        Fut: Future<Output = R>,
203    {
204        let scope = AwaitPointerEventScope {
205            state: self.state.clone(),
206        };
207        block(scope).await
208    }
209}
210
211#[derive(Clone)]
212pub struct AwaitPointerEventScope {
213    state: Rc<PointerInputScopeState>,
214}
215
216impl AwaitPointerEventScope {
217    /// The size of the layout node this handler is attached to. See
218    /// [`PointerInputScope::size`].
219    pub fn size(&self) -> Size {
220        self.state.size.get()
221    }
222
223    pub async fn await_pointer_event(&self) -> PointerEvent {
224        NextPointerEvent {
225            state: self.state.clone(),
226        }
227        .await
228    }
229}
230
231struct NextPointerEvent {
232    state: Rc<PointerInputScopeState>,
233}
234
235impl Future for NextPointerEvent {
236    type Output = PointerEvent;
237
238    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
239        self.state.poll_event(cx)
240    }
241}
242
243struct PointerInputScopeState {
244    events: RefCell<VecDeque<PointerEvent>>,
245    waiting: RefCell<Option<Waker>>,
246    size: Rc<Cell<Size>>,
247}
248
249impl PointerInputScopeState {
250    fn new(size: Rc<Cell<Size>>) -> Self {
251        Self {
252            events: RefCell::new(VecDeque::new()),
253            waiting: RefCell::new(None),
254            size,
255        }
256    }
257
258    fn push_event(&self, event: PointerEvent) {
259        self.events.borrow_mut().push_back(event);
260        let waker = {
261            let mut waiting = self.waiting.borrow_mut();
262            waiting.take()
263        };
264        if let Some(waker) = waker {
265            waker.wake();
266        }
267    }
268
269    fn poll_event(&self, cx: &mut Context<'_>) -> Poll<PointerEvent> {
270        if let Some(event) = self.events.borrow_mut().pop_front() {
271            Poll::Ready(event)
272        } else {
273            self.waiting.replace(Some(cx.waker().clone()));
274            Poll::Pending
275        }
276    }
277}
278
279struct PointerEventDispatcher {
280    state: Rc<RefCell<Option<Rc<PointerInputScopeState>>>>,
281    handler: Rc<dyn Fn(PointerEvent)>,
282}
283
284impl PointerEventDispatcher {
285    fn new() -> Self {
286        let state = Rc::new(RefCell::new(None::<Rc<PointerInputScopeState>>));
287        let state_for_handler = state.clone();
288        let handler = Rc::new(move |event: PointerEvent| {
289            if let Some(inner) = state_for_handler.borrow().as_ref() {
290                inner.push_event(event);
291            }
292        });
293        Self { state, handler }
294    }
295
296    fn handler(&self) -> Rc<dyn Fn(PointerEvent)> {
297        self.handler.clone()
298    }
299
300    fn set_state(&self, state: Option<Rc<PointerInputScopeState>>) {
301        *self.state.borrow_mut() = state;
302    }
303}
304
305struct PointerInputTask {
306    id: u64,
307    owner: PointerInputTaskOwner,
308    inner: Rc<PointerInputTaskInner>,
309}
310
311impl PointerInputTask {
312    fn new(future: PointerInputFuture) -> Self {
313        let inner = Rc::new(PointerInputTaskInner::new(future));
314        let id = Rc::as_ptr(&inner) as usize as u64;
315        let owner = crate::render_state::register_pointer_input_task(id, inner.clone());
316        Self { id, owner, inner }
317    }
318
319    fn poll(&self) {
320        self.inner.poll(self.owner, self.id);
321    }
322
323    fn cancel(self) {
324        self.inner.cancel();
325        crate::render_state::remove_pointer_input_task(self.owner, self.id);
326    }
327}
328
329impl Drop for PointerInputTask {
330    fn drop(&mut self) {
331        self.inner.cancel();
332        crate::render_state::remove_pointer_input_task(self.owner, self.id);
333    }
334}
335
336pub(crate) struct PointerInputTaskInner {
337    future: RefCell<Option<PointerInputFuture>>,
338    is_polling: Cell<bool>,
339    needs_poll: Cell<bool>,
340}
341
342impl PointerInputTaskInner {
343    fn new(future: PointerInputFuture) -> Self {
344        Self {
345            future: RefCell::new(Some(future)),
346            is_polling: Cell::new(false),
347            needs_poll: Cell::new(false),
348        }
349    }
350
351    fn cancel(&self) {
352        self.future.borrow_mut().take();
353    }
354
355    fn request_poll(&self, owner: PointerInputTaskOwner, task_id: u64) {
356        if self.is_polling.get() {
357            self.needs_poll.set(true);
358        } else {
359            self.poll(owner, task_id);
360        }
361    }
362
363    fn poll(&self, owner: PointerInputTaskOwner, task_id: u64) {
364        if self.is_polling.replace(true) {
365            self.needs_poll.set(true);
366            return;
367        }
368        loop {
369            self.needs_poll.set(false);
370            let waker = waker(Arc::new(PointerInputTaskWaker { task_id, owner }));
371            let mut cx = Context::from_waker(&waker);
372            let mut future_slot = self.future.borrow_mut();
373            if let Some(future) = future_slot.as_mut() {
374                let poll_result = future.as_mut().poll(&mut cx);
375                if poll_result.is_ready() {
376                    future_slot.take();
377                }
378            }
379            if !self.needs_poll.get() {
380                break;
381            }
382        }
383        self.is_polling.set(false);
384    }
385}
386
387struct PointerInputTaskWaker {
388    task_id: u64,
389    owner: PointerInputTaskOwner,
390}
391
392impl ArcWake for PointerInputTaskWaker {
393    fn wake_by_ref(arc_self: &Arc<Self>) {
394        crate::render_state::request_pointer_input_task_poll(arc_self.owner, arc_self.task_id);
395    }
396}
397
398pub struct SuspendingPointerInputNode {
399    site: KeyToken,
400    keys: Vec<KeyToken>,
401    handler: PointerInputHandler,
402    dispatcher: PointerEventDispatcher,
403    task: Option<PointerInputTask>,
404    layout_size: Rc<Cell<Size>>,
405    state: NodeState,
406}
407
408impl SuspendingPointerInputNode {
409    fn new(site: KeyToken, keys: Vec<KeyToken>, handler: PointerInputHandler) -> Self {
410        Self {
411            site,
412            keys,
413            handler,
414            dispatcher: PointerEventDispatcher::new(),
415            task: None,
416            layout_size: Rc::new(Cell::new(Size {
417                width: 0.0,
418                height: 0.0,
419            })),
420            state: NodeState::new(),
421        }
422    }
423
424    fn update(&mut self, site: KeyToken, keys: Vec<KeyToken>, handler: PointerInputHandler) {
425        let should_restart = self.site != site || self.keys != keys;
426        self.site = site;
427        self.keys = keys;
428        self.handler = handler;
429        if should_restart {
430            self.restart();
431        }
432    }
433
434    fn restart(&mut self) {
435        self.cancel();
436        self.start();
437    }
438
439    fn start(&mut self) {
440        let state = Rc::new(PointerInputScopeState::new(self.layout_size.clone()));
441        self.dispatcher.set_state(Some(state.clone()));
442        let scope = PointerInputScope::new(state);
443        let future = (self.handler)(scope);
444        let task = PointerInputTask::new(future);
445        task.poll();
446        self.task = Some(task);
447    }
448
449    fn cancel(&mut self) {
450        if let Some(task) = self.task.take() {
451            task.cancel();
452        }
453        self.dispatcher.set_state(None);
454    }
455}
456
457impl Drop for SuspendingPointerInputNode {
458    fn drop(&mut self) {
459        self.cancel();
460    }
461}
462
463impl ModifierNode for SuspendingPointerInputNode {
464    fn on_attach(&mut self, _context: &mut dyn ModifierNodeContext) {
465        self.start();
466    }
467
468    fn on_detach(&mut self) {
469        self.cancel();
470    }
471
472    fn on_reset(&mut self) {}
473
474    impl_pointer_input_node!();
475}
476
477impl DelegatableNode for SuspendingPointerInputNode {
478    fn node_state(&self) -> &NodeState {
479        &self.state
480    }
481}
482
483impl PointerInputNode for SuspendingPointerInputNode {
484    fn pointer_input_handler(&self) -> Option<Rc<dyn Fn(PointerEvent)>> {
485        Some(self.dispatcher.handler())
486    }
487
488    fn layout_size_sink(&self) -> Option<Rc<Cell<Size>>> {
489        Some(self.layout_size.clone())
490    }
491}
492
493#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
494struct KeyToken {
495    type_id: TypeId,
496    hash: u64,
497}
498
499impl KeyToken {
500    fn declaration_site(site: &'static std::panic::Location<'static>) -> Self {
501        Self::new(&(site.file(), site.line(), site.column()))
502    }
503
504    fn new<T: Hash + 'static>(value: &T) -> Self {
505        let mut hasher = default::new();
506        value.hash(&mut hasher);
507        Self {
508            type_id: TypeId::of::<T>(),
509            hash: hasher.finish(),
510        }
511    }
512}