cosmic/widget/rectangle_tracker/
subscription.rs

1use iced::{
2    Rectangle,
3    futures::{
4        StreamExt,
5        channel::mpsc::{UnboundedReceiver, unbounded},
6        stream,
7    },
8};
9use iced_futures::Subscription;
10use std::{collections::HashMap, fmt::Debug, hash::Hash};
11
12use super::RectangleTracker;
13
14#[cold]
15pub fn rectangle_tracker_subscription<
16    I: 'static + Hash + Copy + Send + Sync + Debug,
17    R: 'static + Hash + Copy + Send + Sync + Debug + Eq,
18>(
19    id: I,
20) -> Subscription<(I, RectangleUpdate<R>)> {
21    Subscription::run_with_id(
22        id,
23        stream::unfold(State::Ready, move |state| start_listening(id, state)),
24    )
25}
26
27pub enum State<I> {
28    Ready,
29    Waiting(UnboundedReceiver<(I, Rectangle)>, HashMap<I, Rectangle>),
30    Finished,
31}
32
33async fn start_listening<I: Copy, R: 'static + Hash + Copy + Send + Sync + Debug + Eq>(
34    id: I,
35    mut state: State<R>,
36) -> Option<((I, RectangleUpdate<R>), State<R>)> {
37    loop {
38        let (update, new_state) = match state {
39            State::Ready => {
40                let (tx, rx) = unbounded();
41
42                (
43                    Some((id, RectangleUpdate::Init(RectangleTracker { tx }))),
44                    State::Waiting(rx, HashMap::new()),
45                )
46            }
47            State::Waiting(mut rx, mut map) => match rx.next().await {
48                Some(u) => {
49                    if let Some(prev) = map.get(&u.0) {
50                        let new = u.1;
51                        if (prev.width - new.width).abs() > 0.1
52                            || (prev.height - new.height).abs() > 0.1
53                            || (prev.x - new.x).abs() > 0.1
54                            || (prev.y - new.y).abs() > 0.1
55                        {
56                            map.insert(u.0, new);
57                            (
58                                Some((id, RectangleUpdate::Rectangle(u))),
59                                State::Waiting(rx, map),
60                            )
61                        } else {
62                            (None, State::Waiting(rx, map))
63                        }
64                    } else {
65                        map.insert(u.0, u.1);
66                        (
67                            Some((id, RectangleUpdate::Rectangle(u))),
68                            State::Waiting(rx, map),
69                        )
70                    }
71                }
72                None => (None, State::Finished),
73            },
74            State::Finished => return None,
75        };
76        state = new_state;
77        if let Some(u) = update {
78            return Some((u, state));
79        }
80    }
81}
82
83#[derive(Clone, Debug)]
84pub enum RectangleUpdate<I>
85where
86    I: 'static + Hash + Copy + Send + Sync + Debug,
87{
88    Rectangle((I, Rectangle)),
89    Init(RectangleTracker<I>),
90}