pub struct Menu { /* private fields */ }Expand description
A vertical list of items navigated with the arrow keys.
| Key | Action |
|---|---|
| Up / Down | previous / next item |
| Home / End | first / last item |
| Enter / Space | press button, toggle checkbox, next choice, edit text |
| Left / Right | adjust slider or choice, toggle checkbox |
| Page Up / Page Down | adjust slider by ten steps |
| Letters | jump to the next item starting with that letter |
| Escape | close (if Menu::closable) |
let menu = Menu::new("options", "Options")
.slider("volume", "Volume", 80.0, 0.0, 100.0, 5.0)
.choice("difficulty", "Difficulty", &["Easy", "Normal", "Hard"], 1)
.checkbox("subtitles", "Speak hints", true)
.text_field("name", "Player name", "Hero")
.button("back", "Back")
.music("menu_theme");Implementations§
Source§impl Menu
impl Menu
Sourcepub fn new(id: impl Into<String>, title: impl Into<String>) -> Self
pub fn new(id: impl Into<String>, title: impl Into<String>) -> Self
Creates an empty menu. title is spoken when it opens (may be empty).
Examples found in repository?
More examples
17fn main_menu() -> Menu {
18 Menu::new("main", "Main menu")
19 .button("play", "Play")
20 .button("options", "Options")
21 .button("name", "Change name")
22 .button("quit", "Quit")
23 .closable(false)
24 .wrap(true)
25}
26
27fn options_menu(s: &Settings) -> Menu {
28 Menu::new("options", "Options")
29 .slider("volume", "Volume", s.volume * 100.0, 0.0, 100.0, 10.0)
30 .choice("difficulty", "Difficulty", &["Easy", "Normal", "Hard"], s.difficulty)
31 .text_field("name", "Player name", s.name.clone())
32 .button("back", "Back")
33}49fn title_menu(ctx: &Context) -> Menu {
50 let voice = ctx.speech.voice().map_or("default".to_string(), |v| v.name.clone());
51 let best: u32 = ctx.storage.get_or("best", 0);
52 Menu::new("title", "Bell Catcher")
53 .button("play", "Play")
54 .button("voice", format!("Voice: {voice}"))
55 .slider("rate", "Speech rate", ctx.storage.get_or("rate", 50.0), 0.0, 100.0, 5.0)
56 .button("help", "How to play")
57 .label(format!("Best score: {best}"))
58 .button("quit", "Quit")
59 .closable(false)
60 .wrap(true)
61}
62
63/// One button per voice, with the current one focused. `None` if the output has no voices.
64fn voice_menu(ctx: &Context) -> Option<Menu> {
65 let voices = ctx.speech.voices();
66 if voices.is_empty() {
67 return None;
68 }
69 let mut menu = Menu::new("voices", "Choose a voice").button("default", "Default voice");
70 for (i, voice) in voices.iter().enumerate() {
71 menu = menu.button(format!("voice{i}"), voice.to_string());
72 }
73 if let Some(i) = ctx.speech.voice().and_then(|current| voices.iter().position(|v| v == current)) {
74 menu = menu.focus(&format!("voice{i}"));
75 }
76 Some(menu.button("back", "Back"))
77}Adds a button.
Examples found in repository?
More examples
17fn main_menu() -> Menu {
18 Menu::new("main", "Main menu")
19 .button("play", "Play")
20 .button("options", "Options")
21 .button("name", "Change name")
22 .button("quit", "Quit")
23 .closable(false)
24 .wrap(true)
25}
26
27fn options_menu(s: &Settings) -> Menu {
28 Menu::new("options", "Options")
29 .slider("volume", "Volume", s.volume * 100.0, 0.0, 100.0, 10.0)
30 .choice("difficulty", "Difficulty", &["Easy", "Normal", "Hard"], s.difficulty)
31 .text_field("name", "Player name", s.name.clone())
32 .button("back", "Back")
33}49fn title_menu(ctx: &Context) -> Menu {
50 let voice = ctx.speech.voice().map_or("default".to_string(), |v| v.name.clone());
51 let best: u32 = ctx.storage.get_or("best", 0);
52 Menu::new("title", "Bell Catcher")
53 .button("play", "Play")
54 .button("voice", format!("Voice: {voice}"))
55 .slider("rate", "Speech rate", ctx.storage.get_or("rate", 50.0), 0.0, 100.0, 5.0)
56 .button("help", "How to play")
57 .label(format!("Best score: {best}"))
58 .button("quit", "Quit")
59 .closable(false)
60 .wrap(true)
61}
62
63/// One button per voice, with the current one focused. `None` if the output has no voices.
64fn voice_menu(ctx: &Context) -> Option<Menu> {
65 let voices = ctx.speech.voices();
66 if voices.is_empty() {
67 return None;
68 }
69 let mut menu = Menu::new("voices", "Choose a voice").button("default", "Default voice");
70 for (i, voice) in voices.iter().enumerate() {
71 menu = menu.button(format!("voice{i}"), voice.to_string());
72 }
73 if let Some(i) = ctx.speech.voice().and_then(|current| voices.iter().position(|v| v == current)) {
74 menu = menu.focus(&format!("voice{i}"));
75 }
76 Some(menu.button("back", "Back"))
77}Sourcepub fn checkbox(
self,
id: impl Into<String>,
label: impl Into<String>,
checked: bool,
) -> Self
pub fn checkbox( self, id: impl Into<String>, label: impl Into<String>, checked: bool, ) -> Self
Adds a checkbox.
Sourcepub fn slider(
self,
id: impl Into<String>,
label: impl Into<String>,
value: f32,
min: f32,
max: f32,
step: f32,
) -> Self
pub fn slider( self, id: impl Into<String>, label: impl Into<String>, value: f32, min: f32, max: f32, step: f32, ) -> Self
Adds a slider. value is the start value, min and max the range, and step
how much Left and Right change it (Page Up and Page Down change it by ten steps).
slider("volume", "Volume", 80.0, 0.0, 100.0, 5.0) starts at 80 and goes from 0 to
100 in steps of 5. Read the value with Value::as_f32.
Examples found in repository?
More examples
49fn title_menu(ctx: &Context) -> Menu {
50 let voice = ctx.speech.voice().map_or("default".to_string(), |v| v.name.clone());
51 let best: u32 = ctx.storage.get_or("best", 0);
52 Menu::new("title", "Bell Catcher")
53 .button("play", "Play")
54 .button("voice", format!("Voice: {voice}"))
55 .slider("rate", "Speech rate", ctx.storage.get_or("rate", 50.0), 0.0, 100.0, 5.0)
56 .button("help", "How to play")
57 .label(format!("Best score: {best}"))
58 .button("quit", "Quit")
59 .closable(false)
60 .wrap(true)
61}Sourcepub fn choice(
self,
id: impl Into<String>,
label: impl Into<String>,
options: &[&str],
selected: usize,
) -> Self
pub fn choice( self, id: impl Into<String>, label: impl Into<String>, options: &[&str], selected: usize, ) -> Self
Adds a choice between several options. selected is the index of the option chosen
at the start (0 = the first one).
Sourcepub fn text_field(
self,
id: impl Into<String>,
label: impl Into<String>,
value: impl Into<String>,
) -> Self
pub fn text_field( self, id: impl Into<String>, label: impl Into<String>, value: impl Into<String>, ) -> Self
Adds an editable text field.
Sourcepub fn password_field(
self,
id: impl Into<String>,
label: impl Into<String>,
) -> Self
pub fn password_field( self, id: impl Into<String>, label: impl Into<String>, ) -> Self
Adds a password field (content is never spoken).
Sourcepub fn label(self, text: impl Into<String>) -> Self
pub fn label(self, text: impl Into<String>) -> Self
Adds a line of information that cannot be activated.
Examples found in repository?
49fn title_menu(ctx: &Context) -> Menu {
50 let voice = ctx.speech.voice().map_or("default".to_string(), |v| v.name.clone());
51 let best: u32 = ctx.storage.get_or("best", 0);
52 Menu::new("title", "Bell Catcher")
53 .button("play", "Play")
54 .button("voice", format!("Voice: {voice}"))
55 .slider("rate", "Speech rate", ctx.storage.get_or("rate", 50.0), 0.0, 100.0, 5.0)
56 .button("help", "How to play")
57 .label(format!("Best score: {best}"))
58 .button("quit", "Quit")
59 .closable(false)
60 .wrap(true)
61}Sourcepub fn wrap(self, wrap: bool) -> Self
pub fn wrap(self, wrap: bool) -> Self
Jump from the last to the first item and back (default false).
Examples found in repository?
More examples
49fn title_menu(ctx: &Context) -> Menu {
50 let voice = ctx.speech.voice().map_or("default".to_string(), |v| v.name.clone());
51 let best: u32 = ctx.storage.get_or("best", 0);
52 Menu::new("title", "Bell Catcher")
53 .button("play", "Play")
54 .button("voice", format!("Voice: {voice}"))
55 .slider("rate", "Speech rate", ctx.storage.get_or("rate", 50.0), 0.0, 100.0, 5.0)
56 .button("help", "How to play")
57 .label(format!("Best score: {best}"))
58 .button("quit", "Quit")
59 .closable(false)
60 .wrap(true)
61}Sourcepub fn closable(self, closable: bool) -> Self
pub fn closable(self, closable: bool) -> Self
Allow closing with Escape (default true). A main menu often sets this to false.
Examples found in repository?
More examples
49fn title_menu(ctx: &Context) -> Menu {
50 let voice = ctx.speech.voice().map_or("default".to_string(), |v| v.name.clone());
51 let best: u32 = ctx.storage.get_or("best", 0);
52 Menu::new("title", "Bell Catcher")
53 .button("play", "Play")
54 .button("voice", format!("Voice: {voice}"))
55 .slider("rate", "Speech rate", ctx.storage.get_or("rate", 50.0), 0.0, 100.0, 5.0)
56 .button("help", "How to play")
57 .label(format!("Best score: {best}"))
58 .button("quit", "Quit")
59 .closable(false)
60 .wrap(true)
61}Sourcepub fn positions(self, positions: bool) -> Self
pub fn positions(self, positions: bool) -> Self
Speak “2 of 5” after each item (default false).
Sourcepub fn close_on_activate(self, close: bool) -> Self
pub fn close_on_activate(self, close: bool) -> Self
Close the menu automatically after a button was pressed (default false).
Sourcepub fn sounds(self, sounds: UiSounds) -> Self
pub fn sounds(self, sounds: UiSounds) -> Self
Use these sounds instead of the global ones for this menu.
Sourcepub fn sound(self, kind: UiSound, name: Option<&str>) -> Self
pub fn sound(self, kind: UiSound, name: Option<&str>) -> Self
Changes one sound for this menu only, e.g. a special click for the main menu.
None makes it silent. All other sounds stay as they are.
Sourcepub fn music(self, name: impl Into<String>) -> Self
pub fn music(self, name: impl Into<String>) -> Self
Plays the music name in a loop while this menu is open (a file or registered sound).
Submenus and dialogs on top keep it playing unless they have music of their own.
When the menu closes, the music that played before comes back, or the music fades out.
Sourcepub fn focus(self, id: &str) -> Self
pub fn focus(self, id: &str) -> Self
Start with the item id focused.
Examples found in repository?
64fn voice_menu(ctx: &Context) -> Option<Menu> {
65 let voices = ctx.speech.voices();
66 if voices.is_empty() {
67 return None;
68 }
69 let mut menu = Menu::new("voices", "Choose a voice").button("default", "Default voice");
70 for (i, voice) in voices.iter().enumerate() {
71 menu = menu.button(format!("voice{i}"), voice.to_string());
72 }
73 if let Some(i) = ctx.speech.voice().and_then(|current| voices.iter().position(|v| v == current)) {
74 menu = menu.focus(&format!("voice{i}"));
75 }
76 Some(menu.button("back", "Back"))
77}
78
79/// Reopens the title menu so its labels show the new values, focused on `item`.
80fn refresh_title(ctx: &mut Context, item: &str) {
81 ctx.ui.close_id("title");
82 let menu = title_menu(ctx).focus(item);
83 ctx.ui.open(menu);
84}Trait Implementations§
Source§impl Widget for Menu
impl Widget for Menu
Source§fn open(&mut self, ui: &mut UiContext<'_>)
fn open(&mut self, ui: &mut UiContext<'_>)
Source§fn resume(&mut self, value: Option<Value>, ui: &mut UiContext<'_>)
fn resume(&mut self, value: Option<Value>, ui: &mut UiContext<'_>)
value is what the closed widget passed with Status::CloseWith.Auto Trait Implementations§
impl Freeze for Menu
impl RefUnwindSafe for Menu
impl Send for Menu
impl Sync for Menu
impl Unpin for Menu
impl UnsafeUnpin for Menu
impl UnwindSafe for Menu
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
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
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>
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>
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)
&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)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.