use std::fmt::Debug; #[derive(Debug)] pub enum Entry { Menu(Menu), Item(Item), Separator(Separator), Nothing, } impl Entry { fn index_recursive(&mut self, counter: &mut Counter) { match self { Entry::Menu(m) => { m.id = counter.next_value(); for e in &mut m.entries { e.index_recursive(counter); } } Entry::Item(i) => { i.id = counter.next_value(); } Entry::Separator(i) => { i.id = counter.next_value(); } _ => {} } } fn find_item_by_id_recursive(self, id: u32) -> Option> { match self { Entry::Menu(m) => m .entries .into_iter() .find_map(|e| e.find_item_by_id_recursive(id)), Entry::Item(i) => { if i.id == id { Some(i) } else { None } } _ => None, } } } #[derive(Debug)] pub struct Menu { pub id: u32, pub text: String, pub entries: Vec>, } impl Menu { /// Assigns all menu entries consecutive IDs starting from the given first ID. /// /// Returns next non-used value. /// /// This is useful for popup menus. pub fn index(&mut self, first_id: u32) -> u32 { let mut counter = Counter::starting_from(first_id); for e in &mut self.entries { e.index_recursive(&mut counter); } counter.next_value() } /// Returns the item that has the given ID. /// /// Also looks into sub menus. /// /// This is useful for popup menus. pub fn find_item_by_id(self, id: u32) -> Option> { self.entries .into_iter() .find_map(|e| e.find_item_by_id_recursive(id)) } } #[derive(Debug)] pub struct Item { pub id: u32, pub text: String, pub result: R, pub opts: ItemOpts, } #[derive(Debug)] pub struct Separator { pub id: u32, pub text: Option, } /// Unlabeled menu. /// /// This is useful for aggregating a set of entries that can then be added in one go. pub fn anonymous_menu(entries: Vec>) -> Menu { Menu { id: 0, text: "".to_owned(), entries, } } pub fn menu(text: impl Into, entries: Vec>) -> Entry { Entry::Menu(Menu { id: 0, text: text.into(), entries, }) } pub fn item(text: impl Into, result: R) -> Entry { Entry::Item(Item { id: 0, text: text.into(), result, opts: Default::default(), }) } pub fn separator() -> Entry { Entry::Separator(Separator { id: 0, text: None }) } pub fn labeled_separator(name: impl Into) -> Entry { Entry::Separator(Separator { id: 0, text: Some(name.into()), }) } pub fn item_with_opts(text: impl Into, opts: ItemOpts, result: R) -> Entry { Entry::Item(Item { id: 0, text: text.into(), result, opts, }) } #[allow(clippy::redundant_closure)] pub fn disabled_item(text: impl Into) -> Entry { item_with_opts( text, ItemOpts { enabled: false, checked: false, }, R::default(), ) } #[derive(Debug)] pub struct ItemOpts { pub enabled: bool, pub checked: bool, } impl Default for ItemOpts { fn default() -> Self { Self { enabled: true, checked: false, } } } struct Counter { value: u32, } impl Counter { pub fn starting_from(value: u32) -> Self { Self { value } } pub fn next_value(&mut self) -> u32 { let val = self.value; self.value += 1; val } }