Skip to main content

MenuBarBuilder

Struct MenuBarBuilder 

Source
pub struct MenuBarBuilder { /* private fields */ }
Expand description

Builder for MenuBar views.

Implementations§

Source§

impl MenuBarBuilder

Source

pub fn new() -> Self

Source

pub fn menu(self, menu: Menu) -> Self

Add a menu to the menu bar.

Examples found in repository?
examples/20_menu_bar.rs (line 123)
26    fn render(&self, cx: Scope) -> View {
27        let show_help = state!(cx, || false);
28
29        // F1 toggles help
30        cx.use_command(
31            KeyBinding::key(KeyCode::F(1)),
32            with!(show_help => move || show_help.update(|v| *v = !*v)),
33        );
34
35        // Menu state
36        let active_menu = state!(cx, || Option::<usize>::None);
37        let highlighted_menu = state!(cx, || 0usize);
38        let selected_item = state!(cx, || 0usize);
39
40        // App state
41        let message = state!(cx, || "Use Tab to focus menu bar, then Enter to open".to_string());
42        let counter = state!(cx, || 0i32);
43
44        // Command handler - executes menu commands
45        let handle_command = with!(message, counter, active_menu, selected_item => move |cmd_id: &'static str| {
46            let msg = match cmd_id {
47                "file.new" => "Created new file".to_string(),
48                "file.open" => "Opening file...".to_string(),
49                "file.save" => "File saved".to_string(),
50                "file.quit" => "Use Ctrl+Q to quit".to_string(),
51                "edit.undo" => "Undone".to_string(),
52                "edit.redo" => "Redone".to_string(),
53                "edit.cut" => "Cut to clipboard".to_string(),
54                "edit.copy" => "Copied to clipboard".to_string(),
55                "edit.paste" => "Pasted from clipboard".to_string(),
56                "counter.increment" => {
57                    counter.update(|n| *n += 1);
58                    format!("Counter: {}", counter.get())
59                }
60                "counter.decrement" => {
61                    counter.update(|n| *n -= 1);
62                    format!("Counter: {}", counter.get())
63                }
64                "counter.reset" => {
65                    counter.set(0);
66                    "Counter reset".to_string()
67                }
68                _ => format!("Unknown: {}", cmd_id),
69            };
70            message.set(msg);
71            // Close menu after executing command
72            active_menu.set(None);
73            selected_item.set(0);
74        });
75
76        // Menu change handler - opens/closes menus
77        let on_menu_change = with!(active_menu, highlighted_menu, selected_item => move |idx: usize| {
78            if active_menu.get() == Some(idx) {
79                // Clicking same menu toggles it closed
80                active_menu.set(None);
81            } else {
82                active_menu.set(Some(idx));
83                highlighted_menu.set(idx); // Keep highlight in sync
84                selected_item.set(0);
85            }
86        });
87
88        // Highlight change handler - arrow key navigation when no menu is open
89        let on_highlight_change = with!(highlighted_menu => move |idx: usize| {
90            highlighted_menu.set(idx);
91        });
92
93        // Item change handler - navigates within menu
94        let on_item_change = with!(selected_item => move |idx: usize| {
95            selected_item.set(idx);
96        });
97
98        // Build menus
99        let file_menu = Menu::new("File")
100            .command_with_shortcut("file.new", "New", "Ctrl+N")
101            .command_with_shortcut("file.open", "Open", "Ctrl+O")
102            .command_with_shortcut("file.save", "Save", "Ctrl+S")
103            .separator()
104            .command_with_shortcut("file.quit", "Quit", "Ctrl+Q");
105
106        let edit_menu = Menu::new("Edit")
107            .command_with_shortcut("edit.undo", "Undo", "Ctrl+Z")
108            .command_with_shortcut("edit.redo", "Redo", "Ctrl+Y")
109            .separator()
110            .command_with_shortcut("edit.cut", "Cut", "Ctrl+X")
111            .command_with_shortcut("edit.copy", "Copy", "Ctrl+C")
112            .command_with_shortcut("edit.paste", "Paste", "Ctrl+V");
113
114        let counter_menu = Menu::new("Counter")
115            .command("counter.increment", "Increment")
116            .command("counter.decrement", "Decrement")
117            .separator()
118            .command("counter.reset", "Reset to Zero");
119
120        View::vstack()
121            .child(
122                View::menu_bar()
123                    .menu(file_menu)
124                    .menu(edit_menu)
125                    .menu(counter_menu)
126                    .active_menu(active_menu.get())
127                    .highlighted_menu(highlighted_menu.get())
128                    .selected_item(selected_item.get())
129                    .on_select(handle_command)
130                    .on_menu_change(on_menu_change)
131                    .on_highlight_change(on_highlight_change)
132                    .on_item_change(on_item_change)
133                    .build(),
134            )
135            .child(
136                View::boxed()
137                    .flex(1)
138                    .border(true)
139                    .padding(2)
140                    .child(
141                        View::vstack()
142                            .spacing(1)
143                            .child(View::styled_text("Menu Bar Demo").bold().build())
144                            .child(
145                                View::styled_text(format!("Counter: {}", counter.get()))
146                                    .color(Color::Cyan)
147                                    .bold()
148                                    .build(),
149                            )
150                            .child(
151                                View::styled_text(format!("Status: {}", message.get()))
152                                    .dim()
153                                    .build(),
154                            )
155                            .child(View::spacer())
156                            .child(View::styled_text("Keyboard Navigation:").bold().build())
157                            .child(View::text("  Tab         Focus menu bar"))
158                            .child(View::text("  Enter       Open menu / Execute item"))
159                            .child(View::text("  Up/Down     Navigate menu items"))
160                            .child(View::text("  Left/Right  Switch between menus"))
161                            .child(View::text("  Escape      Close menu"))
162                            .child(View::text("  Ctrl+Q      Quit"))
163                            .child(View::text("  F1          Help"))
164                            .build(),
165                    )
166                    .build(),
167            )
168            .child(
169                View::modal()
170                    .visible(show_help.get())
171                    .title("Example 20: Menu Bar")
172                    .on_dismiss(with!(show_help => move || show_help.set(false)))
173                    .child(
174                        View::vstack()
175                            .child(View::styled_text("What you're seeing").bold().build())
176                            .child(View::text("• Dropdown menu bar with keyboard nav"))
177                            .child(View::text("• Menu items with shortcuts"))
178                            .child(View::text("• Separators in menus"))
179                            .child(View::gap(1))
180                            .child(View::styled_text("Key concepts").bold().build())
181                            .child(View::text("• View::menu_bar() creates menu system"))
182                            .child(View::text("• Menu::new().command() adds items"))
183                            .child(View::text("• .command_with_shortcut() shows key hints"))
184                            .child(View::text("• on_select receives command ID"))
185                            .child(View::gap(1))
186                            .child(View::styled_text("Try this").bold().build())
187                            .child(View::text("• Tab to menu, Enter to open"))
188                            .child(View::text("• Arrow keys navigate menus"))
189                            .child(View::text("• Try the Counter menu"))
190                            .child(View::gap(1))
191                            .child(View::styled_text("Next up").bold().build())
192                            .child(View::text("→ 21_toasts: toast notifications"))
193                            .child(View::gap(1))
194                            .child(View::styled_text("Press Escape to close").dim().build())
195                            .build(),
196                    )
197                    .build(),
198            )
199            .build()
200    }
Source

pub fn active_menu(self, index: Option<usize>) -> Self

Set the active menu index (which menu has its dropdown open).

