Skip to main content

MenuSystem

Struct MenuSystem 

Source
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§

§current_menu: M::MenuId

Which menu is currently active.

§cursor: u8

0-based index of the currently-highlighted option.

§scroll_offset: u8

Scroll 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>

Source

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?
examples/hello_dotzuki.rs (line 769)
766fn demo_menu() {
767    println!("\n╔══ MENU NAVIGATION ═════════════════════╗");
768    let provider = HelloConfig::new();
769    let mut menu = MenuSystem::new(&provider);
770    menu.open(MenuScreen::Main);
771
772    println!("║ Menu '{}' — 3 options", provider.title(MenuScreen::Main));
773    for (i, opt) in provider.main_menu_options.iter().enumerate() {
774        let marker = if i == menu.cursor as usize { ">" } else { " " };
775        println!(
776            "║  {} {}{}",
777            marker,
778            opt.label,
779            if opt.enabled { "" } else { " (disabled)" }
780        );
781    }
782
783    // Navigate down × 2
784    menu.handle_input(&MenuInput {
785        down: true,
786        ..Default::default()
787    });
788    menu.handle_input(&MenuInput {
789        down: true,
790        ..Default::default()
791    });
792    println!("║ Cursor after 2× Down: {}", menu.cursor);
793
794    // Select
795    let action = menu.handle_input(&MenuInput {
796        confirm: true,
797        ..Default::default()
798    });
799    println!(
800        "║ Confirm → {:?} ('{}')",
801        action,
802        menu.selected_option()
803            .map(|o| o.label.as_str())
804            .unwrap_or("?")
805    );
806    println!("╚══════════════════════════════════════════╝");
807}
Source

pub fn open(&mut self, menu: M::MenuId)

Open a specific menu, resetting cursor and scroll to their defaults.

Examples found in repository?
examples/hello_dotzuki.rs (line 770)
766fn demo_menu() {
767    println!("\n╔══ MENU NAVIGATION ═════════════════════╗");
768    let provider = HelloConfig::new();
769    let mut menu = MenuSystem::new(&provider);
770    menu.open(MenuScreen::Main);
771
772    println!("║ Menu '{}' — 3 options", provider.title(MenuScreen::Main));
773    for (i, opt) in provider.main_menu_options.iter().enumerate() {
774        let marker = if i == menu.cursor as usize { ">" } else { " " };
775        println!(
776            "║  {} {}{}",
777            marker,
778            opt.label,
779            if opt.enabled { "" } else { " (disabled)" }
780        );
781    }
782
783    // Navigate down × 2
784    menu.handle_input(&MenuInput {
785        down: true,
786        ..Default::default()
787    });
788    menu.handle_input(&MenuInput {
789        down: true,
790        ..Default::default()
791    });
792    println!("║ Cursor after 2× Down: {}", menu.cursor);
793
794    // Select
795    let action = menu.handle_input(&MenuInput {
796        confirm: true,
797        ..Default::default()
798    });
799    println!(
800        "║ Confirm → {:?} ('{}')",
801        action,
802        menu.selected_option()
803            .map(|o| o.label.as_str())
804            .unwrap_or("?")
805    );
806    println!("╚══════════════════════════════════════════╝");
807}
Source

pub fn close(&mut self)

Close the menu.

Source

pub fn is_open(&self) -> bool

Returns true if the menu is currently open.

Source

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?
examples/hello_dotzuki.rs (lines 784-787)
766fn demo_menu() {
767    println!("\n╔══ MENU NAVIGATION ═════════════════════╗");
768    let provider = HelloConfig::new();
769    let mut menu = MenuSystem::new(&provider);
770    menu.open(MenuScreen::Main);
771
772    println!("║ Menu '{}' — 3 options", provider.title(MenuScreen::Main));
773    for (i, opt) in provider.main_menu_options.iter().enumerate() {
774        let marker = if i == menu.cursor as usize { ">" } else { " " };
775        println!(
776            "║  {} {}{}",
777            marker,
778            opt.label,
779            if opt.enabled { "" } else { " (disabled)" }
780        );
781    }
782
783    // Navigate down × 2
784    menu.handle_input(&MenuInput {
785        down: true,
786        ..Default::default()
787    });
788    menu.handle_input(&MenuInput {
789        down: true,
790        ..Default::default()
791    });
792    println!("║ Cursor after 2× Down: {}", menu.cursor);
793
794    // Select
795    let action = menu.handle_input(&MenuInput {
796        confirm: true,
797        ..Default::default()
798    });
799    println!(
800        "║ Confirm → {:?} ('{}')",
801        action,
802        menu.selected_option()
803            .map(|o| o.label.as_str())
804            .unwrap_or("?")
805    );
806    println!("╚══════════════════════════════════════════╝");
807}
Source

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?
examples/hello_dotzuki.rs (line 802)
766fn demo_menu() {
767    println!("\n╔══ MENU NAVIGATION ═════════════════════╗");
768    let provider = HelloConfig::new();
769    let mut menu = MenuSystem::new(&provider);
770    menu.open(MenuScreen::Main);
771
772    println!("║ Menu '{}' — 3 options", provider.title(MenuScreen::Main));
773    for (i, opt) in provider.main_menu_options.iter().enumerate() {
774        let marker = if i == menu.cursor as usize { ">" } else { " " };
775        println!(
776            "║  {} {}{}",
777            marker,
778            opt.label,
779            if opt.enabled { "" } else { " (disabled)" }
780        );
781    }
782
783    // Navigate down × 2
784    menu.handle_input(&MenuInput {
785        down: true,
786        ..Default::default()
787    });
788    menu.handle_input(&MenuInput {
789        down: true,
790        ..Default::default()
791    });
792    println!("║ Cursor after 2× Down: {}", menu.cursor);
793
794    // Select
795    let action = menu.handle_input(&MenuInput {
796        confirm: true,
797        ..Default::default()
798    });
799    println!(
800        "║ Confirm → {:?} ('{}')",
801        action,
802        menu.selected_option()
803            .map(|o| o.label.as_str())
804            .unwrap_or("?")
805    );
806    println!("╚══════════════════════════════════════════╝");
807}
Source

pub fn render<P: Painter>(&self, painter: &mut P)

Render the menu onto painter using the Painter trait.

This draws the text box, title, options, and cursor indicator through the backend-agnostic Painter interface. TUI and pixel backends produce identical visual output (modulo resolution).

Auto Trait Implementations§

§

impl<'prov, M> Freeze for MenuSystem<'prov, M>

§

impl<'prov, M> RefUnwindSafe for MenuSystem<'prov, M>

§

impl<'prov, M> Send for MenuSystem<'prov, M>

§

impl<'prov, M> Sync for MenuSystem<'prov, M>

§

impl<'prov, M> Unpin for MenuSystem<'prov, M>

§

impl<'prov, M> UnsafeUnpin for MenuSystem<'prov, M>

§

impl<'prov, M> UnwindSafe for MenuSystem<'prov, M>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.