takecrate/tui/
dialog.rs

1use std::sync::mpsc::Receiver;
2
3use cursive::{
4    align::HAlign,
5    view::Nameable,
6    views::{Dialog, DialogFocus, LinearLayout, NamedView, ProgressBar, TextView},
7    Cursive,
8};
9
10use crate::{
11    error::{InstallerError, InstallerErrorKind},
12    locale::Locale,
13};
14
15pub enum GuidedDialogButton<T> {
16    Exit,
17    Next(T),
18}
19
20impl<T> GuidedDialogButton<T> {
21    pub fn unwrap_button(self) -> Result<T, InstallerError> {
22        match self {
23            GuidedDialogButton::Exit => Err(InstallerErrorKind::InterruptedByUser.into()),
24            GuidedDialogButton::Next(value) => Ok(value),
25        }
26    }
27}
28
29pub fn guided_dialog<T, F>(
30    locale: &Locale,
31    title: &str,
32    value_callback: F,
33) -> (Dialog, Receiver<GuidedDialogButton<T>>)
34where
35    F: Fn(&mut Cursive) -> T + Send + Sync + 'static,
36    T: Send + 'static,
37{
38    let (sender, receiver) = std::sync::mpsc::sync_channel(1);
39    let sender2 = sender.clone();
40
41    let exit_text = locale.text("button-exit");
42    let next_text = locale.text("button-next");
43
44    let mut dialog = Dialog::new().title(title).h_align(HAlign::Right);
45
46    dialog.add_button(exit_text, move |cursive| {
47        cursive.pop_layer();
48        sender.send(GuidedDialogButton::Exit).unwrap();
49    });
50    dialog.add_button(next_text, move |cursive| {
51        cursive.pop_layer();
52        let value = value_callback(cursive);
53        sender2.send(GuidedDialogButton::Next(value)).unwrap();
54    });
55
56    let _ = dialog.set_focus(DialogFocus::Button(1));
57
58    (dialog, receiver)
59}
60
61pub fn info_dialog(locale: &Locale, title: &str) -> (Dialog, Receiver<()>) {
62    let (sender, receiver) = std::sync::mpsc::sync_channel(1);
63
64    let button_text = locale.text("button-ok");
65
66    let dialog = Dialog::new()
67        .title(title)
68        .button(button_text, move |cursive| {
69            cursive.pop_layer();
70            sender.send(()).unwrap();
71        })
72        .h_align(HAlign::Center);
73
74    (dialog, receiver)
75}
76
77const PROGRESS_DIALOG: &str = "progress_dialog";
78const PROGRESS_DIALOG_TEXT: &str = "progress_dialog_text";
79const PROGRESS_DIALOG_SUBTEXT: &str = "progress_dialog_subtext";
80const PROGRESS_DIALOG_PROGRESS_BAR: &str = "progress_dialog_progress_bar";
81
82pub fn progress_dialog(title: &str) -> NamedView<Dialog> {
83    let layout = LinearLayout::vertical()
84        .child(TextView::empty().with_name(PROGRESS_DIALOG_TEXT))
85        .child(TextView::empty().with_name(PROGRESS_DIALOG_SUBTEXT))
86        .child(ProgressBar::new().with_name(PROGRESS_DIALOG_PROGRESS_BAR));
87
88    Dialog::new()
89        .title(title)
90        .content(layout)
91        .with_name(PROGRESS_DIALOG)
92}
93
94pub fn set_progress_dialog_text(cursive: &mut Cursive, value: &str) {
95    if let Some(mut text_view) = cursive.find_name::<TextView>(PROGRESS_DIALOG_TEXT) {
96        text_view.set_content(value);
97    }
98}
99
100pub fn update_progress_dialog_bar(cursive: &mut Cursive, current: u64, total: u64) {
101    if let Some(mut progress_bar) = cursive.find_name::<ProgressBar>(PROGRESS_DIALOG_PROGRESS_BAR) {
102        progress_bar.set_max(total as usize);
103        progress_bar.set_value(current as usize);
104    }
105}
106
107pub fn dismiss_progress_dialog(cursive: &mut Cursive) {
108    if let Some(position) = cursive.screen_mut().find_layer_from_name(PROGRESS_DIALOG) {
109        cursive.screen_mut().remove_layer(position);
110    }
111}