1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use crate::spring;
use dioxus::prelude::*;
use futures::StreamExt;
use interpolation::Lerp;
use std::{collections::VecDeque, task::Poll, time::Duration};

/// Hook to animate a value from some initial value `from`.
/// 
/// The returned [`UseSpringRef`] can be used to queue and control animations.
/// Values are linearly interpolated and sent to the handler `f`.
pub fn use_spring_ref<V>(from: V, f: impl FnMut(V) + 'static) -> UseSpringRef<V>
where
    V: Lerp<Scalar = f32> + Clone + 'static,
{
    let mut channel = use_hook(|| CopyValue::new(async_channel::unbounded()));
    let mut f_cell = Some(f);

    use_future(move || {
        let mut f = f_cell.take().unwrap();
        let mut current = from.clone();
        let mut spring_cell = None;
        let mut stack = VecDeque::new();

        futures::future::poll_fn(move |cx| {
            while let Poll::Ready(Some(msg)) = channel.write().1.poll_next_unpin(cx) {
                match msg {
                    Message::Set(to, duration_cell) => {
                        if let Some(duration) = duration_cell {
                            let spring = spring(current.clone(), to, duration);
                            spring_cell = Some(Box::pin(spring));
                            stack.clear();
                        } else {
                            current = to.clone();
                            spring_cell.take();
                            stack.clear();
                            f(to);
                        }
                    }
                    Message::Queue(to, duration) => {
                        stack.push_back((to, duration));
                    }
                }
            }

            if spring_cell.is_none() {
                if let Some((to, duration)) = stack.pop_front() {
                    let spring = crate::spring(current.clone(), to, duration);
                    spring_cell = Some(Box::pin(spring));
                } else {
                    spring_cell = None;
                }
            }

            while let Some(spring) = spring_cell.as_mut() {
                let mut is_done = false;
                while let Poll::Ready(item) = spring.poll_next_unpin(cx) {
                    if let Some(val) = item {
                        current = val.clone();
                        f(val);
                    } else {
                        is_done = true;
                        break;
                    }
                }

                if is_done {
                    if let Some((to, duration)) = stack.pop_front() {
                        let spring = crate::spring(current.clone(), to, duration);
                        spring_cell = Some(Box::pin(spring));
                    } else {
                        spring_cell = None;
                    }
                } else {
                    break;
                }
            }

            Poll::<()>::Pending
        })
    });

    UseSpringRef { channel }
}

pub(crate) enum Message<V> {
    Set(V, Option<Duration>),
    Queue(V, Duration),
}

/// Hook returned from [`use_spring_ref`]
pub struct UseSpringRef<V: 'static> {
    channel: CopyValue<(
        async_channel::Sender<Message<V>>,
        async_channel::Receiver<Message<V>>,
    )>,
}

impl<V> UseSpringRef<V> {
    pub fn set(&self, to: V) {
        self.channel
            .read()
            .0
            .send_blocking(Message::Set(to, None))
            .unwrap();
    }

    pub fn animate(&self, to: V, duration: Duration) {
        self.channel
            .read()
            .0
            .send_blocking(Message::Set(to, Some(duration)))
            .unwrap();
    }

    pub fn queue(&self, to: V, duration: Duration) {
        self.channel
            .read()
            .0
            .send_blocking(Message::Queue(to, duration))
            .unwrap();
    }
}

impl<V> Clone for UseSpringRef<V> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<V> Copy for UseSpringRef<V> {}