pub struct MenuBarBuilder { /* private fields */ }Expand description
Builder for MenuBar views.
Implementations§
Source§impl MenuBarBuilder
impl MenuBarBuilder
pub fn new() -> 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 }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 }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 }Sourcepub fn selected_item(self, index: usize) -> Self
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 }Sourcepub fn on_select(self, callback: impl Fn(&'static str) + 'static) -> Self
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 }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 }Sourcepub fn on_highlight_change(self, callback: impl Fn(usize) + 'static) -> Self
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 }Sourcepub fn on_item_change(self, callback: impl Fn(usize) + 'static) -> Self
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 }Sourcepub fn build(self) -> View
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
impl Default for MenuBarBuilder
Source§fn default() -> MenuBarBuilder
fn default() -> MenuBarBuilder
Returns the “default value” for a type. Read more
Auto Trait Implementations§
impl Freeze for MenuBarBuilder
impl !RefUnwindSafe for MenuBarBuilder
impl !Send for MenuBarBuilder
impl !Sync for MenuBarBuilder
impl Unpin for MenuBarBuilder
impl !UnwindSafe for MenuBarBuilder
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
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>
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)
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)
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.