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