Modals
You can use the Modals
server to pop up the following objects:
- Notifications
- Static: shows a message plus an "Okay" button
- Dynamic: can sequence through multiple messages
- Checkboxes (multi-select list)
- Radioboxes (single-select list)
- Text Entry with validator
- Progress bars
To use Modals
in your code, you will need to add modals
to your Cargo.toml
file. From an application in the apps
directory:
modals = {path = "../../services/modals"}
In all of the examples, you will need this pre-amble to create the modals
object. The object can be re-used as many times as you like.
// connect to the modals object through the name resolver
let xns = XousNames::new().unwrap();
let modals = modals::Modals::new(&xns).unwrap();
Static Notification
modals.show_notification("This is a test!").expect("notification failed");
This will pop up a notification that says "This is a test!". Execution blocks at this line until the user pressses any key to acknowledge the notification.
Progress bar
One can create a progress bar using the start_progress()
method, with the following parameters:
name
: A&str
that is the title of the progress barstart
: Au32
that is the starting ordinalend
: Au32
that is the ending ordinalcurrent
: Au32
that is the initial point of the progress bar
start
should be less than end
, and current
should be between start
and end
, inclusive.
Once the bar is created, you can update its progress using the update_progress()
method. It takes a number that represents the current progress between the start and end ordinal.
The progress bar is closed by calling the finish_progress()
method.
// the ticktimer is used just to introduce a delay in this example. Normally, you'd do something computationally useful instead of just waiting.
let tt = ticktimer_server::Ticktimer::new().unwrap();
let start = 1;
let end = 20;
modals.start_progress("Progress Quest", start, end, start).expect("couldn't raise progress bar");
for i in (start..end).step_by(2) {
modals.update_progress(i).expect("couldn't update progress bar");
tt.sleep_ms(100).unwrap();
}
modals.finish_progress().expect("couldn't dismiss progress bar");
Dynamic Notifications
Dynamic notifications are notifications which don't have an option for the user to close them; instead, the calling program controls when the dialog can be closed, and can also dynamically update the message. This is useful for displaying, for example, multi-phase progress updates without stopping and waiting for a user to hit "OK".
The API is similar to that of the Progress Bar, in that there are start, update, and close phases:
- To pop up the dynamic notification, use the
dynamic_notification(title: Option<&str>, text: Option<&str>)
method. The bothtitle
andtext
are optional, but at least one is recommended, otherwise you get an empty notification. - Updates to the notification are done using
dynamic_notification_update(title: Option<&str>, text: Option<&str>)
. Arguments that areNone
do not update, and show the same text as before. - Once you are finished showing the set of notifications, you must close the
dialog with
dynamic_notification_close()
.
Text entry
One can request text entry using the get_text()
method. This takes the following parameters:
prompt
: A&str
that is the prompt to the uservalidator
: AnOption<fn(TextEntryPayload, u32) -> Option<ValidatorErr>>
. This is an optional function that takes the text entry payload, along with a dispatch opcode. The dispatch opcode allows a single validator function to be re-used across multiple invocations ofget_text()
.validator_op
: AnOption<u32>
. WhenSome()
, the argument inside is passed to the validator to indicate which type of text is being validated.
The idea behind the validator_op
is that you could create an Enum
type that specifies the type of text you're entering, and you would pass the u32
version of that Enum
to the get_text()
call so that a single validator
function can be used to check multiple types of text entry.
// you can also use the num_derive crate to have bi-directional transformation of the enum
enum ValidatorOp {
Int2 = 0,
Int = 1,
}
//
fn my_code() {
// ... insert code to create modals object, etc.
match modals.get_text("Input an integer greater than 2", Some(test_validator), Some(ValidatorOp::Int2 as u32)) {
Ok(text) => {
log::info!("Input: {}", text.0);
}
_ => {
log::error!("get_text failed");
}
}
match modals.get_text("Input any integer", Some(test_validator), Some(ValidatorOp::Int2 as u32)) {
Ok(text) => {
log::info!("Input: {}", text.0);
}
_ => {
log::error!("get_text failed");
}
}
}
fn test_validator(input: TextEntryPayload, opcode: u32) -> Option<xous_ipc::String::<256>> {
let text_str = input.as_str();
match text_str.parse::<u32>() {
Ok(input_int) =>
if opcode == ValidatorOp::Int2 as u32 {
if input_int <= 2 {
return Some(xous_ipc::String::<256>::from_str("input must be larger than 2"))
} else {
return None
}
} else if opcode == ValidatorOp::Int as u32 {
return None
} else {
panic!("unknown discriminant");
}
_ => return Some(xous_ipc::String::<256>::from_str("enter an integer value"))
}
}
Radio Box
A radio box is a mechanism to force a user to pick exactly one item from a list of options.
One can construct a radio box by first repeatedly calling add_list_item()
with a &str
description of the items to select, and then calling get_radiobutton()
with a &str
of
the prompt. The returned value will be the &str
description of the selected item.
Note that upon completion of the radio box, the list of items is automatically cleared
in preparation for another invocation of modals
.
const RADIO_TEST: [&'static str; 4] = [
"zebra",
"cow",
"horse",
"cat",
];
for item in RADIO_TEST {
modals.add_list_item(item).expect("couldn't build radio item list");
}
match modals.get_radiobutton("Pick an animal") {
Ok(animal) => log::info!("{} was picked", animal),
_ => log::error!("get_radiobutton failed"),
}
Check Box
A check box is a mechanism to present a user with a list of several options, of which they can select none, some, or all of them.
The usage is nearly identical to the Radio Box above, except that the return value
is a Vec::<String>
. The Vec
will be empty if no elements are selected.
const CHECKBOX_TEST: [&'static str; 5] = [
"happy",
"😃",
"安",
"peaceful",
"...something else!",
];
for item in CHECKBOX_TEST {
modals.add_list_item(item).expect("couldn't build checkbox list");
}
match modals.get_checkbox("You can have it all:") {
Ok(things) => {
log::info!("The user picked {} things:", things.len());
for thing in things {
log::info!("{}", thing);
}
},
_ => log::error!("get_checkbox failed"),
}