zng_ext_input/focus/
iter.rs

1//! Focusable info tree iterators.
2//!
3
4use zng_app::widget::info::{
5    WidgetInfo,
6    iter::{self as w_iter, TreeIterator},
7};
8
9use super::*;
10
11/// Filter-maps an iterator of [`WidgetInfo`] to [`WidgetFocusInfo`].
12///
13///  [`WidgetInfo`]: zng_app::widget::info::WidgetInfo
14pub trait IterFocusableExt<I: Iterator<Item = WidgetInfo>> {
15    /// Returns an iterator of only the focusable widgets.
16    ///
17    /// See the [`FOCUS.focus_disabled_widgets`] and [`FOCUS.focus_hidden_widgets`] config for more on the parameter.
18    ///
19    /// [`FOCUS.focus_disabled_widgets`]: crate::focus::FOCUS::focus_disabled_widgets
20    /// [`FOCUS.focus_hidden_widgets`]: crate::focus::FOCUS::focus_hidden_widgets
21    fn focusable(self, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> IterFocusable<I>;
22}
23impl<I> IterFocusableExt<I> for I
24where
25    I: Iterator<Item = WidgetInfo>,
26{
27    fn focusable(self, focus_disabled_widgets: bool, focus_hidden_widgets: bool) -> IterFocusable<I> {
28        IterFocusable {
29            iter: self,
30            mode: FocusMode::new(focus_disabled_widgets, focus_hidden_widgets),
31        }
32    }
33}
34
35/// Filter a widget info iterator to only focusable items.
36///
37/// Use [`IterFocusableExt::focusable`] to create.
38pub struct IterFocusable<I: Iterator<Item = WidgetInfo>> {
39    iter: I,
40    mode: FocusMode,
41}
42impl<I> Iterator for IterFocusable<I>
43where
44    I: Iterator<Item = WidgetInfo>,
45{
46    type Item = WidgetFocusInfo;
47
48    fn next(&mut self) -> Option<Self::Item> {
49        for next in self.iter.by_ref() {
50            if let Some(next) = next.into_focusable(self.mode.contains(FocusMode::DISABLED), self.mode.contains(FocusMode::HIDDEN)) {
51                return Some(next);
52            }
53        }
54        None
55    }
56}
57impl<I> DoubleEndedIterator for IterFocusable<I>
58where
59    I: Iterator<Item = WidgetInfo> + DoubleEndedIterator,
60{
61    fn next_back(&mut self) -> Option<Self::Item> {
62        while let Some(next) = self.iter.next_back() {
63            if let Some(next) = next.into_focusable(self.mode.contains(FocusMode::DISABLED), self.mode.contains(FocusMode::HIDDEN)) {
64                return Some(next);
65            }
66        }
67        None
68    }
69}
70
71/// Iterator over all focusable items in a branch of the widget tree.
72///
73/// This `struct` is created by the [`descendants`] and [`self_and_descendants`] methods on [`WidgetFocusInfo`].
74/// See its documentation for more.
75///
76/// [`descendants`]: WidgetFocusInfo::descendants
77/// [`self_and_descendants`]: WidgetFocusInfo::self_and_descendants
78pub struct FocusTreeIter<I>
79where
80    I: TreeIterator,
81{
82    iter: I,
83    mode: FocusMode,
84}
85impl<I> FocusTreeIter<I>
86where
87    I: TreeIterator,
88{
89    pub(super) fn new(iter: I, mode: FocusMode) -> Self {
90        Self { iter, mode }
91    }
92
93    /// Filter out entire branches of descendants at a time.
94    ///
95    /// Note that you can convert `bool` into [`TreeFilter`] to use this method just like the iterator default.
96    ///
97    /// [`TreeFilter`]: w_iter::TreeFilter
98    pub fn tree_filter<F>(self, mut filter: F) -> FocusTreeFilterIter<I, impl FnMut(&WidgetInfo) -> w_iter::TreeFilter>
99    where
100        F: FnMut(&WidgetFocusInfo) -> w_iter::TreeFilter,
101    {
102        FocusTreeFilterIter {
103            iter: self.iter.tree_filter(move |w| {
104                if let Some(f) = w
105                    .clone()
106                    .into_focusable(self.mode.contains(FocusMode::DISABLED), self.mode.contains(FocusMode::HIDDEN))
107                {
108                    filter(&f)
109                } else {
110                    w_iter::TreeFilter::Skip
111                }
112            }),
113            mode: self.mode,
114        }
115    }
116
117    /// Returns the first focusable included by `filter`.
118    ///
119    /// Note that you can convert `bool` into [`TreeFilter`] to use this method just like the iterator default.
120    ///
121    /// [`TreeFilter`]: w_iter::TreeFilter
122    pub fn tree_find<F>(self, filter: F) -> Option<WidgetFocusInfo>
123    where
124        F: FnMut(&WidgetFocusInfo) -> w_iter::TreeFilter,
125    {
126        self.tree_filter(filter).next()
127    }
128
129    /// Returns if the `filter` allows any focusable.
130    ///
131    /// Note that you can convert `bool` into [`TreeFilter`] to use this method just like the iterator default.
132    ///
133    /// [`TreeFilter`]: w_iter::TreeFilter
134    pub fn tree_any<F>(self, filter: F) -> bool
135    where
136        F: FnMut(&WidgetFocusInfo) -> w_iter::TreeFilter,
137    {
138        self.tree_find(filter).is_some()
139    }
140}
141impl FocusTreeIter<w_iter::TreeIter> {
142    /// Creates a reverse tree iterator.
143    pub fn tree_rev(self) -> FocusTreeIter<w_iter::RevTreeIter> {
144        FocusTreeIter::new(self.iter.tree_rev(), self.mode)
145    }
146}
147
148impl<I> Iterator for FocusTreeIter<I>
149where
150    I: TreeIterator,
151{
152    type Item = WidgetFocusInfo;
153
154    fn next(&mut self) -> Option<Self::Item> {
155        for next in self.iter.by_ref() {
156            if let Some(next) = next.into_focusable(self.mode.contains(FocusMode::DISABLED), self.mode.contains(FocusMode::HIDDEN)) {
157                return Some(next);
158            }
159        }
160        None
161    }
162}
163
164/// An iterator that filters a focusable widget tree.
165///
166/// This `struct` is created by the [`FocusTreeIter::tree_filter`] method. See its documentation for more.
167pub struct FocusTreeFilterIter<I, F>
168where
169    I: TreeIterator,
170    F: FnMut(&WidgetInfo) -> w_iter::TreeFilter,
171{
172    iter: w_iter::TreeFilterIter<I, F>,
173    mode: FocusMode,
174}
175impl<I, F> Iterator for FocusTreeFilterIter<I, F>
176where
177    F: FnMut(&WidgetInfo) -> w_iter::TreeFilter,
178    I: TreeIterator,
179{
180    type Item = WidgetFocusInfo;
181
182    fn next(&mut self) -> Option<Self::Item> {
183        self.iter
184            .next()
185            .map(|w| w.into_focus_info(self.mode.contains(FocusMode::DISABLED), self.mode.contains(FocusMode::HIDDEN)))
186    }
187}