Examples found in repository?
examples/20_menu_bar.rs (line 126)
26    fn render(&self, cx: Scope) -> View {
27        let show_help = state!(cx, || false);
28
29        // F1 toggles help
30        cx.use_command(
31            KeyBinding::key(KeyCode::F(1)),
32            with!(show_help => move || show_help.update(|v| *v = !*v)),
33        );
34
35        // Menu state
36        let active_menu = state!(cx, || Option::<usize>::None);
37        let highlighted_menu = state!(cx, || 0usize);
38        let selected_item = state!(cx, || 0usize);
39
40        // App state
41        let message = state!(cx, || "Use Tab to focus menu bar, then Enter to open".to_string());
42        let counter = state!(cx, || 0i32);
43
44        // Command handler - executes menu commands
45        let handle_command = with!(message, counter, active_menu, selected_item => move |cmd_id: &'static str| {
46            let msg = match cmd_id {
47                "file.new" => "Created new file".to_string(),
48                "file.open" => "Opening file...".to_string(),
49                "file.save" => "File saved".to_string(),
50                "file.quit" => "Use Ctrl+Q to quit".to_string(),
51                "edit.undo" => "Undone".to_string(),
52                "edit.redo" => "Redone".to_string(),
53                "edit.cut" => "Cut to clipboard".to_string(),
54                "edit.copy" => "Copied to clipboard".to_string(),
55                "edit.paste" => "Pasted from clipboard".to_string(),
56                "counter.increment" => {
57                    counter.update(|n| *n += 1);
58                    format!("Counter: {}", counter.get())
59                }
60                "counter.decrement" => {
61                    counter.update(|n| *n -= 1);
62                    format!("Counter: {}", counter.get())
63                }
64                "counter.reset" => {
65                    counter.set(0);
66                    "Counter reset".to_string()
67                }
68                _ => format!("Unknown: {}", cmd_id),
69            };
70            message.set(msg);
71            // Close menu after executing command
72            active_menu.set(None);
73            selected_item.set(0);
74        });
75
76        // Menu change handler - opens/closes menus
77        let on_menu_change = with!(active_menu, highlighted_menu, selected_item => move |idx: usize| {
78            if active_menu.get() == Some(idx) {
79                // Clicking same menu toggles it closed
80                active_menu.set(None);
81            } else {
82                active_menu.set(Some(idx));
83                highlighted_menu.set(idx); // Keep highlight in sync
84                selected_item.set(0);
85            }
86        });
87
88        // Highlight change handler - arrow key navigation when no menu is open
89        let on_highlight_change = with!(highlighted_menu => move |idx: usize| {
90            highlighted_menu.set(idx);
91        });
92
93        // Item change handler - navigates within menu
94        let on_item_change = with!(selected_item => move |idx: usize| {
95            selected_item.set(idx);
96        });
97
98        // Build menus
99        let file_menu = Menu::new("File")
100            .command_with_shortcut("file.new", "New", "Ctrl+N")
101            .command_with_shortcut("file.open", "Open", "Ctrl+O")
102            .command_with_shortcut("file.save", "Save", "Ctrl+S")
103            .separator()
104            .command_with_shortcut("file.quit", "Quit", "Ctrl+Q");
105
106        let edit_menu = Menu::new("Edit")
107            .command_with_shortcut("edit.undo", "Undo", "Ctrl+Z")
108            .command_with_shortcut("edit.redo", "Redo", "Ctrl+Y")
109            .separator()
110            .command_with_shortcut("edit.cut", "Cut", "Ctrl+X")
111            .command_with_shortcut("edit.copy", "Copy", "Ctrl+C")
112            .command_with_shortcut("edit.paste", "Paste", "Ctrl+V");
113
114        let counter_menu = Menu::new("Counter")
115            .command("counter.increment", "Increment")
116            .command("counter.decrement", "Decrement")
117            .separator()
118            .command("counter.reset", "Reset to Zero");
119
120        View::vstack()
121            .child(
122                View::menu_bar()
123                    .menu(file_menu)
124                    .menu(edit_menu)
125                    .menu(counter_menu)
126                    .active_menu(active_menu.get())
127                    .highlighted_menu(highlighted_menu.get())
128                    .selected_item(selected_item.get())
129                    .on_select(handle_command)
130                    .on_menu_change(on_menu_change)
131                    .on_highlight_change(on_highlight_change)
132                    .on_item_change(on_item_change)
133                    .build(),
134            )
135            .child(
136                View::boxed()
137                    .flex(1)
138                    .border(true)
139                    .padding(2)
140                    .child(
141                        View::vstack()
142                            .spacing(1)
143                            .child(View::styled_text("Menu Bar Demo").bold().build())
144                            .child(
145                                View::styled_text(format!("Counter: {}", counter.get()))
146                                    .color(Color::Cyan)
147                                    .bold()
148                                    .build(),
149                            )
150                            .child(
151                                View::styled_text(format!("Status: {}", message.get()))
152                                    .dim()
153                                    .build(),
154                            )
155                            .child(View::spacer())
156                            .child(View::styled_text("Keyboard Navigation:").bold().build())
157                            .child(View::text("  Tab         Focus menu bar"))
158                            .child(View::text("  Enter       Open menu / Execute item"))
159                            .child(View::text("  Up/Down     Navigate menu items"))
160                            .child(View::text("  Left/Right  Switch between menus"))
161                            .child(View::text("  Escape      Close menu"))
162                            .child(View::text("  Ctrl+Q      Quit"))
163                            .child(View::text("  F1          Help"))
164                            .build(),
165                    )
166                    .build(),
167            )
168            .child(
169                View::modal()
170                    .visible(show_help.get())
171                    .title("Example 20: Menu Bar")
172                    .on_dismiss(with!(show_help => move || show_help.set(false)))
173                    .child(
174                        View::vstack()
175                            .child(View::styled_text("What you're seeing").bold().build())
176                            .child(View::text("• Dropdown menu bar with keyboard nav"))
177                            .child(View::text("• Menu items with shortcuts"))
178                            .child(View::text("• Separators in menus"))
179                            .child(View::gap(1))
180                            .child(View::styled_text("Key concepts").bold().build())
181                            .child(View::text("• View::menu_bar() creates menu system"))
182                            .child(View::text("• Menu::new().command() adds items"))
183                            .child(View::text("• .command_with_shortcut() shows key hints"))
184                            .child(View::text("• on_select receives command ID"))
185                            .child(View::gap(1))
186                            .child(View::styled_text("Try this").bold().build())
187                            .child(View::text("• Tab to menu, Enter to open"))
188                            .child(View::text("• Arrow keys navigate menus"))
189                            .child(View::text("• Try the Counter menu"))
190                            .child(View::gap(1))
191                            .child(View::styled_text("Next up").bold().build())
192                            .child(View::text("→ 21_toasts: toast notifications"))
193                            .child(View::gap(1))
194                            .child(View::styled_text("Press Escape to close").dim().build())
195                            .build(),
196                    )
197                    .build(),
198            )
199            .build()
200    }
Source

pub fn highlighted_menu(self, index: usize) -> Self

Set the highlighted menu index (for keyboard navigation).

