relm4/extensions/
iter_children.rs

1use super::ContainerChild;
2use gtk::prelude::{Cast, GridExt, IsA, WidgetExt};
3
4/// An iterator over container children.
5#[derive(Debug)]
6struct ChildrenIterator<T: RelmIterChildrenExt> {
7    start: Option<T::Child>,
8    end: Option<T::Child>,
9    done: bool,
10}
11
12impl<T: RelmIterChildrenExt> ChildrenIterator<T> {
13    /// Create a new iterator over children of `widget`.
14    fn new(widget: &T) -> Self {
15        let start = widget.first_child().map(|child| {
16            child
17                .downcast::<T::Child>()
18                .expect("The type of children does not match.")
19        });
20        let end = widget.last_child().map(|child| {
21            child
22                .downcast::<T::Child>()
23                .expect("The type of children does not match.")
24        });
25        let done = start.is_none();
26        Self { start, end, done }
27    }
28}
29
30impl<T: RelmIterChildrenExt> Iterator for ChildrenIterator<T> {
31    type Item = T::Child;
32    fn next(&mut self) -> Option<Self::Item> {
33        if self.done {
34            None
35        } else {
36            // Handle cases where only one child exists and
37            // when all but one widget were consumed
38            if self.start == self.end {
39                self.done = true;
40                self.start.clone()
41            } else if let Some(start) = self.start.take() {
42                // "Increment" the start child
43                self.start = start.next_sibling().map(|child| {
44                    child
45                        .downcast::<T::Child>()
46                        .expect("The type of children does not match.")
47                });
48                // Just to make sure the iterator ends next time
49                // because all widgets were consumed
50                self.done = self.start.is_none();
51                Some(start)
52            } else {
53                None
54            }
55        }
56    }
57}
58
59impl<T: RelmIterChildrenExt> DoubleEndedIterator for ChildrenIterator<T> {
60    fn next_back(&mut self) -> Option<Self::Item> {
61        if self.done {
62            None
63        } else {
64            // Handle cases where only one child exists and
65            // when all but one widget were consumed
66            if self.start == self.end {
67                self.done = true;
68                self.end.clone()
69            } else if let Some(end) = self.end.take() {
70                // "Decrement" the end child
71                self.end = end.prev_sibling().map(|child| {
72                    child
73                        .downcast::<T::Child>()
74                        .expect("The type of children does not match.")
75                });
76                // Just to make sure the iterator ends next time
77                // because all widgets were consumed
78                self.done = self.end.is_none();
79                Some(end)
80            } else {
81                None
82            }
83        }
84    }
85}
86
87/// Widget types which allow iteration over their children.
88pub trait RelmIterChildrenExt: ContainerChild + IsA<gtk::Widget> {
89    /// Returns an iterator over container children.
90    fn iter_children(&self) -> Box<dyn DoubleEndedIterator<Item = Self::Child>> {
91        Box::new(ChildrenIterator::new(self))
92    }
93}
94
95impl RelmIterChildrenExt for gtk::Box {}
96impl RelmIterChildrenExt for gtk::ListBox {}
97impl RelmIterChildrenExt for gtk::FlowBox {}
98impl RelmIterChildrenExt for gtk::Grid {
99    // `gtk::Grid` places children in the order they were added to the grid.
100    //
101    // We have to provide a separate implementation that would sort children
102    // depending on their position.
103    fn iter_children(&self) -> Box<dyn DoubleEndedIterator<Item = Self::Child>> {
104        let mut vec = Vec::new();
105        let mut widget = self.first_child();
106        while let Some(child) = widget {
107            widget = child.next_sibling();
108            let (column, row, _, _) = self.query_child(&child);
109            vec.push((column, row, child));
110        }
111
112        vec.sort_by(|(col_a, row_a, _), (col_b, row_b, _)| {
113            if row_a == row_b {
114                col_a.cmp(col_b)
115            } else {
116                row_a.cmp(row_b)
117            }
118        });
119
120        Box::new(vec.into_iter().map(|(_, _, child)| child))
121    }
122}
123impl RelmIterChildrenExt for gtk::Stack {}
124
125#[cfg(feature = "libadwaita")]
126#[cfg_attr(docsrs, doc(cfg(feature = "libadwaita")))]
127mod libadwaita {
128    use super::RelmIterChildrenExt;
129    use gtk::prelude::{Cast, ListModelExt};
130
131    impl RelmIterChildrenExt for adw::TabView {
132        fn iter_children(&self) -> Box<dyn DoubleEndedIterator<Item = Self::Child>> {
133            let pages = self.pages();
134            Box::new(
135                (0..pages.n_items())
136                    .filter_map(move |index| pages.item(index))
137                    .filter_map(|item| item.downcast::<adw::TabPage>().ok())
138                    .map(|page| page.child()),
139            )
140        }
141    }
142}