use super::ContainerChild;
use gtk::prelude::{Cast, GridExt, IsA, WidgetExt};
#[derive(Debug)]
struct ChildrenIterator<T: RelmIterChildrenExt> {
start: Option<T::Child>,
end: Option<T::Child>,
done: bool,
}
impl<T: RelmIterChildrenExt> ChildrenIterator<T> {
fn new(widget: &T) -> Self {
let start = widget.first_child().map(|child| {
child
.downcast::<T::Child>()
.expect("The type of children does not match.")
});
let end = widget.last_child().map(|child| {
child
.downcast::<T::Child>()
.expect("The type of children does not match.")
});
let done = start.is_none();
Self { start, end, done }
}
}
impl<T: RelmIterChildrenExt> Iterator for ChildrenIterator<T> {
type Item = T::Child;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
None
} else {
if self.start == self.end {
self.done = true;
self.start.clone()
} else if let Some(start) = self.start.take() {
self.start = start.next_sibling().map(|child| {
child
.downcast::<T::Child>()
.expect("The type of children does not match.")
});
self.done = self.start.is_none();
Some(start)
} else {
None
}
}
}
}
impl<T: RelmIterChildrenExt> DoubleEndedIterator for ChildrenIterator<T> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.done {
None
} else {
if self.start == self.end {
self.done = true;
self.end.clone()
} else if let Some(end) = self.end.take() {
self.end = end.prev_sibling().map(|child| {
child
.downcast::<T::Child>()
.expect("The type of children does not match.")
});
self.done = self.end.is_none();
Some(end)
} else {
None
}
}
}
}
pub trait RelmIterChildrenExt: ContainerChild + IsA<gtk::Widget> {
fn iter_children(&self) -> Box<dyn DoubleEndedIterator<Item = Self::Child>> {
Box::new(ChildrenIterator::new(self))
}
}
impl RelmIterChildrenExt for gtk::Box {}
impl RelmIterChildrenExt for gtk::ListBox {}
impl RelmIterChildrenExt for gtk::FlowBox {}
impl RelmIterChildrenExt for gtk::Grid {
fn iter_children(&self) -> Box<dyn DoubleEndedIterator<Item = Self::Child>> {
let mut vec = Vec::new();
let mut widget = self.first_child();
while let Some(child) = widget {
widget = child.next_sibling();
let (column, row, _, _) = self.query_child(&child);
vec.push((column, row, child));
}
vec.sort_by(|(col_a, row_a, _), (col_b, row_b, _)| {
if row_a == row_b {
col_a.cmp(col_b)
} else {
row_a.cmp(row_b)
}
});
Box::new(vec.into_iter().map(|(_, _, child)| child))
}
}
impl RelmIterChildrenExt for gtk::Stack {}
#[cfg(feature = "libadwaita")]
#[cfg_attr(docsrs, doc(cfg(feature = "libadwaita")))]
mod libadwaita {
use super::RelmIterChildrenExt;
use gtk::prelude::{Cast, ListModelExt};
impl RelmIterChildrenExt for adw::TabView {
fn iter_children(&self) -> Box<dyn DoubleEndedIterator<Item = Self::Child>> {
let pages = self.pages();
Box::new(
(0..pages.n_items())
.filter_map(move |index| pages.item(index))
.filter_map(|item| item.downcast::<adw::TabPage>().ok())
.map(|page| page.child()),
)
}
}
}