Examples found in repository?
examples/20_menu_bar.rs (line 127)
26    fn render(&self, cx: Scope) -> View {
27        let show_help = state!(cx, || false);
28
29        // F1 toggles help
30        cx.use_command(
31            KeyBinding::key(KeyCode::F(1)),
32            with!(show_help => move || show_help.update(|v| *v = !*v)),
33        );
34
35        // Menu state
36        let active_menu = state!(cx, || Option::<usize>::None);
37        let highlighted_menu = state!(cx, || 0usize);
38        let selected_item = state!(cx, || 0usize);
39
40        // App state
41        let message = state!(cx, || "Use Tab to focus menu bar, then Enter to open".to_string());
42        let counter = state!(cx, || 0i32);
43
44        // Command handler - executes menu commands
45        let handle_command = with!(message, counter, active_menu, selected_item => move |cmd_id: &'static str| {
46            let msg = match cmd_id {
47                "file.new" => "Created new file".to_string(),
48                "file.open" => "Opening file...".to_string(),
49                "file.save" => "File saved".to_string(),
50                "file.quit" => "Use Ctrl+Q to quit".to_string(),
51                "edit.undo" => "Undone".to_string(),
52                "edit.redo" => "Redone".to_string(),
53                "edit.cut" => "Cut to clipboard".to_string(),
54                "edit.copy" => "Copied to clipboard".to_string(),
55                "edit.paste" => "Pasted from clipboard".to_string(),
56                "counter.increment" => {
57                    counter.update(|n| *n += 1);
58                    format!("Counter: {}", counter.get())
59                }
60                "counter.decrement" => {
61                    counter.update(|n| *n -= 1);
62                    format!("Counter: {}", counter.get())
63                }
64                "counter.reset" => {
65                    counter.set(0);
66                    "Counter reset".to_string()
67                }
68                _ => format!("Unknown: {}", cmd_id),
69            };
70            message.set(msg);
71            // Close menu after executing command
72            active_menu.set(None);
73            selected_item.set(0);
74        });
75
76        // Menu change handler - opens/closes menus
77        let on_menu_change = with!(active_menu, highlighted_menu, selected_item => move |idx: usize| {
78            if active_menu.get() == Some(idx) {
79                // Clicking same menu toggles it closed
80                active_menu.set(None);
81            } else {
82                active_menu.set(Some(idx));
83                highlighted_menu.set(idx); // Keep highlight in sync
84                selected_item.set(0);
85            }
86        });
87
88        // Highlight change handler - arrow key navigation when no menu is open
89        let on_highlight_change = with!(highlighted_menu => move |idx: usize| {
90            highlighted_menu.set(idx);
91        });
92
93        // Item change handler - navigates within menu
94        let on_item_change = with!(selected_item => move |idx: usize| {
95            selected_item.set(idx);
96        });
97
98        // Build menus
99        let file_menu = Menu::new("File")
100            .command_with_shortcut("file.new", "New", "Ctrl+N")
101            .command_with_shortcut("file.open", "Open", "Ctrl+O")
102            .command_with_shortcut("file.save", "Save", "Ctrl+S")
103            .separator()
104            .command_with_shortcut("file.quit", "Quit", "Ctrl+Q");
105
106        let edit_menu = Menu::new("Edit")
107            .command_with_shortcut("edit.undo", "Undo", "Ctrl+Z")
108            .command_with_shortcut("edit.redo", "Redo", "Ctrl+Y")
109            .separator()
110            .command_with_shortcut("edit.cut", "Cut", "Ctrl+X")
111            .command_with_shortcut("edit.copy", "Copy", "Ctrl+C")
112            .command_with_shortcut("edit.paste", "Paste", "Ctrl+V");
113
114        let counter_menu = Menu::new("Counter")
115            .command("counter.increment", "Increment")
116            .command("counter.decrement", "Decrement")
117            .separator()
118            .command("counter.reset", "Reset to Zero");
119
120        View::vstack()
121            .child(
122                View::menu_bar()
123                    .menu(file_menu)
124                    .menu(edit_menu)
125                    .menu(counter_menu)
126                    .active_menu(active_menu.get())
127                    .highlighted_menu(highlighted_menu.get())
128                    .selected_item(selected_item.get())
129                    .on_select(handle_command)
130                    .on_menu_change(on_menu_change)
131                    .on_highlight_change(on_highlight_change)
132                    .on_item_change(on_item_change)
133                    .build(),
134            )
135            .child(
136                View::boxed()
137                    .flex(1)
138                    .border(true)
139                    .padding(2)
140                    .child(
141                        View::vstack()
142                            .spacing(1)
143                            .child(View::styled_text("Menu Bar Demo").bold().build())
144                            .child(
145                                View::styled_text(format!("Counter: {}", counter.get()))
146                                    .color(Color::Cyan)
147                                    .bold()
148                                    .build(),
149                            )
150                            .child(
151                                View::styled_text(format!("Status: {}", message.get()))
152                                    .dim()
153                                    .build(),
154                            )
155                            .child(View::spacer())
156                            .child(View::styled_text("Keyboard Navigation:").bold().build())
157                            .child(View::text("  Tab         Focus menu bar"))
158                            .child(View::text("  Enter       Open menu / Execute item"))
159                            .child(View::text("  Up/Down     Navigate menu items"))
160                            .child(View::text("  Left/Right  Switch between menus"))
161                            .child(View::text("  Escape      Close menu"))
162                            .child(View::text("  Ctrl+Q      Quit"))
163                            .child(View::text("  F1          Help"))
164                            .build(),
165                    )
166                    .build(),
167            )
168            .child(
169                View::modal()
170                    .visible(show_help.get())
171                    .title("Example 20: Menu Bar")
172                    .on_dismiss(with!(show_help => move || show_help.set(false)))
173                    .child(
174                        View::vstack()
175                            .child(View::styled_text("What you're seeing").bold().build())
176                            .child(View::text("• Dropdown menu bar with keyboard nav"))
177                            .child(View::text("• Menu items with shortcuts"))
178                            .child(View::text("• Separators in menus"))
179                            .child(View::gap(1))
180                            .child(View::styled_text("Key concepts").bold().build())
181                            .child(View::text("• View::menu_bar() creates menu system"))
182                            .child(View::text("• Menu::new().command() adds items"))
183                            .child(View::text("• .command_with_shortcut() shows key hints"))
184                            .child(View::text("• on_select receives command ID"))
185                            .child(View::gap(1))
186                            .child(View::styled_text("Try this").bold().build())
187                            .child(View::text("• Tab to menu, Enter to open"))
188                            .child(View::text("• Arrow keys navigate menus"))
189                            .child(View::text("• Try the Counter menu"))
190                            .child(View::gap(1))
191                            .child(View::styled_text("Next up").bold().build())
192                            .child(View::text("→ 21_toasts: toast notifications"))
193                            .child(View::gap(1))
194                            .child(View::styled_text("Press Escape to close").dim().build())
195                            .build(),
196                    )
197                    .build(),
198            )
199            .build()
200    }
Source

pub fn selected_item(self, index: usize) -> Self

Set the selected item in the active menu.

