aboutsummaryrefslogtreecommitdiff
path: root/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs131
1 files changed, 127 insertions, 4 deletions
diff --git a/src/lib.rs b/src/lib.rs
index f4ac763..aa19105 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -7,6 +7,7 @@
//!
//! The `dialog` crate can be used to display different types of dialog boxes. The supported types
//! are:
+//! - [`FileSelection`][]: a file chooser dialog box
//! - [`Input`][]: a text input dialog
//! - [`Message`][]: a simple message box
//! - [`Password`][]: a password input dialog
@@ -15,6 +16,8 @@
//! These dialog boxes can be displayed using various backends:
//! - [`Dialog`][]: uses `dialog` to display ncurses-based dialog boxes (requires the external
//! `dialog` tool)
+//! - [`KDialog`][]: uses `kdialog` to display Qt-based dialog boxes (requires the external
+//! `kdialog` tool)
//! - [`Stdio`][]: prints messages to the standard output and reads user input form standard input
//! (intended as a fallback backend)
//! - [`Zenity`][]: uses `zenity` to display GTK-based dialog boxes (requires the external `zenity`
@@ -68,10 +71,12 @@
//! ```
//!
//! [`Dialog`]: backends/struct.Dialog.html
+//! [`FileSelection`]: struct.FileSelection.html
//! [`Input`]: struct.Input.html
//! [`Message`]: struct.Message.html
//! [`Password`]: struct.Password.html
//! [`Question`]: struct.Question.html
+//! [`KDialog`]: backends/struct.KDialog.html
//! [`Stdio`]: backends/struct.Stdio.html
//! [`Zenity`]: backends/struct.Zenity.html
//! [`default_backend`]: fn.default_backend.html
@@ -89,7 +94,11 @@ mod error;
/// [`Backend`]: trait.Backend.html
pub mod backends;
-use std::env;
+use dirs;
+use std::{
+ env,
+ path::{Path, PathBuf},
+};
pub use crate::error::{Error, Result};
@@ -339,19 +348,121 @@ impl DialogBox for Question {
}
}
+/// The type of a file selection dialog.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum FileSelectionMode {
+ /// An Open File dialog, meaning that the user can only select an existing file.
+ Open,
+ /// A Save File dialog, meaning that the user is allowed to select a non-existing file.
+ Save,
+}
+
+/// A file chooser dialog box.
+///
+/// This dialog box opens a file choser with an optional title in the specified path. If the path
+/// is not specified, it defaults to the user’s home directory.
+///
+/// The backends might support multiple operation modes, for example open or save dialogs. You can
+/// select a mode using the [`FileSelectionMode`][] enum, though the backend might ignore the mode
+/// and just display a simple file dialog. Per default, the mode is set to `Open`.
+///
+/// # Example
+///
+/// ```no_run
+/// use dialog::DialogBox;
+///
+/// let choice = dialog::FileSelection::new("Please select a file")
+/// .title("File Selection")
+/// .path("/home/user/Downloads")
+/// .show()
+/// .expect("Could not display dialog box");
+/// println!("The user chose: {:?}", choice);
+/// ```
+///
+/// [`FileSelectionMode`]: enum.FileSelectionMode.html
+pub struct FileSelection {
+ text: String,
+ title: Option<String>,
+ path: Option<PathBuf>,
+ mode: FileSelectionMode,
+}
+
+impl FileSelection {
+ /// Creates a new file chooser with the given path.
+ pub fn new(text: impl Into<String>) -> FileSelection {
+ FileSelection {
+ text: text.into(),
+ title: None,
+ path: dirs::home_dir(),
+ mode: FileSelectionMode::Open,
+ }
+ }
+
+ /// Sets the title of this file chooser dialog box.
+ ///
+ /// This method returns a reference to `self` to enable chaining.
+ pub fn title(&mut self, title: impl Into<String>) -> &mut FileSelection {
+ self.title = Some(title.into());
+ self
+ }
+
+ /// Sets the path of this file chooser dialog box.
+ ///
+ /// This method returns a reference to `self` to enable chaining.
+ pub fn path(&mut self, path: impl AsRef<Path>) -> &mut FileSelection {
+ self.path = Some(path.as_ref().to_path_buf());
+ self
+ }
+
+ /// Gets the path of this file chooser dialog box.
+ ///
+ /// This method returns the validated directory as a `String`.
+ pub fn path_to_string(&self) -> Option<String> {
+ match self.path {
+ Some(ref path) if path.is_dir() => {
+ // The backends expect a trailing / after the directory
+ path.to_str().map(|s| s.to_string() + "/")
+ }
+ _ => None,
+ }
+ }
+
+ /// Sets the operation mode of the file chooser.
+ ///
+ /// This method returns a reference to `self` to enable chaining.
+ pub fn mode(&mut self, mode: FileSelectionMode) -> &mut FileSelection {
+ self.mode = mode;
+ self
+ }
+}
+
+impl DialogBox for FileSelection {
+ type Output = Option<String>;
+
+ fn show_with<B>(&self, backend: impl AsRef<B>) -> Result<Self::Output>
+ where
+ B: backends::Backend + ?Sized,
+ {
+ backend.as_ref().show_file_selection(self)
+ }
+}
+
/// Creates a new instance of the default backend.
///
/// The following steps are performed to determine the default backend:
/// - If the `DIALOG` environment variable is set to a valid backend name, this backend is used.
/// A valid backend name is the name of a struct in the `backends` module implementing the
/// `Backend` trait in any case.
-/// - If the `DISPLAY` environment variable is set, the first available backend from this list is
-/// used:
-/// - [`Zenity`][]
+/// - If the `DISPLAY` environment variable is set, the following resolution algorithm is used:
+/// - If the `XDG_CURRENT_DESKTOP` environment variable is set to `KDE`, [`KDialog`][] is used.
+/// - Otherwise, the first available backend from this list is used:
+/// - [`Zenity`][]
+/// - [`KDialog`][]
/// - If the [`Dialog`][] backend is available, it is used.
/// - Otherwise, a [`Stdio`][] instance is returned.
///
/// [`Dialog`]: backends/struct.Dialog.html
+/// [`KDialog`]: backends/struct.KDialog.html
/// [`Stdio`]: backends/struct.Stdio.html
/// [`Zenity`]: backends/struct.Zenity.html
pub fn default_backend() -> Box<dyn backends::Backend> {
@@ -363,9 +474,21 @@ pub fn default_backend() -> Box<dyn backends::Backend> {
if let Ok(display) = env::var("DISPLAY") {
if !display.is_empty() {
+ // Prefer KDialog if the user is logged into a KDE session
+ let kdialog_available = backends::KDialog::is_available();
+ if let Ok(desktop) = env::var("XDG_CURRENT_DESKTOP") {
+ if kdialog_available && desktop == "KDE" {
+ return Box::new(backends::KDialog::new());
+ }
+ }
+
if backends::Zenity::is_available() {
return Box::new(backends::Zenity::new());
}
+
+ if kdialog_available {
+ return Box::new(backends::KDialog::new());
+ }
}
}