Skip to main content

cosmic/widget/rectangle_tracker/
subscription.rs

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