pub struct MenuSystem<'prov, M: MenuProvider> {
pub current_menu: M::MenuId,
pub cursor: u8,
pub scroll_offset: u8,
/* private fields */
}Expand description
Stateful menu controller that owns cursor position, scroll offset, and open/closed state.
MenuSystem is parameterised over the MenuProvider implementation,
so it can call into the provider for data without any downcasting or
dynamic dispatch overhead.
Fields§
Which menu is currently active.
cursor: u80-based index of the currently-highlighted option.
scroll_offset: u8Scroll offset (for scrollable menus). 0 = first visible option is at index 0 in the full option list.
Implementations§
Source§impl<'prov, M: MenuProvider> MenuSystem<'prov, M>
impl<'prov, M: MenuProvider> MenuSystem<'prov, M>
Sourcepub fn new(provider: &'prov M) -> Self
pub fn new(provider: &'prov M) -> Self
Create a new MenuSystem backed by provider. The menu starts
closed; call open to activate it.
Examples found in repository?
524fn demo_menu() {
525 println!("\n╔══ MENU NAVIGATION ═════════════════════╗");
526 let provider = HelloConfig::new();
527 let mut menu = MenuSystem::new(&provider);
528 menu.open(MenuScreen::Main);
529
530 println!("║ Menu '{}' — 3 options", provider.title(MenuScreen::Main));
531 for (i, opt) in provider.main_menu_options.iter().enumerate() {
532 let marker = if i == menu.cursor as usize { ">" } else { " " };
533 println!("║ {} {}{}", marker, opt.label, if opt.enabled { "" } else { " (disabled)" });
534 }
535
536 // Navigate down × 2
537 menu.handle_input(&MenuInput { down: true, ..Default::default() });
538 menu.handle_input(&MenuInput { down: true, ..Default::default() });
539 println!("║ Cursor after 2× Down: {}", menu.cursor);
540
541 // Select
542 let action = menu.handle_input(&MenuInput { confirm: true, ..Default::default() });
543 println!("║ Confirm → {:?} ('{}')", action,
544 menu.selected_option().map(|o| o.label.as_str()).unwrap_or("?"));
545 println!("╚══════════════════════════════════════════╝");
546}Sourcepub fn open(&mut self, menu: M::MenuId)
pub fn open(&mut self, menu: M::MenuId)
Open a specific menu, resetting cursor and scroll to their defaults.
Examples found in repository?
524fn demo_menu() {
525 println!("\n╔══ MENU NAVIGATION ═════════════════════╗");
526 let provider = HelloConfig::new();
527 let mut menu = MenuSystem::new(&provider);
528 menu.open(MenuScreen::Main);
529
530 println!("║ Menu '{}' — 3 options", provider.title(MenuScreen::Main));
531 for (i, opt) in provider.main_menu_options.iter().enumerate() {
532 let marker = if i == menu.cursor as usize { ">" } else { " " };
533 println!("║ {} {}{}", marker, opt.label, if opt.enabled { "" } else { " (disabled)" });
534 }
535
536 // Navigate down × 2
537 menu.handle_input(&MenuInput { down: true, ..Default::default() });
538 menu.handle_input(&MenuInput { down: true, ..Default::default() });
539 println!("║ Cursor after 2× Down: {}", menu.cursor);
540
541 // Select
542 let action = menu.handle_input(&MenuInput { confirm: true, ..Default::default() });
543 println!("║ Confirm → {:?} ('{}')", action,
544 menu.selected_option().map(|o| o.label.as_str()).unwrap_or("?"));
545 println!("╚══════════════════════════════════════════╝");
546}Sourcepub fn handle_input(&mut self, input: &MenuInput) -> MenuAction
pub fn handle_input(&mut self, input: &MenuInput) -> MenuAction
Process one frame of abstract input and return a MenuAction.
The caller should call this once per frame with the aggregated directional / confirm / cancel state.
Examples found in repository?
524fn demo_menu() {
525 println!("\n╔══ MENU NAVIGATION ═════════════════════╗");
526 let provider = HelloConfig::new();
527 let mut menu = MenuSystem::new(&provider);
528 menu.open(MenuScreen::Main);
529
530 println!("║ Menu '{}' — 3 options", provider.title(MenuScreen::Main));
531 for (i, opt) in provider.main_menu_options.iter().enumerate() {
532 let marker = if i == menu.cursor as usize { ">" } else { " " };
533 println!("║ {} {}{}", marker, opt.label, if opt.enabled { "" } else { " (disabled)" });
534 }
535
536 // Navigate down × 2
537 menu.handle_input(&MenuInput { down: true, ..Default::default() });
538 menu.handle_input(&MenuInput { down: true, ..Default::default() });
539 println!("║ Cursor after 2× Down: {}", menu.cursor);
540
541 // Select
542 let action = menu.handle_input(&MenuInput { confirm: true, ..Default::default() });
543 println!("║ Confirm → {:?} ('{}')", action,
544 menu.selected_option().map(|o| o.label.as_str()).unwrap_or("?"));
545 println!("╚══════════════════════════════════════════╝");
546}Sourcepub fn selected_option(&self) -> Option<&MenuOption>
pub fn selected_option(&self) -> Option<&MenuOption>
Return the currently-selected option, or None if the cursor is
out of range or the menu is closed.
Examples found in repository?
524fn demo_menu() {
525 println!("\n╔══ MENU NAVIGATION ═════════════════════╗");
526 let provider = HelloConfig::new();
527 let mut menu = MenuSystem::new(&provider);
528 menu.open(MenuScreen::Main);
529
530 println!("║ Menu '{}' — 3 options", provider.title(MenuScreen::Main));
531 for (i, opt) in provider.main_menu_options.iter().enumerate() {
532 let marker = if i == menu.cursor as usize { ">" } else { " " };
533 println!("║ {} {}{}", marker, opt.label, if opt.enabled { "" } else { " (disabled)" });
534 }
535
536 // Navigate down × 2
537 menu.handle_input(&MenuInput { down: true, ..Default::default() });
538 menu.handle_input(&MenuInput { down: true, ..Default::default() });
539 println!("║ Cursor after 2× Down: {}", menu.cursor);
540
541 // Select
542 let action = menu.handle_input(&MenuInput { confirm: true, ..Default::default() });
543 println!("║ Confirm → {:?} ('{}')", action,
544 menu.selected_option().map(|o| o.label.as_str()).unwrap_or("?"));
545 println!("╚══════════════════════════════════════════╝");
546}