use crate::menu::Menu;
use crate::menuitem::MenuItem;
pub enum Event<'a, C>
where
C: Clone + Default
{
EnterScope,
LeaveScope,
MenuItem(&'a MenuItem<C>)
}
struct IterNode<'a, C>
where
C: Clone + Default
{
lst: &'a Vec<MenuItem<C>>,
idx: usize,
did_enter_scope: bool,
did_leave_scope: bool
}
type FilterFn<C> = fn(mi: &MenuItem<C>) -> bool;
pub struct MenuIter<'a, C>
where
C: Clone + Default
{
stack: Vec<IterNode<'a, C>>,
filter: Option<FilterFn<C>>
}
impl<'a, C> MenuIter<'a, C>
where
C: Clone + Default
{
pub fn new(menu: &'a Menu<C>) -> Self {
let mut iterstack = Vec::new();
iterstack.push(IterNode {
lst: &menu.rootlst,
idx: 0,
did_enter_scope: true,
did_leave_scope: true
});
MenuIter {
stack: iterstack,
filter: None
}
}
pub fn itemfilter(&mut self, f: FilterFn<C>) -> &mut Self {
self.filter = Some(f);
self
}
pub fn root_scope(&mut self) -> &mut Self {
if let Some(root) = self.stack.first_mut() {
root.did_enter_scope = false;
root.did_leave_scope = false;
}
self
}
}
impl<'a, C> Iterator for MenuIter<'a, C>
where
C: Clone + Default
{
type Item = Event<'a, C>;
fn next(&mut self) -> Option<Self::Item> {
while !self.stack.is_empty() {
let mut it = self.stack.pop().unwrap();
if let Some(f) = self.filter {
if it.idx < it.lst.len() {
let mi = &it.lst[it.idx];
if f(mi) == false {
it.idx += 1;
self.stack.push(it);
continue;
}
}
}
if it.did_enter_scope == false {
it.did_enter_scope = true;
self.stack.push(it);
return Some(Event::EnterScope);
}
if it.idx == it.lst.len() {
if it.did_enter_scope == true && it.did_leave_scope == false {
it.did_leave_scope = true;
self.stack.push(it);
return Some(Event::LeaveScope);
}
continue;
}
if it.lst[it.idx].children.is_empty() {
let ret = Event::MenuItem(&it.lst[it.idx]);
it.idx += 1;
self.stack.push(it);
return Some(ret);
} else {
let ret = Event::MenuItem(&it.lst[it.idx]);
self.stack.push(IterNode {
lst: &it.lst,
idx: it.idx + 1,
did_enter_scope: it.did_enter_scope,
did_leave_scope: it.did_leave_scope
});
self.stack.push(IterNode {
lst: &it.lst[it.idx].children,
idx: 0,
did_enter_scope: false,
did_leave_scope: false
});
return Some(ret);
}
}
None
}
}