|
@@ -124,6 +124,95 @@ macro_rules! file_dialog_builder {
|
|
};
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+macro_rules! message_dialog_builder {
|
|
|
|
+ () => {
|
|
|
|
+ /// A builder for message dialogs.
|
|
|
|
+ pub struct MessageDialogBuilder(rfd::MessageDialog);
|
|
|
|
+
|
|
|
|
+ impl MessageDialogBuilder {
|
|
|
|
+ /// Creates a new message dialog builder.
|
|
|
|
+ pub fn new(title: impl AsRef<str>, message: impl AsRef<str>) -> Self {
|
|
|
|
+ let title = title.as_ref().to_string();
|
|
|
|
+ let message = message.as_ref().to_string();
|
|
|
|
+ Self(
|
|
|
|
+ rfd::MessageDialog::new()
|
|
|
|
+ .set_title(&title)
|
|
|
|
+ .set_description(&message),
|
|
|
|
+ )
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Set parent windows explicitly (optional)
|
|
|
|
+ ///
|
|
|
|
+ /// ## Platform-specific
|
|
|
|
+ ///
|
|
|
|
+ /// - **Linux:** Unsupported.
|
|
|
|
+ pub fn parent<W: raw_window_handle::HasRawWindowHandle>(mut self, parent: &W) -> Self {
|
|
|
|
+ self.0 = self.0.set_parent(parent);
|
|
|
|
+ self
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Set the set of button that will be displayed on the dialog.
|
|
|
|
+ pub fn buttons(mut self, buttons: MessageDialogButtons) -> Self {
|
|
|
|
+ self.0 = self.0.set_buttons(buttons.into());
|
|
|
|
+ self
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Set type of a dialog.
|
|
|
|
+ ///
|
|
|
|
+ /// Depending on the system it can result in type specific icon to show up,
|
|
|
|
+ /// the will inform user it message is a error, warning or just information.
|
|
|
|
+ pub fn kind(mut self, kind: MessageDialogKind) -> Self {
|
|
|
|
+ self.0 = self.0.set_level(kind.into());
|
|
|
|
+ self
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/// Options for action buttons on message dialogs.
|
|
|
|
+#[non_exhaustive]
|
|
|
|
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
|
|
|
+pub enum MessageDialogButtons {
|
|
|
|
+ /// Ok button.
|
|
|
|
+ Ok,
|
|
|
|
+ /// Ok and Cancel buttons.
|
|
|
|
+ OkCancel,
|
|
|
|
+ /// Yes and No buttons.
|
|
|
|
+ YesNo,
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+impl From<MessageDialogButtons> for rfd::MessageButtons {
|
|
|
|
+ fn from(kind: MessageDialogButtons) -> Self {
|
|
|
|
+ match kind {
|
|
|
|
+ MessageDialogButtons::Ok => Self::Ok,
|
|
|
|
+ MessageDialogButtons::OkCancel => Self::OkCancel,
|
|
|
|
+ MessageDialogButtons::YesNo => Self::YesNo,
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/// Types of message, ask and confirm dialogs.
|
|
|
|
+#[non_exhaustive]
|
|
|
|
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
|
|
|
+pub enum MessageDialogKind {
|
|
|
|
+ /// Information dialog.
|
|
|
|
+ Info,
|
|
|
|
+ /// Warning dialog.
|
|
|
|
+ Warning,
|
|
|
|
+ /// Error dialog.
|
|
|
|
+ Error,
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+impl From<MessageDialogKind> for rfd::MessageLevel {
|
|
|
|
+ fn from(kind: MessageDialogKind) -> Self {
|
|
|
|
+ match kind {
|
|
|
|
+ MessageDialogKind::Info => Self::Info,
|
|
|
|
+ MessageDialogKind::Warning => Self::Warning,
|
|
|
|
+ MessageDialogKind::Error => Self::Error,
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
/// Blocking interfaces for the dialog APIs.
|
|
/// Blocking interfaces for the dialog APIs.
|
|
///
|
|
///
|
|
/// The blocking APIs will block the current thread to execute instead of relying on callback closures,
|
|
/// The blocking APIs will block the current thread to execute instead of relying on callback closures,
|
|
@@ -132,11 +221,13 @@ macro_rules! file_dialog_builder {
|
|
/// **NOTE:** You cannot block the main thread when executing the dialog APIs, so you must use the [`crate::api::dialog`] methods instead.
|
|
/// **NOTE:** You cannot block the main thread when executing the dialog APIs, so you must use the [`crate::api::dialog`] methods instead.
|
|
/// Examples of main thread context are the [`crate::App::run`] closure and non-async commmands.
|
|
/// Examples of main thread context are the [`crate::App::run`] closure and non-async commmands.
|
|
pub mod blocking {
|
|
pub mod blocking {
|
|
|
|
+ use super::{MessageDialogButtons, MessageDialogKind};
|
|
use crate::{Runtime, Window};
|
|
use crate::{Runtime, Window};
|
|
use std::path::{Path, PathBuf};
|
|
use std::path::{Path, PathBuf};
|
|
use std::sync::mpsc::sync_channel;
|
|
use std::sync::mpsc::sync_channel;
|
|
|
|
|
|
file_dialog_builder!();
|
|
file_dialog_builder!();
|
|
|
|
+ message_dialog_builder!();
|
|
|
|
|
|
impl FileDialogBuilder {
|
|
impl FileDialogBuilder {
|
|
/// Shows the dialog to select a single file.
|
|
/// Shows the dialog to select a single file.
|
|
@@ -233,6 +324,22 @@ pub mod blocking {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ impl MessageDialogBuilder {
|
|
|
|
+ //// Shows a message dialog.
|
|
|
|
+ ///
|
|
|
|
+ /// - In `Ok` dialog, it will return `true` when `OK` was pressed.
|
|
|
|
+ /// - In `OkCancel` dialog, it will return `true` when `OK` was pressed.
|
|
|
|
+ /// - In `YesNo` dialog, it will return `true` when `Yes` was pressed.
|
|
|
|
+ pub fn show(self) -> bool {
|
|
|
|
+ let (tx, rx) = sync_channel(1);
|
|
|
|
+ let f = move |response| {
|
|
|
|
+ tx.send(response).unwrap();
|
|
|
|
+ };
|
|
|
|
+ run_dialog!(self.0.show(), f);
|
|
|
|
+ rx.recv().unwrap()
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
/// Displays a dialog with a message and an optional title with a "yes" and a "no" button and wait for it to be closed.
|
|
/// Displays a dialog with a message and an optional title with a "yes" and a "no" button and wait for it to be closed.
|
|
///
|
|
///
|
|
/// This is a blocking operation,
|
|
/// This is a blocking operation,
|
|
@@ -314,6 +421,7 @@ pub mod blocking {
|
|
title,
|
|
title,
|
|
message,
|
|
message,
|
|
buttons,
|
|
buttons,
|
|
|
|
+ MessageDialogKind::Info,
|
|
move |response| {
|
|
move |response| {
|
|
tx.send(response).unwrap();
|
|
tx.send(response).unwrap();
|
|
},
|
|
},
|
|
@@ -323,10 +431,12 @@ pub mod blocking {
|
|
}
|
|
}
|
|
|
|
|
|
mod nonblocking {
|
|
mod nonblocking {
|
|
|
|
+ use super::{MessageDialogButtons, MessageDialogKind};
|
|
use crate::{Runtime, Window};
|
|
use crate::{Runtime, Window};
|
|
use std::path::{Path, PathBuf};
|
|
use std::path::{Path, PathBuf};
|
|
|
|
|
|
file_dialog_builder!();
|
|
file_dialog_builder!();
|
|
|
|
+ message_dialog_builder!();
|
|
|
|
|
|
impl FileDialogBuilder {
|
|
impl FileDialogBuilder {
|
|
/// Shows the dialog to select a single file.
|
|
/// Shows the dialog to select a single file.
|
|
@@ -431,6 +541,17 @@ mod nonblocking {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ impl MessageDialogBuilder {
|
|
|
|
+ /// Shows a message dialog:
|
|
|
|
+ ///
|
|
|
|
+ /// - In `Ok` dialog, it will call the closure with `true` when `OK` was pressed
|
|
|
|
+ /// - In `OkCancel` dialog, it will call the closure with `true` when `OK` was pressed
|
|
|
|
+ /// - In `YesNo` dialog, it will call the closure with `true` when `Yes` was pressed
|
|
|
|
+ pub fn show<F: FnOnce(bool) + Send + 'static>(self, f: F) {
|
|
|
|
+ run_dialog!(self.0.show(), f);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
/// Displays a non-blocking dialog with a message and an optional title with a "yes" and a "no" button.
|
|
/// Displays a non-blocking dialog with a message and an optional title with a "yes" and a "no" button.
|
|
///
|
|
///
|
|
/// This is not a blocking operation,
|
|
/// This is not a blocking operation,
|
|
@@ -453,7 +574,14 @@ mod nonblocking {
|
|
message: impl AsRef<str>,
|
|
message: impl AsRef<str>,
|
|
f: F,
|
|
f: F,
|
|
) {
|
|
) {
|
|
- run_message_dialog(parent_window, title, message, rfd::MessageButtons::YesNo, f)
|
|
|
|
|
|
+ run_message_dialog(
|
|
|
|
+ parent_window,
|
|
|
|
+ title,
|
|
|
|
+ message,
|
|
|
|
+ rfd::MessageButtons::YesNo,
|
|
|
|
+ MessageDialogKind::Info,
|
|
|
|
+ f,
|
|
|
|
+ )
|
|
}
|
|
}
|
|
|
|
|
|
/// Displays a non-blocking dialog with a message and an optional title with an "ok" and a "cancel" button.
|
|
/// Displays a non-blocking dialog with a message and an optional title with an "ok" and a "cancel" button.
|
|
@@ -483,6 +611,7 @@ mod nonblocking {
|
|
title,
|
|
title,
|
|
message,
|
|
message,
|
|
rfd::MessageButtons::OkCancel,
|
|
rfd::MessageButtons::OkCancel,
|
|
|
|
+ MessageDialogKind::Info,
|
|
f,
|
|
f,
|
|
)
|
|
)
|
|
}
|
|
}
|
|
@@ -511,6 +640,7 @@ mod nonblocking {
|
|
title,
|
|
title,
|
|
message,
|
|
message,
|
|
rfd::MessageButtons::Ok,
|
|
rfd::MessageButtons::Ok,
|
|
|
|
+ MessageDialogKind::Info,
|
|
|_| {},
|
|
|_| {},
|
|
)
|
|
)
|
|
}
|
|
}
|
|
@@ -521,6 +651,7 @@ mod nonblocking {
|
|
title: impl AsRef<str>,
|
|
title: impl AsRef<str>,
|
|
message: impl AsRef<str>,
|
|
message: impl AsRef<str>,
|
|
buttons: rfd::MessageButtons,
|
|
buttons: rfd::MessageButtons,
|
|
|
|
+ level: MessageDialogKind,
|
|
f: F,
|
|
f: F,
|
|
) {
|
|
) {
|
|
let title = title.as_ref().to_string();
|
|
let title = title.as_ref().to_string();
|
|
@@ -530,7 +661,7 @@ mod nonblocking {
|
|
.set_title(&title)
|
|
.set_title(&title)
|
|
.set_description(&message)
|
|
.set_description(&message)
|
|
.set_buttons(buttons)
|
|
.set_buttons(buttons)
|
|
- .set_level(rfd::MessageLevel::Info);
|
|
|
|
|
|
+ .set_level(level.into());
|
|
|
|
|
|
#[cfg(any(windows, target_os = "macos"))]
|
|
#[cfg(any(windows, target_os = "macos"))]
|
|
{
|
|
{
|