Examples found in repository?
examples/20_menu_bar.rs (line 128)
26    fn render(&self, cx: Scope) -> View {
27        let show_help = state!(cx, || false);
28
29        // F1 toggles help
30        cx.use_command(
31            KeyBinding::key(KeyCode::F(1)),
32            with!(show_help => move || show_help.update(|v| *v = !*v)),
33        );
34
35        // Menu state
36        let active_menu = state!(cx, || Option::<usize>::None);
37        let highlighted_menu = state!(cx, || 0usize);
38        let selected_item = state!(cx, || 0usize);
39
40        // App state
41        let message = state!(cx, || "Use Tab to focus menu bar, then Enter to open".to_string());
42        let counter = state!(cx, || 0i32);
43
44        // Command handler - executes menu commands
45        let handle_command = with!(message, counter, active_menu, selected_item => move |cmd_id: &'static str| {
46            let msg = match cmd_id {
47                "file.new" => "Created new file".to_string(),
48                "file.open" => "Opening file...".to_string(),
49                "file.save" => "File saved".to_string(),
50                "file.quit" => "Use Ctrl+Q to quit".to_string(),
51                "edit.undo" => "Undone".to_string(),
52                "edit.redo" => "Redone".to_string(),
53                "edit.cut" => "Cut to clipboard".to_string(),
54                "edit.copy" => "Copied to clipboard".to_string(),
55                "edit.paste" => "Pasted from clipboard".to_string(),
56                "counter.increment" => {
57                    counter.update(|n| *n += 1);
58                    format!("Counter: {}", counter.get())
59                }
60                "counter.decrement" => {
61                    counter.update(|n| *n -= 1);
62                    format!("Counter: {}", counter.get())
63                }
64                "counter.reset" => {
65                    counter.set(0);
66                    "Counter reset".to_string()
67                }
68                _ => format!("Unknown: {}", cmd_id),
69            };
70            message.set(msg);
71            // Close menu after executing command
72            active_menu.set(None);
73            selected_item.set(0);
74        });
75
76        // Menu change handler - opens/closes menus
77        let on_menu_change = with!(active_menu, highlighted_menu, selected_item => move |idx: usize| {
78            if active_menu.get() == Some(idx) {
79                // Clicking same menu toggles it closed
80                active_menu.set(None);
81            } else {
82                active_menu.set(Some(idx));
83                highlighted_menu.set(idx); // Keep highlight in sync
84                selected_item.set(0);
85            }
86        });
87
88        // Highlight change handler - arrow key navigation when no menu is open
89        let on_highlight_change = with!(highlighted_menu => move |idx: usize| {
90            highlighted_menu.set(idx);
91        });
92
93        // Item change handler - navigates within menu
94        let on_item_change = with!(selected_item => move |idx: usize| {
95            selected_item.set(idx);
96        });
97
98        // Build menus
99        let file_menu = Menu::new("File")
100            .command_with_shortcut("file.new", "New", "Ctrl+N")
101            .command_with_shortcut("file.open", "Open", "Ctrl+O")
102            .command_with_shortcut("file.save", "Save", "Ctrl+S")
103            .separator()
104            .command_with_shortcut("file.quit", "Quit", "Ctrl+Q");
105
106        let edit_menu = Menu::new("Edit")
107            .command_with_shortcut("edit.undo", "Undo", "Ctrl+Z")
108            .command_with_shortcut("edit.redo", "Redo", "Ctrl+Y")
109            .separator()
110            .command_with_shortcut("edit.cut", "Cut", "Ctrl+X")
111            .command_with_shortcut("edit.copy", "Copy", "Ctrl+C")
112            .command_with_shortcut("edit.paste", "Paste", "Ctrl+V");
113
114        let counter_menu = Menu::new("Counter")
115            .command("counter.increment", "Increment")
116            .command("counter.decrement", "Decrement")
117            .separator()
118            .command("counter.reset", "Reset to Zero");
119
120        View::vstack()
121            .child(
122                View::menu_bar()
123                    .menu(file_menu)
124                    .menu(edit_menu)
125                    .menu(counter_menu)
126                    .active_menu(active_menu.get())
127                    .highlighted_menu(highlighted_menu.get())
128                    .selected_item(selected_item.get())
129                    .on_select(handle_command)
130                    .on_menu_change(on_menu_change)
131                    .on_highlight_change(on_highlight_change)
132                    .on_item_change(on_item_change)
133                    .build(),
134            )
135            .child(
136                View::boxed()
137                    .flex(1)
138                    .border(true)
139                    .padding(2)
140                    .child(
141                        View::vstack()
142                            .spacing(1)
143                            .child(View::styled_text("Menu Bar Demo").bold().build())
144                            .child(
145                                View::styled_text(format!("Counter: {}", counter.get()))
146                                    .color(Color::Cyan)
147                                    .bold()
148                                    .build(),
149                            )
150                            .child(
151                                View::styled_text(format!("Status: {}", message.get()))
152                                    .dim()
153                                    .build(),
154                            )
155                            .child(View::spacer())
156                            .child(View::styled_text("Keyboard Navigation:").bold().build())
157                            .child(View::text("  Tab         Focus menu bar"))
158                            .child(View::text("  Enter       Open menu / Execute item"))
159                            .child(View::text("  Up/Down     Navigate menu items"))
160                            .child(View::text("  Left/Right  Switch between menus"))
161                            .child(View::text("  Escape      Close menu"))
162                            .child(View::text("  Ctrl+Q      Quit"))
163                            .child(View::text("  F1          Help"))
164                            .build(),
165                    )
166                    .build(),
167            )
168            .child(
169                View::modal()
170                    .visible(show_help.get())
171                    .title("Example 20: Menu Bar")
172                    .on_dismiss(with!(show_help => move || show_help.set(false)))
173                    .child(
174                        View::vstack()
175                            .child(View::styled_text("What you're seeing").bold().build())
176                            .child(View::text("• Dropdown menu bar with keyboard nav"))
177                            .child(View::text("• Menu items with shortcuts"))
178                            .child(View::text("• Separators in menus"))
179                            .child(View::gap(1))
180                            .child(View::styled_text("Key concepts").bold().build())
181                            .child(View::text("• View::menu_bar() creates menu system"))
182                            .child(View::text("• Menu::new().command() adds items"))
183                            .child(View::text("• .command_with_shortcut() shows key hints"))
184                            .child(View::text("• on_select receives command ID"))
185                            .child(View::gap(1))
186                            .child(View::styled_text("Try this").bold().build())
187                            .child(View::text("• Tab to menu, Enter to open"))
188                            .child(View::text("• Arrow keys navigate menus"))
189                            .child(View::text("• Try the Counter menu"))
190                            .child(View::gap(1))
191                            .child(View::styled_text("Next up").bold().build())
192                            .child(View::text("→ 21_toasts: toast notifications"))
193                            .child(View::gap(1))
194                            .child(View::styled_text("Press Escape to close").dim().build())
195                            .build(),
196                    )
197                    .build(),
198            )
199            .build()
200    }
Source

