rustdialogcursive

How to create a new Dialog after cursive.run() has been called?


I would like to create and display a new Dialog from inside a "save" button handler.

use std::thread;
use std::time::Duration;
use cursive::align::HAlign;
use cursive::views::{Dialog, DummyView, LinearLayout, TextView};
use cursive::Cursive;
use cursive::view::Resizable;

fn main() {
    env_logger::init();

    let mut siv = cursive::default();
    siv.add_global_callback('q', Cursive::quit);

    siv.add_layer(
        Dialog::around(
            LinearLayout::vertical()
            .child(DummyView.fixed_height(1))
            .child(TextView::new("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."))
            .child(DummyView.fixed_height(1))
        )
        .title("Parent Dialogue")
        .button("Save and Restart", save)
        .button("Cancel", |s| s.quit())
        .h_align(HAlign::Center)
    );

    siv.run();
}

fn save(siv: &mut Cursive) {
    siv.add_layer(
        Dialog::around(
            LinearLayout::vertical()
            .child(DummyView.fixed_height(1))
            .child(TextView::new("Please Wait..."))
            .child(DummyView.fixed_height(1))
        )
        .title("Restarting Service")
        .h_align(HAlign::Center)
    );
    // What can I do to get cursive to draw this Dialog on the screen,
    // but not to wait for keys etc.?

    // Do things which take time.
    // No need to respond to the UI, we just want the user to see the 
    // "please wait" dialogue until the program exits.
    thread::sleep(Duration::from_secs(10));

    siv.quit();
}

The code above fails to display the new Dialog. I assume that this is because it's run after siv::run() has been called at program start.

How do I tell cursive that it should refresh the display?

There is a siv.runner(backend).refresh() which looks hopeful, but I don't have a backend to pass as an argument to .runner().


Solution

  • To solve this, I had to change the flow of information.

    I'd love to see a better way of doing this.

    use std::cell::Cell;
    use std::rc::Rc;
    use std::thread;
    use std::time::Duration;
    use cursive::align::HAlign;
    use cursive::views::{Dialog, DummyView, LinearLayout, TextView};
    use cursive::{Cursive, CursiveRunnable, CursiveRunner};
    use cursive::view::Resizable;
    
    fn main() {
        env_logger::init();
    
        let siv = cursive::default();
        let mut siv = siv.into_runner();
    
        siv.add_global_callback('q', Cursive::quit);
    
        let save_clicked = Rc::new(Cell::new(false));
        let save_clicked_clone = save_clicked.clone();
    
        siv.add_layer(
            Dialog::around(
                LinearLayout::vertical()
                .child(DummyView.fixed_height(1))
                .child(TextView::new("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."))
                .child(DummyView.fixed_height(1))
            )
            .title("Parent Dialogue")
            .button("Save and Restart", move |_s| save_clicked_clone.set(true))
            .button("Cancel", |s| s.quit())
            .h_align(HAlign::Center)
        );
    
        siv.refresh();
        loop {
            siv.step();
            if !siv.is_running() {
                break;
            }
            if save_clicked.get() {
                save(&mut siv);       
                break;
            }
        }
    }
    
    fn save(siv: &mut CursiveRunner<CursiveRunnable>) {
        siv.add_layer(
            Dialog::around(
                LinearLayout::vertical()
                .child(DummyView.fixed_height(1))
                .child(TextView::new("Please Wait..."))
                .child(DummyView.fixed_height(1))
            )
            .title("Restarting Service")
            .h_align(HAlign::Center)
        );
        // Get cursive to draw this Dialog on the screen,
        // but not wait for keys.
        siv.refresh();
    
        // Do things which take time.
        // No need to service the UI, we just want the user to see the 
        // "please waiting" dialogue until the program exits.
        thread::sleep(Duration::from_secs(3));
    
        siv.quit();
    }