cursive/doc/tutorial_2.md
2016-08-20 01:22:58 -07:00

5.8 KiB

Starting with cursive: Intermediate

Target goal

This time, we'll work on a slightly bigger example, where the user will need to actually make some choices. Here is what the code will look like:

extern crate cursive;

use cursive::Cursive;
use cursive::views::Dialog;

fn main() {
    let mut siv = Cursive::new();

    siv.add_layer(Dialog::text("This is a survey!\nPress <Next> when you're ready.")
        .title("Important survey")
        .button("Next", show_next));

    siv.run();
}

fn show_next(s: &mut Cursive) {
    s.pop_layer();
    s.add_layer(Dialog::text("Did you do the thing?")
        .title("Question 1")
        .button("Yes!", |s| show_answer(s, "I knew it! Well done!"))
        .button("No!", |s| show_answer(s, "I knew you couldn't be trusted!"))
        .button("Uh?", |s| s.add_layer(Dialog::info("Try again!"))));
}

fn show_answer(s: &mut Cursive, msg: &str) {
    s.pop_layer();
    s.add_layer(Dialog::text(msg)
        .title("Results")
        .button("Finish", |s| s.quit()));
}
Tutorial 2 goal

Preparation

This time you're not a beginner anymore, so we'll skip the introduction! Let's start from a basic cursive application:

extern crate cursive

use cursive::Cursive;

fn main() {
    let mut siv = Cursive::new();

    siv.run();
}

Dialogs

Instead of directly using a [TextView], we'll use a [Dialog] this time. A dialog is wrapper with a frame around another view, and optionally a title and/or buttons.

[Dialog::new] directly takes a view, so we'll directly give it the TextView:

extern crate cursive;

use cursive::Cursive;
use cursive::views::Dialog;
use cursive::views::TextView;

fn main() {
    let mut siv = Cursive::new();

    siv.add_layer(Dialog::new(TextView::new("...")));

    siv.run();
}

Since creating a Dialog around a TextView is a pretty common task, [Dialog::text] is a shortcut function that directly does that. Our line becomes a little shorter (and we don't need to import cursive::views::TextView` anymore):

siv.add_layer(Dialog::text("..."));

Next, let's add a title. To do that, Dialog has a chainable Dialog::title method. It takes the dialog by value, and return it back, making function chains easy:

siv.add_layer(Dialog::text("...").title("..."));

This way of chaining method to set-up the view is very common in cursive. Most views provide chainable variants of their methods, to allow creating the view and configuring it in one spot.

Buttons

Our dialog looks better than a TextView alone, but it's still missing some action. Adding some buttons there will help.

Just like with the title, Dialog has a [Dialog::button] method to add a button in a chain. This method takes a label and a callback, the same kind we saw in the previous tutorial:

siv.add_layer(Dialog::text("...")
    .title("...")
    .button("Quit", |s| s.quit()));

Only this time, we don't want to exit the application right away. Instead of packing everything into the closure, let's use a separate function for the callback. Here is the current state:

extern crate cursive;

use cursive::Cursive;
use cursive::views::Dialog;

fn main() {
    let mut siv = Cursive::new();

    siv.add_layer(Dialog::text("This is a survey!\nPress <Next> when you're ready.")
        .title("Important survey")
        .button("Next", show_next));

    siv.run();
}

fn show_next(s: &mut Cursive) {
    // Empty for now
}

Layers

After the user presses the <Next> button, we want to hide the current popup and show a new one instead. We'll use Cursive::pop_layer to do that.

Then, we add a new Dialog, this time with a few more buttons:

fn show_next(s: &mut Cursive) {
    s.pop_layer();
    s.add_layer(Dialog::text("Did you do the thing?")
        .title("Question 1")
        .button("Yes!", |s| ()) //< Do something interesting here...
        .button("No!", |s| ())  //< And here as well...
        .button("Uh?", |s| ()); //< And finally here too.
}

As you see, Dialog::button() can be called several times to add more buttons.

The <Uh?> button will add a new popup, without removing the current one: it'll act as a dismissable infobox. Dialog::info() is a shortcut to build such a popup:

fn show_next(s: &mut Cursive) {
    s.pop_layer();
    s.add_layer(Dialog::text("Did you do the thing?")
        .title("Question 1")
        .button("Yes!", |s| ()) //< We'll fill this callback soon,
        .button("No!", |s| ())  //< along with this one.
        .button("Uh?", |s| s.add_layer(Dialog::info("Try again!"))));
}

Finally, let's have the "Yes" and "No" buttons use the same callback method to print a message, but with a different text parameter:

fn show_next(s: &mut Cursive) {
    s.pop_layer();
    s.add_layer(Dialog::text("Did you do the thing?")
        .title("Question 1")
        .button("Yes!", |s| show_answer(s, "I knew it! Well done!"))
        .button("No!", |s| show_answer(s, "I knew you couldn't be trusted!"))
        .button("Uh?", |s| s.add_layer(Dialog::info("Try again!"))));
}

fn show_answer(s: &mut Cursive, msg: &str) {
    s.pop_layer();
    s.add_layer(Dialog::text(msg)
        .title("Results")
        .button("Finish", |s| s.quit()));
}

Here, show_answer() does the same thing: remove the previous layer, and add a new Dialog instead.

Conclusion

Your code should now look like the one at the top of this guide.

As you've seen, the Dialog view is a nice way to present a TextView, but it works with any other content. Actually, most layers you'll add will start with a Dialog wrapping some other view.