pub fn on_select(self, callback: impl Fn(&'static str) + 'static) -> Self

Set the callback for command selection.

Examples found in repository?
examples/20_menu_bar.rs (line 129)
26    fn render(&self, cx: Scope) -> View {
27        let show_help = state!(cx, || false);
28
29        // F1 toggles help
30        cx.use_command(
31            KeyBinding::key(KeyCode::F(1)),
32            with!(show_help => move || show_help.update(|v| *v = !*v)),
33        );
34
35        // Menu state
36        let active_menu = state!(cx, || Option::<usize>::None);
37        let highlighted_menu = state!(cx, || 0usize);
38        let selected_item = state!(cx, || 0usize);
39
40        // App state
41        let message = state!(cx, || "Use Tab to focus menu bar, then Enter to open".to_string());
42        let counter = state!(cx, || 0i32);
43
44        // Command handler - executes menu commands
45        let handle_command = with!(message, counter, active_menu, selected_item => move |cmd_id: &'static str| {
46            let msg = match cmd_id {
47                "file.new" => "Created new file".to_string(),
48                "file.open" => "Opening file...".to_string(),
49                "file.save" => "File saved".to_string(),
50                "file.quit" => "Use Ctrl+Q to quit".to_string(),
51                "edit.undo" => "Undone".to_string(),
52                "edit.redo" => "Redone".to_string(),
53                "edit.cut" => "Cut to clipboard".to_string(),
54                "edit.copy" => "Copied to clipboard".to_string(),
55                "edit.paste" => "Pasted from clipboard".to_string(),
56                "counter.increment" => {
57                    counter.update(|n| *n += 1);
58                    format!("Counter: {}", counter.get())
59                }
60                "counter.decrement" => {
61                    counter.update(|n| *n -= 1);
62                    format!("Counter: {}", counter.get())
63                }
64                "counter.reset" => {
65                    counter.set(0);
66                    "Counter reset".to_string()
67                }
68                _ => format!("Unknown: {}", cmd_id),
69            };
70            message.set(msg);
71            // Close menu after executing command
72            active_menu.set(None);
73            selected_item.set(0);
74        });
75
76        // Menu change handler - opens/closes menus
77        let on_menu_change = with!(active_menu, highlighted_menu, selected_item => move |idx: usize| {
78            if active_menu.get() == Some(idx) {
79                // Clicking same menu toggles it closed
80                active_menu.set(None);
81            } else {
82                active_menu.set(Some(idx));
83                highlighted_menu.set(idx); // Keep highlight in sync
84                selected_item.set(0);
85            }
86        });
87
88        // Highlight change handler - arrow key navigation when no menu is open
89        let on_highlight_change = with!(highlighted_menu => move |idx: usize| {
90            highlighted_menu.set(idx);
91        });
92
93        // Item change handler - navigates within menu
94        let on_item_change = with!(selected_item => move |idx: usize| {
95            selected_item.set(idx);
96        });
97
98        // Build menus
99        let file_menu = Menu::new("File")
100            .command_with_shortcut("file.new", "New", "Ctrl+N")
101            .command_with_shortcut("file.open", "Open", "Ctrl+O")
102            .command_with_shortcut("file.save", "Save", "Ctrl+S")
103            .separator()
104            .command_with_shortcut("file.quit", "Quit", "Ctrl+Q");
105
106        let edit_menu = Menu::new("Edit")
107            .command_with_shortcut("edit.undo", "Undo", "Ctrl+Z")
108            .command_with_shortcut("edit.redo", "Redo", "Ctrl+Y")
109            .separator()
110            .command_with_shortcut("edit.cut", "Cut", "Ctrl+X")
111            .command_with_shortcut("edit.copy", "Copy", "Ctrl+C")
112            .command_with_shortcut("edit.paste", "Paste", "Ctrl+V");
113
114        let counter_menu = Menu::new("Counter")
115            .command("counter.increment", "Increment")
116            .command("counter.decrement", "Decrement")
117            .separator()
118            .command("counter.reset", "Reset to Zero");
119
120        View::vstack()
121            .child(
122                View::menu_bar()
123                    .menu(file_menu)
124                    .menu(edit_menu)
125                    .menu(counter_menu)
126                    .active_menu(active_menu.get())
127                    .highlighted_menu(highlighted_menu.get())
128                    .selected_item(selected_item.get())
129                    .on_select(handle_command)
130                    .on_menu_change(on_menu_change)
131                    .on_highlight_change(on_highlight_change)
132                    .on_item_change(on_item_change)
133                    .build(),
134            )
135            .child(
136                View::boxed()
137                    .flex(1)
138                    .border(true)
139                    .padding(2)
140                    .child(
141                        View::vstack()
142                            .spacing(1)
143                            .child(View::styled_text("Menu Bar Demo").bold().build())
144                            .child(
145                                View::styled_text(format!("Counter: {}", counter.get()))
146                                    .color(Color::Cyan)
147                                    .bold()
148                                    .build(),
149                            )
150                            .child(
151                                View::styled_text(format!("Status: {}", message.get()))
152                                    .dim()
153                                    .build(),
154                            )
155                            .child(View::spacer())
156                            .child(View::styled_text("Keyboard Navigation:").bold().build())
157                            .child(View::text("  Tab         Focus menu bar"))
158                            .child(View::text("  Enter       Open menu / Execute item"))
159                            .child(View::text("  Up/Down     Navigate menu items"))
160                            .child(View::text("  Left/Right  Switch between menus"))
161                            .child(View::text("  Escape      Close menu"))
162                            .child(View::text("  Ctrl+Q      Quit"))
163                            .child(View::text("  F1          Help"))
164                            .build(),
165                    )
166                    .build(),
167            )
168            .child(
169                View::modal()
170                    .visible(show_help.get())
171                    .title("Example 20: Menu Bar")
172                    .on_dismiss(with!(show_help => move || show_help.set(false)))
173                    .child(
174                        View::vstack()
175                            .child(View::styled_text("What you're seeing").bold().build())
176                            .child(View::text("• Dropdown menu bar with keyboard nav"))
177                            .child(View::text("• Menu items with shortcuts"))
178                            .child(View::text("• Separators in menus"))
179                            .child(View::gap(1))
180                            .child(View::styled_text("Key concepts").bold().build())
181                            .child(View::text("• View::menu_bar() creates menu system"))
182                            .child(View::text("• Menu::new().command() adds items"))
183                            .child(View::text("• .command_with_shortcut() shows key hints"))
184                            .child(View::text("• on_select receives command ID"))
185                            .child(View::gap(1))
186                            .child(View::styled_text("Try this").bold().build())
187                            .child(View::text("• Tab to menu, Enter to open"))
188                            .child(View::text("• Arrow keys navigate menus"))
189                            .child(View::text("• Try the Counter menu"))
190                            .child(View::gap(1))
191                            .child(View::styled_text("Next up").bold().build())
192                            .child(View::text("→ 21_toasts: toast notifications"))
193                            .child(View::gap(1))
194                            .child(View::styled_text("Press Escape to close").dim().build())
195                            .build(),
196                    )
197                    .build(),
198            )
199            .build()
200    }
Source

pub fn on_menu_change(self, callback: impl Fn(usize) + 'static) -> Self

Set the callback for menu changes (opens/closes dropdown).

Examples found in repository?
examples/20_menu_bar.rs (line 130)
26    fn render(&self, cx: Scope) -> View {
27        let show_help = state!(cx, || false);
28
29        // F1 toggles help
30        cx.use_command(
31            KeyBinding::key(KeyCode::F(1)),
32            with!(show_help => move || show_help.update(|v| *v = !*v)),
33        );
34
35        // Menu state
36        let active_menu = state!(cx, || Option::<usize>::None);
37        let highlighted_menu = state!(cx, || 0usize);
38        let selected_item = state!(cx, || 0usize);
39
40        // App state
41        let message = state!(cx, || "Use Tab to focus menu bar, then Enter to open".to_string());
42        let counter = state!(cx, || 0i32);
43
44        // Command handler - executes menu commands
45        let handle_command = with!(message, counter, active_menu, selected_item => move |cmd_id: &'static str| {
46            let msg = match cmd_id {
47                "file.new" => "Created new file".to_string(),
48                "file.open" => "Opening file...".to_string(),
49                "file.save" => "File saved".to_string(),
50                "file.quit" => "Use Ctrl+Q to quit".to_string(),
51                "edit.undo" => "Undone".to_string(),
52                "edit.redo" => "Redone".to_string(),
53                "edit.cut" => "Cut to clipboard".to_string(),
54                "edit.copy" => "Copied to clipboard".to_string(),
55                "edit.paste" => "Pasted from clipboard".to_string(),
56                "counter.increment" => {
57                    counter.update(|n| *n += 1);
58                    format!("Counter: {}", counter.get())
59                }
60                "counter.decrement" => {
61                    counter.update(|n| *n -= 1);
62                    format!("Counter: {}", counter.get())
63                }
64                "counter.reset" => {
65                    counter.set(0);
66                    "Counter reset".to_string()
67                }
68                _ => format!("Unknown: {}", cmd_id),
69            };
70            message.set(msg);
71            // Close menu after executing command
72            active_menu.set(None);
73            selected_item.set(0);
74        });
75
76        // Menu change handler - opens/closes menus
77        let on_menu_change = with!(active_menu, highlighted_menu, selected_item => move |idx: usize| {
78            if active_menu.get() == Some(idx) {
79                // Clicking same menu toggles it closed
80                active_menu.set(None);
81            } else {
82                active_menu.set(Some(idx));
83                highlighted_menu.set(idx); // Keep highlight in sync
84                selected_item.set(0);
85            }
86        });
87
88        // Highlight change handler - arrow key navigation when no menu is open
89        let on_highlight_change = with!(highlighted_menu => move |idx: usize| {
90            highlighted_menu.set(idx);
91        });
92
93        // Item change handler - navigates within menu
94        let on_item_change = with!(selected_item => move |idx: usize| {
95            selected_item.set(idx);
96        });
97
98        // Build menus
99        let file_menu = Menu::new("File")
100            .command_with_shortcut("file.new", "New", "Ctrl+N")
101            .command_with_shortcut("file.open", "Open", "Ctrl+O")
102            .command_with_shortcut("file.save", "Save", "Ctrl+S")
103            .separator()
104            .command_with_shortcut("file.quit", "Quit", "Ctrl+Q");
105
106        let edit_menu = Menu::new("Edit")
107            .command_with_shortcut("edit.undo", "Undo", "Ctrl+Z")
108            .command_with_shortcut("edit.redo", "Redo", "Ctrl+Y")
109            .separator()
110            .command_with_shortcut("edit.cut", "Cut", "Ctrl+X")
111            .command_with_shortcut("edit.copy", "Copy", "Ctrl+C")
112            .command_with_shortcut("edit.paste", "Paste", "Ctrl+V");
113
114        let counter_menu = Menu::new("Counter")
115            .command("counter.increment", "Increment")
116            .command("counter.decrement", "Decrement")
117            .separator()
118            .command("counter.reset", "Reset to Zero");
119
120        View::vstack()
121            .child(
122                View::menu_bar()
123                    .menu(file_menu)
124                    .menu(edit_menu)
125                    .menu(counter_menu)
126                    .active_menu(active_menu.get())
127                    .highlighted_menu(highlighted_menu.get())
128                    .selected_item(selected_item.get())
129                    .on_select(handle_command)
130                    .on_menu_change(on_menu_change)
131                    .on_highlight_change(on_highlight_change)
132                    .on_item_change(on_item_change)
133                    .build(),
134            )
135            .child(
136                View::boxed()
137                    .flex(1)
138                    .border(true)
139                    .padding(2)
140                    .child(
141                        View::vstack()
142                            .spacing(1)
143                            .child(View::styled_text("Menu Bar Demo").bold().build())
144                            .child(
145                                View::styled_text(format!("Counter: {}", counter.get()))
146                                    .color(Color::Cyan)
147                                    .bold()
148                                    .build(),
149                            )
150                            .child(
151                                View::styled_text(format!("Status: {}", message.get()))
152                                    .dim()
153                                    .build(),
154                            )
155                            .child(View::spacer())
156                            .child(View::styled_text("Keyboard Navigation:").bold().build())
157                            .child(View::text("  Tab         Focus menu bar"))
158                            .child(View::text("  Enter       Open menu / Execute item"))
159                            .child(View::text("  Up/Down     Navigate menu items"))
160                            .child(View::text("  Left/Right  Switch between menus"))
161                            .child(View::text("  Escape      Close menu"))
162                            .child(View::text("  Ctrl+Q      Quit"))
163                            .child(View::text("  F1          Help"))
164                            .build(),
165                    )
166                    .build(),
167            )
168            .child(
169                View::modal()
170                    .visible(show_help.get())
171                    .title("Example 20: Menu Bar")
172                    .on_dismiss(with!(show_help => move || show_help.set(false)))
173                    .child(
174                        View::vstack()
175                            .child(View::styled_text("What you're seeing").bold().build())
176                            .child(View::text("• Dropdown menu bar with keyboard nav"))
177                            .child(View::text("• Menu items with shortcuts"))
178                            .child(View::text("• Separators in menus"))
179                            .child(View::gap(1))
180                            .child(View::styled_text("Key concepts").bold().build())
181                            .child(View::text("• View::menu_bar() creates menu system"))
182                            .child(View::text("• Menu::new().command() adds items"))
183                            .child(View::text("• .command_with_shortcut() shows key hints"))
184                            .child(View::text("• on_select receives command ID"))
185                            .child(View::gap(1))
186                            .child(View::styled_text("Try this").bold().build())
187                            .child(View::text("• Tab to menu, Enter to open"))
188                            .child(View::text("• Arrow keys navigate menus"))
189                            .child(View::text("• Try the Counter menu"))
190                            .child(View::gap(1))
191                            .child(View::styled_text("Next up").bold().build())
192                            .child(View::text("→ 21_toasts: toast notifications"))
193                            .child(View::gap(1))
194                            .child(View::styled_text("Press Escape to close").dim().build())
195                            .build(),
196                    )
197                    .build(),
198            )
199            .build()
200    }
Source

