relm4/extensions/
iter_children.rs1use super::ContainerChild;
2use gtk::prelude::{Cast, GridExt, IsA, WidgetExt};
3
4#[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 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 if self.start == self.end {
39 self.done = true;
40 self.start.clone()
41 } else if let Some(start) = self.start.take() {
42 self.start = start.next_sibling().map(|child| {
44 child
45 .downcast::<T::Child>()
46 .expect("The type of children does not match.")
47 });
48 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 if self.start == self.end {
67 self.done = true;
68 self.end.clone()
69 } else if let Some(end) = self.end.take() {
70 self.end = end.prev_sibling().map(|child| {
72 child
73 .downcast::<T::Child>()
74 .expect("The type of children does not match.")
75 });
76 self.done = self.end.is_none();
79 Some(end)
80 } else {
81 None
82 }
83 }
84 }
85}
86
87pub trait RelmIterChildrenExt: ContainerChild + IsA<gtk::Widget> {
89 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 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}