diff options
Diffstat (limited to 'src/lib.rs')
-rw-r--r-- | src/lib.rs | 131 |
1 files changed, 127 insertions, 4 deletions
@@ -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()); + } } } |