pub fn on_highlight_change(self, callback: impl Fn(usize) + 'static) -> Self

Set the callback for highlight changes (arrow key navigation).

Examples found in repository?
examples/20_menu_bar.rs (line 131)
26    fn render(&self, cx: Scope) -> View {
27        let show_help = state!(cx, || false);
28
29        // F1 toggles help
30        cx.use_command(
31            KeyBinding::key(KeyCode::F(1)),
32            with!(show_help => move || show_help.update(|v| *v = !*v)),
33        );
34
35        // Menu state
36        let active_menu = state!(cx, || Option::<usize>::None);
37        let highlighted_menu = state!(cx, || 0usize);
38        let selected_item = state!(cx, || 0usize);
39
40        // App state
41        let message = state!(cx, || "Use Tab to focus menu bar, then Enter to open".to_string());
42        let counter = state!(cx, || 0i32);
43
44        // Command handler - executes menu commands
45        let handle_command = with!(message, counter, active_menu, selected_item => move |cmd_id: &'static str| {
46            let msg = match cmd_id {
47                "file.new" => "Created new file".to_string(),
48                "file.open" => "Opening file...".to_string(),
49                "file.save" => "File saved".to_string(),
50                "file.quit" => "Use Ctrl+Q to quit".to_string(),
51                "edit.undo" => "Undone".to_string(),
52                "edit.redo" => "Redone".to_string(),
53                "edit.cut" => "Cut to clipboard".to_string(),
54                "edit.copy" => "Copied to clipboard".to_string(),
55                "edit.paste" => "Pasted from clipboard".to_string(),
56                "counter.increment" => {
57                    counter.update(|n| *n += 1);
58                    format!("Counter: {}", counter.get())
59                }
60                "counter.decrement" => {
61                    counter.update(|n| *n -= 1);
62                    format!("Counter: {}", counter.get())
63                }
64                "counter.reset" => {
65                    counter.set(0);
66                    "Counter reset".to_string()
67                }
68                _ => format!("Unknown: {}", cmd_id),
69            };
70            message.set(msg);
71            // Close menu after executing command
72            active_menu.set(None);
73            selected_item.set(0);
74        });
75
76        // Menu change handler - opens/closes menus
77        let on_menu_change = with!(active_menu, highlighted_menu, selected_item => move |idx: usize| {
78            if active_menu.get() == Some(idx) {
79                // Clicking same menu toggles it closed
80                active_menu.set(None);
81            } else {
82                active_menu.set(Some(idx));
83                highlighted_menu.set(idx); // Keep highlight in sync
84                selected_item.set(0);
85            }
86        });
87
88        // Highlight change handler - arrow key navigation when no menu is open
89        let on_highlight_change = with!(highlighted_menu => move |idx: usize| {
90            highlighted_menu.set(idx);
91        });
92
93        // Item change handler - navigates within menu
94        let on_item_change = with!(selected_item => move |idx: usize| {
95            selected_item.set(idx);
96        });
97
98        // Build menus
99        let file_menu = Menu::new("File")
100            .command_with_shortcut("file.new", "New", "Ctrl+N")
101            .command_with_shortcut("file.open", "Open", "Ctrl+O")
102            .command_with_shortcut("file.save", "Save", "Ctrl+S")
103            .separator()
104            .command_with_shortcut("file.quit", "Quit", "Ctrl+Q");
105
106        let edit_menu = Menu::new("Edit")
107            .command_with_shortcut("edit.undo", "Undo", "Ctrl+Z")
108            .command_with_shortcut("edit.redo", "Redo", "Ctrl+Y")
109            .separator()
110            .command_with_shortcut("edit.cut", "Cut", "Ctrl+X")
111            .command_with_shortcut("edit.copy", "Copy", "Ctrl+C")
112            .command_with_shortcut("edit.paste", "Paste", "Ctrl+V");
113
114        let counter_menu = Menu::new("Counter")
115            .command("counter.increment", "Increment")
116            .command("counter.decrement", "Decrement")
117            .separator()
118            .command("counter.reset", "Reset to Zero");
119
120        View::vstack()
121            .child(
122                View::menu_bar()
123                    .menu(file_menu)
124                    .menu(edit_menu)
125                    .menu(counter_menu)
126                    .active_menu(active_menu.get())
127                    .highlighted_menu(highlighted_menu.get())
128                    .selected_item(selected_item.get())
129                    .on_select(handle_command)
130                    .on_menu_change(on_menu_change)
131                    .on_highlight_change(on_highlight_change)
132                    .on_item_change(on_item_change)
133                    .build(),
134            )
135            .child(
136                View::boxed()
137                    .flex(1)
138                    .border(true)
139                    .padding(2)
140                    .child(
141                        View::vstack()
142                            .spacing(1)
143                            .child(View::styled_text("Menu Bar Demo").bold().build())
144                            .child(
145                                View::styled_text(format!("Counter: {}", counter.get()))
146                                    .color(Color::Cyan)
147                                    .bold()
148                                    .build(),
149                            )
150                            .child(
151                                View::styled_text(format!("Status: {}", message.get()))
152                                    .dim()
153                                    .build(),
154                            )
155                            .child(View::spacer())
156                            .child(View::styled_text("Keyboard Navigation:").bold().build())
157                            .child(View::text("  Tab         Focus menu bar"))
158                            .child(View::text("  Enter       Open menu / Execute item"))
159                            .child(View::text("  Up/Down     Navigate menu items"))
160                            .child(View::text("  Left/Right  Switch between menus"))
161                            .child(View::text("  Escape      Close menu"))
162                            .child(View::text("  Ctrl+Q      Quit"))
163                            .child(View::text("  F1          Help"))
164                            .build(),
165                    )
166                    .build(),
167            )
168            .child(
169                View::modal()
170                    .visible(show_help.get())
171                    .title("Example 20: Menu Bar")
172                    .on_dismiss(with!(show_help => move || show_help.set(false)))
173                    .child(
174                        View::vstack()
175                            .child(View::styled_text("What you're seeing").bold().build())
176                            .child(View::text("• Dropdown menu bar with keyboard nav"))
177                            .child(View::text("• Menu items with shortcuts"))
178                            .child(View::text("• Separators in menus"))
179                            .child(View::gap(1))
180                            .child(View::styled_text("Key concepts").bold().build())
181                            .child(View::text("• View::menu_bar() creates menu system"))
182                            .child(View::text("• Menu::new().command() adds items"))
183                            .child(View::text("• .command_with_shortcut() shows key hints"))
184                            .child(View::text("• on_select receives command ID"))
185                            .child(View::gap(1))
186                            .child(View::styled_text("Try this").bold().build())
187                            .child(View::text("• Tab to menu, Enter to open"))
188                            .child(View::text("• Arrow keys navigate menus"))
189                            .child(View::text("• Try the Counter menu"))
190                            .child(View::gap(1))
191                            .child(View::styled_text("Next up").bold().build())
192                            .child(View::text("→ 21_toasts: toast notifications"))
193                            .child(View::gap(1))
194                            .child(View::styled_text("Press Escape to close").dim().build())
195                            .build(),
196                    )
197                    .build(),
198            )
199            .build()
200    }
Source

