Skip to content

Commit f46175d

Browse files
authored
feat(core): expose option to set dialog type, closes #4183 (#4187)
1 parent d99c5d5 commit f46175d

File tree

6 files changed

+268
-59
lines changed

6 files changed

+268
-59
lines changed

.changes/dialog-type.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"tauri": patch
3+
"api": patch
4+
---
5+
6+
Expose option to set the dialog type.

core/tauri/scripts/bundle.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/tauri/src/api/dialog.rs

Lines changed: 133 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,95 @@ macro_rules! file_dialog_builder {
124124
};
125125
}
126126

127+
macro_rules! message_dialog_builder {
128+
() => {
129+
/// A builder for message dialogs.
130+
pub struct MessageDialogBuilder(rfd::MessageDialog);
131+
132+
impl MessageDialogBuilder {
133+
/// Creates a new message dialog builder.
134+
pub fn new(title: impl AsRef<str>, message: impl AsRef<str>) -> Self {
135+
let title = title.as_ref().to_string();
136+
let message = message.as_ref().to_string();
137+
Self(
138+
rfd::MessageDialog::new()
139+
.set_title(&title)
140+
.set_description(&message),
141+
)
142+
}
143+
144+
/// Set parent windows explicitly (optional)
145+
///
146+
/// ## Platform-specific
147+
///
148+
/// - **Linux:** Unsupported.
149+
pub fn parent<W: raw_window_handle::HasRawWindowHandle>(mut self, parent: &W) -> Self {
150+
self.0 = self.0.set_parent(parent);
151+
self
152+
}
153+
154+
/// Set the set of button that will be displayed on the dialog.
155+
pub fn buttons(mut self, buttons: MessageDialogButtons) -> Self {
156+
self.0 = self.0.set_buttons(buttons.into());
157+
self
158+
}
159+
160+
/// Set type of a dialog.
161+
///
162+
/// Depending on the system it can result in type specific icon to show up,
163+
/// the will inform user it message is a error, warning or just information.
164+
pub fn kind(mut self, kind: MessageDialogKind) -> Self {
165+
self.0 = self.0.set_level(kind.into());
166+
self
167+
}
168+
}
169+
};
170+
}
171+
172+
/// Options for action buttons on message dialogs.
173+
#[non_exhaustive]
174+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
175+
pub enum MessageDialogButtons {
176+
/// Ok button.
177+
Ok,
178+
/// Ok and Cancel buttons.
179+
OkCancel,
180+
/// Yes and No buttons.
181+
YesNo,
182+
}
183+
184+
impl From<MessageDialogButtons> for rfd::MessageButtons {
185+
fn from(kind: MessageDialogButtons) -> Self {
186+
match kind {
187+
MessageDialogButtons::Ok => Self::Ok,
188+
MessageDialogButtons::OkCancel => Self::OkCancel,
189+
MessageDialogButtons::YesNo => Self::YesNo,
190+
}
191+
}
192+
}
193+
194+
/// Types of message, ask and confirm dialogs.
195+
#[non_exhaustive]
196+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
197+
pub enum MessageDialogKind {
198+
/// Information dialog.
199+
Info,
200+
/// Warning dialog.
201+
Warning,
202+
/// Error dialog.
203+
Error,
204+
}
205+
206+
impl From<MessageDialogKind> for rfd::MessageLevel {
207+
fn from(kind: MessageDialogKind) -> Self {
208+
match kind {
209+
MessageDialogKind::Info => Self::Info,
210+
MessageDialogKind::Warning => Self::Warning,
211+
MessageDialogKind::Error => Self::Error,
212+
}
213+
}
214+
}
215+
127216
/// Blocking interfaces for the dialog APIs.
128217
///
129218
/// 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 {
132221
/// **NOTE:** You cannot block the main thread when executing the dialog APIs, so you must use the [`crate::api::dialog`] methods instead.
133222
/// Examples of main thread context are the [`crate::App::run`] closure and non-async commmands.
134223
pub mod blocking {
224+
use super::{MessageDialogButtons, MessageDialogKind};
135225
use crate::{Runtime, Window};
136226
use std::path::{Path, PathBuf};
137227
use std::sync::mpsc::sync_channel;
138228

139229
file_dialog_builder!();
230+
message_dialog_builder!();
140231

141232
impl FileDialogBuilder {
142233
/// Shows the dialog to select a single file.
@@ -233,6 +324,22 @@ pub mod blocking {
233324
}
234325
}
235326

327+
impl MessageDialogBuilder {
328+
//// Shows a message dialog.
329+
///
330+
/// - In `Ok` dialog, it will return `true` when `OK` was pressed.
331+
/// - In `OkCancel` dialog, it will return `true` when `OK` was pressed.
332+
/// - In `YesNo` dialog, it will return `true` when `Yes` was pressed.
333+
pub fn show(self) -> bool {
334+
let (tx, rx) = sync_channel(1);
335+
let f = move |response| {
336+
tx.send(response).unwrap();
337+
};
338+
run_dialog!(self.0.show(), f);
339+
rx.recv().unwrap()
340+
}
341+
}
342+
236343
/// Displays a dialog with a message and an optional title with a "yes" and a "no" button and wait for it to be closed.
237344
///
238345
/// This is a blocking operation,
@@ -314,6 +421,7 @@ pub mod blocking {
314421
title,
315422
message,
316423
buttons,
424+
MessageDialogKind::Info,
317425
move |response| {
318426
tx.send(response).unwrap();
319427
},
@@ -323,10 +431,12 @@ pub mod blocking {
323431
}
324432

325433
mod nonblocking {
434+
use super::{MessageDialogButtons, MessageDialogKind};
326435
use crate::{Runtime, Window};
327436
use std::path::{Path, PathBuf};
328437

329438
file_dialog_builder!();
439+
message_dialog_builder!();
330440

331441
impl FileDialogBuilder {
332442
/// Shows the dialog to select a single file.
@@ -431,6 +541,17 @@ mod nonblocking {
431541
}
432542
}
433543

544+
impl MessageDialogBuilder {
545+
/// Shows a message dialog:
546+
///
547+
/// - In `Ok` dialog, it will call the closure with `true` when `OK` was pressed
548+
/// - In `OkCancel` dialog, it will call the closure with `true` when `OK` was pressed
549+
/// - In `YesNo` dialog, it will call the closure with `true` when `Yes` was pressed
550+
pub fn show<F: FnOnce(bool) + Send + 'static>(self, f: F) {
551+
run_dialog!(self.0.show(), f);
552+
}
553+
}
554+
434555
/// Displays a non-blocking dialog with a message and an optional title with a "yes" and a "no" button.
435556
///
436557
/// This is not a blocking operation,
@@ -453,7 +574,14 @@ mod nonblocking {
453574
message: impl AsRef<str>,
454575
f: F,
455576
) {
456-
run_message_dialog(parent_window, title, message, rfd::MessageButtons::YesNo, f)
577+
run_message_dialog(
578+
parent_window,
579+
title,
580+
message,
581+
rfd::MessageButtons::YesNo,
582+
MessageDialogKind::Info,
583+
f,
584+
)
457585
}
458586

459587
/// 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 {
483611
title,
484612
message,
485613
rfd::MessageButtons::OkCancel,
614+
MessageDialogKind::Info,
486615
f,
487616
)
488617
}
@@ -511,6 +640,7 @@ mod nonblocking {
511640
title,
512641
message,
513642
rfd::MessageButtons::Ok,
643+
MessageDialogKind::Info,
514644
|_| {},
515645
)
516646
}
@@ -521,6 +651,7 @@ mod nonblocking {
521651
title: impl AsRef<str>,
522652
message: impl AsRef<str>,
523653
buttons: rfd::MessageButtons,
654+
level: MessageDialogKind,
524655
f: F,
525656
) {
526657
let title = title.as_ref().to_string();
@@ -530,7 +661,7 @@ mod nonblocking {
530661
.set_title(&title)
531662
.set_description(&message)
532663
.set_buttons(buttons)
533-
.set_level(rfd::MessageLevel::Info);
664+
.set_level(level.into());
534665

535666
#[cfg(any(windows, target_os = "macos"))]
536667
{

core/tauri/src/endpoints/dialog.rs

Lines changed: 87 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,38 @@ use super::{InvokeContext, InvokeResponse};
88
use crate::Runtime;
99
#[cfg(any(dialog_open, dialog_save))]
1010
use crate::{api::dialog::blocking::FileDialogBuilder, Manager, Scopes};
11-
use serde::Deserialize;
11+
use serde::{Deserialize, Deserializer};
1212
use tauri_macros::{command_enum, module_command_handler, CommandModule};
1313

1414
use std::path::PathBuf;
1515

16+
#[cfg(any(dialog_message, dialog_ask, dialog_confirm))]
17+
macro_rules! message_dialog {
18+
($fn_name: ident, $allowlist: ident, $buttons: expr) => {
19+
#[module_command_handler($allowlist)]
20+
fn $fn_name<R: Runtime>(
21+
context: InvokeContext<R>,
22+
title: Option<String>,
23+
message: String,
24+
level: Option<MessageDialogType>,
25+
) -> super::Result<bool> {
26+
let mut builder = crate::api::dialog::blocking::MessageDialogBuilder::new(
27+
title.unwrap_or_else(|| context.window.app_handle.package_info().name.clone()),
28+
message,
29+
)
30+
.buttons($buttons);
31+
#[cfg(any(windows, target_os = "macos"))]
32+
{
33+
builder = builder.parent(&context.window);
34+
}
35+
if let Some(level) = level {
36+
builder = builder.kind(level.into());
37+
}
38+
Ok(builder.show())
39+
}
40+
};
41+
}
42+
1643
#[allow(dead_code)]
1744
#[derive(Debug, Clone, Deserialize)]
1845
#[serde(rename_all = "camelCase")]
@@ -57,6 +84,44 @@ pub struct SaveDialogOptions {
5784
pub default_path: Option<PathBuf>,
5885
}
5986

87+
/// Types of message, ask and confirm dialogs.
88+
#[non_exhaustive]
89+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
90+
pub enum MessageDialogType {
91+
/// Information dialog.
92+
Info,
93+
/// Warning dialog.
94+
Warning,
95+
/// Error dialog.
96+
Error,
97+
}
98+
99+
impl<'de> Deserialize<'de> for MessageDialogType {
100+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
101+
where
102+
D: Deserializer<'de>,
103+
{
104+
let s = String::deserialize(deserializer)?;
105+
Ok(match s.to_lowercase().as_str() {
106+
"info" => MessageDialogType::Info,
107+
"warning" => MessageDialogType::Warning,
108+
"error" => MessageDialogType::Error,
109+
_ => MessageDialogType::Info,
110+
})
111+
}
112+
}
113+
114+
#[cfg(any(dialog_message, dialog_ask, dialog_confirm))]
115+
impl From<MessageDialogType> for crate::api::dialog::MessageDialogKind {
116+
fn from(kind: MessageDialogType) -> Self {
117+
match kind {
118+
MessageDialogType::Info => Self::Info,
119+
MessageDialogType::Warning => Self::Warning,
120+
MessageDialogType::Error => Self::Error,
121+
}
122+
}
123+
}
124+
60125
/// The API descriptor.
61126
#[command_enum]
62127
#[derive(Deserialize, CommandModule)]
@@ -73,16 +138,22 @@ pub enum Cmd {
73138
MessageDialog {
74139
title: Option<String>,
75140
message: String,
141+
#[serde(rename = "type")]
142+
level: Option<MessageDialogType>,
76143
},
77144
#[cmd(dialog_ask, "dialog > ask")]
78145
AskDialog {
79146
title: Option<String>,
80147
message: String,
148+
#[serde(rename = "type")]
149+
level: Option<MessageDialogType>,
81150
},
82151
#[cmd(dialog_confirm, "dialog > confirm")]
83152
ConfirmDialog {
84153
title: Option<String>,
85154
message: String,
155+
#[serde(rename = "type")]
156+
level: Option<MessageDialogType>,
86157
},
87158
}
88159

@@ -170,45 +241,23 @@ impl Cmd {
170241
Ok(path)
171242
}
172243

173-
#[module_command_handler(dialog_message)]
174-
fn message_dialog<R: Runtime>(
175-
context: InvokeContext<R>,
176-
title: Option<String>,
177-
message: String,
178-
) -> super::Result<()> {
179-
crate::api::dialog::blocking::message(
180-
Some(&context.window),
181-
title.unwrap_or_else(|| context.window.app_handle.package_info().name.clone()),
182-
message,
183-
);
184-
Ok(())
185-
}
244+
message_dialog!(
245+
message_dialog,
246+
dialog_message,
247+
crate::api::dialog::MessageDialogButtons::Ok
248+
);
186249

187-
#[module_command_handler(dialog_ask)]
188-
fn ask_dialog<R: Runtime>(
189-
context: InvokeContext<R>,
190-
title: Option<String>,
191-
message: String,
192-
) -> super::Result<bool> {
193-
Ok(crate::api::dialog::blocking::ask(
194-
Some(&context.window),
195-
title.unwrap_or_else(|| context.window.app_handle.package_info().name.clone()),
196-
message,
197-
))
198-
}
250+
message_dialog!(
251+
ask_dialog,
252+
dialog_ask,
253+
crate::api::dialog::MessageDialogButtons::YesNo
254+
);
199255

200-
#[module_command_handler(dialog_confirm)]
201-
fn confirm_dialog<R: Runtime>(
202-
context: InvokeContext<R>,
203-
title: Option<String>,
204-
message: String,
205-
) -> super::Result<bool> {
206-
Ok(crate::api::dialog::blocking::confirm(
207-
Some(&context.window),
208-
title.unwrap_or_else(|| context.window.app_handle.package_info().name.clone()),
209-
message,
210-
))
211-
}
256+
message_dialog!(
257+
confirm_dialog,
258+
dialog_confirm,
259+
crate::api::dialog::MessageDialogButtons::OkCancel
260+
);
212261
}
213262

214263
#[cfg(any(dialog_open, dialog_save))]

0 commit comments

Comments
 (0)