aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/question.rs12
-rw-r--r--src/backends/dialog.rs33
-rw-r--r--src/backends/mod.rs3
-rw-r--r--src/lib.rs60
4 files changed, 104 insertions, 4 deletions
diff --git a/examples/question.rs b/examples/question.rs
new file mode 100644
index 0000000..d9c0e91
--- /dev/null
+++ b/examples/question.rs
@@ -0,0 +1,12 @@
+// Copyright (C) 2019 Robin Krahl <robin.krahl@ireas.org>
+// SPDX-License-Identifier: MIT
+
+use std::io::Result;
+
+use dialog::DialogBox;
+
+fn main() -> Result<()> {
+ let choice = dialog::Question::new("Do you want to continue?").show()?;
+ println!("The user chose: {:?}", choice);
+ Ok(())
+}
diff --git a/src/backends/dialog.rs b/src/backends/dialog.rs
index 8cde8cc..fb0f73f 100644
--- a/src/backends/dialog.rs
+++ b/src/backends/dialog.rs
@@ -5,7 +5,7 @@ use std::io;
use std::io::Result;
use std::process;
-use crate::{Input, Message};
+use crate::{Choice, Input, Message, Question};
/// The `dialog` backend.
///
@@ -87,6 +87,25 @@ fn require_success(status: process::ExitStatus) -> Result<()> {
}
}
+fn get_choice(status: process::ExitStatus) -> Result<Choice> {
+ if let Some(code) = status.code() {
+ match code {
+ 0 => Ok(Choice::Yes),
+ 1 => Ok(Choice::No),
+ 255 => Ok(Choice::Cancel),
+ code => Err(io::Error::new(
+ io::ErrorKind::Other,
+ format!("Could not execute dialog: {}", code),
+ )),
+ }
+ } else {
+ Err(io::Error::new(
+ io::ErrorKind::Other,
+ "dialog was terminated by a signal",
+ ))
+ }
+}
+
fn get_stderr(output: process::Output) -> Result<Option<String>> {
if output.status.success() {
String::from_utf8(output.stderr)
@@ -99,10 +118,10 @@ fn get_stderr(output: process::Output) -> Result<Option<String>> {
match code {
0 => Ok(None),
1 => Ok(None),
- -1 => Ok(None),
- _ => Err(io::Error::new(
+ 255 => Ok(None),
+ code => Err(io::Error::new(
io::ErrorKind::Other,
- "Could not execute dialog",
+ format!("Could not execute dialog: {}", code),
)),
}
} else {
@@ -131,4 +150,10 @@ impl super::Backend for Dialog {
.and_then(|output| require_success(output.status))
.map(|_| ())
}
+
+ fn show_question(&self, question: &Question) -> Result<Choice> {
+ let args = vec!["--yesno", &question.text];
+ self.execute(args, vec![], &question.title)
+ .and_then(|output| get_choice(output.status))
+ }
}
diff --git a/src/backends/mod.rs b/src/backends/mod.rs
index 1abb8d1..12a178b 100644
--- a/src/backends/mod.rs
+++ b/src/backends/mod.rs
@@ -22,4 +22,7 @@ pub trait Backend {
/// Shows the given message dialog.
fn show_message(&self, message: &super::Message) -> Result<()>;
+
+ /// Shows the given question dialog and returns the choice.
+ fn show_question(&self, question: &super::Question) -> Result<super::Choice>;
}
diff --git a/src/lib.rs b/src/lib.rs
index 0c9d9f9..b51a3cc 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -9,6 +9,7 @@
//! are:
//! - [`Input`][]: a text input dialog
//! - [`Message`][]: a simple message box
+//! - [`Question`][]: a question dialog box
//!
//! These dialog boxes can be displayed using various backends:
//! - [`Dialog`][]: uses `dialog` to display ncurses-based dialog boxes (requires the external
@@ -64,6 +65,7 @@
//! [`Dialog`]: backends/struct.Dialog.html
//! [`Input`]: struct.Input.html
//! [`Message`]: struct.Message.html
+//! [`Question`]: struct.Question.html
//! [`default_backend`]: fn.default_backend.html
//! [`show`]: trait.DialogBox.html#method.show
//! [`show_with`]: trait.DialogBox.html#method.show_with
@@ -203,6 +205,64 @@ impl DialogBox for Input {
}
}
+/// A user choise in a dialog box.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum Choice {
+ /// The yes button.
+ Yes,
+ /// The no button.
+ No,
+ /// The cancel button or a cancelled dialog.
+ Cancel,
+}
+
+/// A question dialog box.
+///
+/// This dialog box displays a text and an optional title and has a yes and a no button. The
+/// output is the button presed by the user, or Cancel if the dialog has been cancelled.
+///
+/// # Example
+///
+/// ```no_run
+/// use dialog::DialogBox;
+///
+/// let choice = dialog::Question::new("Do you want to continue?")
+/// .title("Question")
+/// .show()
+/// .expect("Could not display dialog box");
+/// println!("The user chose: {:?}", choice);
+/// ```
+pub struct Question {
+ text: String,
+ title: Option<String>,
+}
+
+impl Question {
+ /// Creates a new question dialog with the given text.
+ pub fn new(text: impl Into<String>) -> Question {
+ Question {
+ text: text.into(),
+ title: None,
+ }
+ }
+
+ /// Sets the title of this question dialog box.
+ ///
+ /// This method returns a reference to `self` to enable chaining.
+ pub fn title(&mut self, title: impl Into<String>) -> &mut Question {
+ self.title = Some(title.into());
+ self
+ }
+}
+
+impl DialogBox for Question {
+ type Output = Choice;
+
+ fn show_with(&self, backend: &impl backends::Backend) -> Result<Self::Output> {
+ backend.show_question(self)
+ }
+}
+
/// Creates a new instance of the default backend.
///
/// The current implementation always returns a [`Dialog`][] instance.