pub fn on_item_change(self, callback: impl Fn(usize) + 'static) -> Self

Set the callback for item selection changes within a menu.

Examples found in repository?
examples/20_menu_bar.rs (line 132)
26    fn render(&self, cx: Scope) -> View {
27        let show_help = state!(cx, || false);
28
29        // F1 toggles help
30        cx.use_command(
31            KeyBinding::key(KeyCode::F(1)),
32            with!(show_help => move || show_help.update(|v| *v = !*v)),
33        );
34
35        // Menu state
36        let active_menu = state!(cx, || Option::<usize>::None);
37        let highlighted_menu = state!(cx, || 0usize);
38        let selected_item = state!(cx, || 0usize);
39
40        // App state
41        let message = state!(cx, || "Use Tab to focus menu bar, then Enter to open".to_string());
42        let counter = state!(cx, || 0i32);
43
44        // Command handler - executes menu commands
45        let handle_command = with!(message, counter, active_menu, selected_item => move |cmd_id: &'static str| {
46            let msg = match cmd_id {
47                "file.new" => "Created new file".to_string(),
48                "file.open" => "Opening file...".to_string(),
49                "file.save" => "File saved".to_string(),
50                "file.quit" => "Use Ctrl+Q to quit".to_string(),
51                "edit.undo" => "Undone".to_string(),
52                "edit.redo" => "Redone".to_string(),
53                "edit.cut" => "Cut to clipboard".to_string(),
54                "edit.copy" => "Copied to clipboard".to_string(),
55                "edit.paste" => "Pasted from clipboard".to_string(),
56                "counter.increment" => {
57                    counter.update(|n| *n += 1);
58                    format!("Counter: {}", counter.get())
59                }
60                "counter.decrement" => {
61                    counter.update(|n| *n -= 1);
62                    format!("Counter: {}", counter.get())
63                }
64                "counter.reset" => {
65                    counter.set(0);
66                    "Counter reset".to_string()
67                }
68                _ => format!("Unknown: {}", cmd_id),
69            };
70            message.set(msg);
71            // Close menu after executing command
72            active_menu.set(None);
73            selected_item.set(0);
74        });
75
76        // Menu change handler - opens/closes menus
77        let on_menu_change = with!(active_menu, highlighted_menu, selected_item => move |idx: usize| {
78            if active_menu.get() == Some(idx) {
79                // Clicking same menu toggles it closed
80                active_menu.set(None);
81            } else {
82                active_menu.set(Some(idx));
83                highlighted_menu.set(idx); // Keep highlight in sync
84                selected_item.set(0);
85            }
86        });
87
88        // Highlight change handler - arrow key navigation when no menu is open
89        let on_highlight_change = with!(highlighted_menu => move |idx: usize| {
90            highlighted_menu.set(idx);
91        });
92
93        // Item change handler - navigates within menu
94        let on_item_change = with!(selected_item => move |idx: usize| {
95            selected_item.set(idx);
96        });
97
98        // Build menus
99        let file_menu = Menu::new("File")
100            .command_with_shortcut("file.new", "New", "Ctrl+N")
101            .command_with_shortcut("file.open", "Open", "Ctrl+O")
102            .command_with_shortcut("file.save", "Save", "Ctrl+S")
103            .separator()
104            .command_with_shortcut("file.quit", "Quit", "Ctrl+Q");
105
106        let edit_menu = Menu::new("Edit")
107            .command_with_shortcut("edit.undo", "Undo", "Ctrl+Z")
108            .command_with_shortcut("edit.redo", "Redo", "Ctrl+Y")
109            .separator()
110            .command_with_shortcut("edit.cut", "Cut", "Ctrl+X")
111            .command_with_shortcut("edit.copy", "Copy", "Ctrl+C")
112            .command_with_shortcut("edit.paste", "Paste", "Ctrl+V");
113
114        let counter_menu = Menu::new("Counter")
115            .command("counter.increment", "Increment")
116            .command("counter.decrement", "Decrement")
117            .separator()
118            .command("counter.reset", "Reset to Zero");
119
120        View::vstack()
121            .child(
122                View::menu_bar()
123                    .menu(file_menu)
124                    .menu(edit_menu)
125                    .menu(counter_menu)
126                    .active_menu(active_menu.get())
127                    .highlighted_menu(highlighted_menu.get())
128                    .selected_item(selected_item.get())
129                    .on_select(handle_command)
130                    .on_menu_change(on_menu_change)
131                    .on_highlight_change(on_highlight_change)
132                    .on_item_change(on_item_change)
133                    .build(),
134            )
135            .child(
136                View::boxed()
137                    .flex(1)
138                    .border(true)
139                    .padding(2)
140                    .child(
141                        View::vstack()
142                            .spacing(1)
143                            .child(View::styled_text("Menu Bar Demo").bold().build())
144                            .child(
145                                View::styled_text(format!("Counter: {}", counter.get()))
146                                    .color(Color::Cyan)
147                                    .bold()
148                                    .build(),
149                            )
150                            .child(
151                                View::styled_text(format!("Status: {}", message.get()))
152                                    .dim()
153                                    .build(),
154                            )
155                            .child(View::spacer())
156                            .child(View::styled_text("Keyboard Navigation:").bold().build())
157                            .child(View::text("  Tab         Focus menu bar"))
158                            .child(View::text("  Enter       Open menu / Execute item"))
159                            .child(View::text("  Up/Down     Navigate menu items"))
160                            .child(View::text("  Left/Right  Switch between menus"))
161                            .child(View::text("  Escape      Close menu"))
162                            .child(View::text("  Ctrl+Q      Quit"))
163                            .child(View::text("  F1          Help"))
164                            .build(),
165                    )
166                    .build(),
167            )
168            .child(
169                View::modal()
170                    .visible(show_help.get())
171                    .title("Example 20: Menu Bar")
172                    .on_dismiss(with!(show_help => move || show_help.set(false)))
173                    .child(
174                        View::vstack()
175                            .child(View::styled_text("What you're seeing").bold().build())
176                            .child(View::text("• Dropdown menu bar with keyboard nav"))
177                            .child(View::text("• Menu items with shortcuts"))
178                            .child(View::text("• Separators in menus"))
179                            .child(View::gap(1))
180                            .child(View::styled_text("Key concepts").bold().build())
181                            .child(View::text("• View::menu_bar() creates menu system"))
182                            .child(View::text("• Menu::new().command() adds items"))
183                            .child(View::text("• .command_with_shortcut() shows key hints"))
184                            .child(View::text("• on_select receives command ID"))
185                            .child(View::gap(1))
186                            .child(View::styled_text("Try this").bold().build())
187                            .child(View::text("• Tab to menu, Enter to open"))
188                            .child(View::text("• Arrow keys navigate menus"))
189                            .child(View::text("• Try the Counter menu"))
190                            .child(View::gap(1))
191                            .child(View::styled_text("Next up").bold().build())
192                            .child(View::text("→ 21_toasts: toast notifications"))
193                            .child(View::gap(1))
194                            .child(View::styled_text("Press Escape to close").dim().build())
195                            .build(),
196                    )
197                    .build(),
198            )
199            .build()
200    }
Source

pub fn build(self) -> View

Examples found in repository?
examples/20_menu_bar.rs (line 133)
26    fn render(&self, cx: Scope) -> View {
27        let show_help = state!(cx, || false);
28
29        // F1 toggles help
30        cx.use_command(
31            KeyBinding::key(KeyCode::F(1)),
32            with!(show_help => move || show_help.update(|v| *v = !*v)),
33        );
34
35        // Menu state
36        let active_menu = state!(cx, || Option::<usize>::None);
37        let highlighted_menu = state!(cx, || 0usize);
38        let selected_item = state!(cx, || 0usize);
39
40        // App state
41        let message = state!(cx, || "Use Tab to focus menu bar, then Enter to open".to_string());
42        let counter = state!(cx, || 0i32);
43
44        // Command handler - executes menu commands
45        let handle_command = with!(message, counter, active_menu, selected_item => move |cmd_id: &'static str| {
46            let msg = match cmd_id {
47                "file.new" => "Created new file".to_string(),
48                "file.open" => "Opening file...".to_string(),
49                "file.save" => "File saved".to_string(),
50                "file.quit" => "Use Ctrl+Q to quit".to_string(),
51                "edit.undo" => "Undone".to_string(),
52                "edit.redo" => "Redone".to_string(),
53                "edit.cut" => "Cut to clipboard".to_string(),
54                "edit.copy" => "Copied to clipboard".to_string(),
55                "edit.paste" => "Pasted from clipboard".to_string(),
56                "counter.increment" => {
57                    counter.update(|n| *n += 1);
58                    format!("Counter: {}", counter.get())
59                }
60                "counter.decrement" => {
61                    counter.update(|n| *n -= 1);
62                    format!("Counter: {}", counter.get())
63                }
64                "counter.reset" => {
65                    counter.set(0);
66                    "Counter reset".to_string()
67                }
68                _ => format!("Unknown: {}", cmd_id),
69            };
70            message.set(msg);
71            // Close menu after executing command
72            active_menu.set(None);
73            selected_item.set(0);
74        });
75
76        // Menu change handler - opens/closes menus
77        let on_menu_change = with!(active_menu, highlighted_menu, selected_item => move |idx: usize| {
78            if active_menu.get() == Some(idx) {
79                // Clicking same menu toggles it closed
80                active_menu.set(None);
81            } else {
82                active_menu.set(Some(idx));
83                highlighted_menu.set(idx); // Keep highlight in sync
84                selected_item.set(0);
85            }
86        });
87
88        // Highlight change handler - arrow key navigation when no menu is open
89        let on_highlight_change = with!(highlighted_menu => move |idx: usize| {
90            highlighted_menu.set(idx);
91        });
92
93        // Item change handler - navigates within menu
94        let on_item_change = with!(selected_item => move |idx: usize| {
95            selected_item.set(idx);
96        });
97
98        // Build menus
99        let file_menu = Menu::new("File")
100            .command_with_shortcut("file.new", "New", "Ctrl+N")
101            .command_with_shortcut("file.open", "Open", "Ctrl+O")
102            .command_with_shortcut("file.save", "Save", "Ctrl+S")
103            .separator()
104            .command_with_shortcut("file.quit", "Quit", "Ctrl+Q");
105
106        let edit_menu = Menu::new("Edit")
107            .command_with_shortcut("edit.undo", "Undo", "Ctrl+Z")
108            .command_with_shortcut("edit.redo", "Redo", "Ctrl+Y")
109            .separator()
110            .command_with_shortcut("edit.cut", "Cut", "Ctrl+X")
111            .command_with_shortcut("edit.copy", "Copy", "Ctrl+C")
112            .command_with_shortcut("edit.paste", "Paste", "Ctrl+V");
113
114        let counter_menu = Menu::new("Counter")
115            .command("counter.increment", "Increment")
116            .command("counter.decrement", "Decrement")
117            .separator()
118            .command("counter.reset", "Reset to Zero");
119
120        View::vstack()
121            .child(
122                View::menu_bar()
123                    .menu(file_menu)
124                    .menu(edit_menu)
125                    .menu(counter_menu)
126                    .active_menu(active_menu.get())
127                    .highlighted_menu(highlighted_menu.get())
128                    .selected_item(selected_item.get())
129                    .on_select(handle_command)
130                    .on_menu_change(on_menu_change)
131                    .on_highlight_change(on_highlight_change)
132                    .on_item_change(on_item_change)
133                    .build(),
134            )
135            .child(
136                View::boxed()
137                    .flex(1)
138                    .border(true)
139                    .padding(2)
140                    .child(
141                        View::vstack()
142                            .spacing(1)
143                            .child(View::styled_text("Menu Bar Demo").bold().build())
144                            .child(
145                                View::styled_text(format!("Counter: {}", counter.get()))
146                                    .color(Color::Cyan)
147                                    .bold()
148                                    .build(),
149                            )
150                            .child(
151                                View::styled_text(format!("Status: {}", message.get()))
152                                    .dim()
153                                    .build(),
154                            )
155                            .child(View::spacer())
156                            .child(View::styled_text("Keyboard Navigation:").bold().build())
157                            .child(View::text("  Tab         Focus menu bar"))
158                            .child(View::text("  Enter       Open menu / Execute item"))
159                            .child(View::text("  Up/Down     Navigate menu items"))
160                            .child(View::text("  Left/Right  Switch between menus"))
161                            .child(View::text("  Escape      Close menu"))
162                            .child(View::text("  Ctrl+Q      Quit"))
163                            .child(View::text("  F1          Help"))
164                            .build(),
165                    )
166                    .build(),
167            )
168            .child(
169                View::modal()
170                    .visible(show_help.get())
171                    .title("Example 20: Menu Bar")
172                    .on_dismiss(with!(show_help => move || show_help.set(false)))
173                    .child(
174                        View::vstack()
175                            .child(View::styled_text("What you're seeing").bold().build())
176                            .child(View::text("• Dropdown menu bar with keyboard nav"))
177                            .child(View::text("• Menu items with shortcuts"))
178                            .child(View::text("• Separators in menus"))
179                            .child(View::gap(1))
180                            .child(View::styled_text("Key concepts").bold().build())
181                            .child(View::text("• View::menu_bar() creates menu system"))
182                            .child(View::text("• Menu::new().command() adds items"))
183                            .child(View::text("• .command_with_shortcut() shows key hints"))
184                            .child(View::text("• on_select receives command ID"))
185                            .child(View::gap(1))
186                            .child(View::styled_text("Try this").bold().build())
187                            .child(View::text("• Tab to menu, Enter to open"))
188                            .child(View::text("• Arrow keys navigate menus"))
189                            .child(View::text("• Try the Counter menu"))
190                            .child(View::gap(1))
191                            .child(View::styled_text("Next up").bold().build())
192                            .child(View::text("→ 21_toasts: toast notifications"))
193                            .child(View::gap(1))
194                            .child(View::styled_text("Press Escape to close").dim().build())
195                            .build(),
196                    )
197                    .build(),
198            )
199            .build()
200    }

Trait Implementations§

Source§

impl Default for MenuBarBuilder

Source§

fn default() -> MenuBarBuilder

Returns the “default value” for a type. Read more

Auto Trait Implementations§

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> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
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 = Infallible

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.