feat: support message dialogs with 3 buttons

pull/2641/head
amrbashir 2 months ago
parent c5b0f51cfd
commit 3ca0daed4d
No known key found for this signature in database

@ -44,6 +44,13 @@
await message("Tauri is awesome!"); await message("Tauri is awesome!");
} }
async function msgCustom(result) {
const buttons = { yes: "awesome", no: "amazing", cancel: "stunning" };
await message(`Tauri is: `, { buttons })
.then((res) => onMessage(`Tauri is ${res}`))
.catch(onMessage);
}
function openDialog() { function openDialog() {
open({ open({
title: "My wonderful open dialog", title: "My wonderful open dialog",
@ -136,12 +143,17 @@
<label for="dialog-directory">Directory</label> <label for="dialog-directory">Directory</label>
</div> </div>
<br /> <br />
<button class="btn" id="open-dialog" on:click={openDialog}>Open dialog</button>
<button class="btn" id="save-dialog" on:click={saveDialog} <div class="flex flex-wrap flex-col md:flex-row gap-2 children:flex-shrink-0">
>Open save dialog</button <button class="btn" id="open-dialog" on:click={openDialog}>Open dialog</button>
> <button class="btn" id="save-dialog" on:click={saveDialog}
<button class="btn" id="prompt-dialog" on:click={prompt}>Prompt</button> >Open save dialog</button
<button class="btn" id="custom-prompt-dialog" on:click={promptCustom} >
>Prompt (custom)</button <button class="btn" id="prompt-dialog" on:click={prompt}>Prompt</button>
> <button class="btn" id="custom-prompt-dialog" on:click={promptCustom}
<button class="btn" id="message-dialog" on:click={msg}>Message</button> >Prompt (custom)</button
>
<button class="btn" id="message-dialog" on:click={msg}>Message</button>
<button class="btn" id="message-dialog" on:click={msgCustom}>Message (custom)</button>
</div>

@ -38,6 +38,7 @@ class MessageOptions {
var title: String? = null var title: String? = null
lateinit var message: String lateinit var message: String
var okButtonLabel: String? = null var okButtonLabel: String? = null
var noButtonLabel: String? = null
var cancelButtonLabel: String? = null var cancelButtonLabel: String? = null
} }
@ -169,9 +170,8 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
return return
} }
val handler = { cancelled: Boolean, value: Boolean -> val handler = { value: String ->
val ret = JSObject() val ret = JSObject()
ret.put("cancelled", cancelled)
ret.put("value", value) ret.put("value", value)
invoke.resolve(ret) invoke.resolve(ret)
} }
@ -183,24 +183,34 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
if (args.title != null) { if (args.title != null) {
builder.setTitle(args.title) builder.setTitle(args.title)
} }
val okButtonLabel = args.okButtonLabel ?: "Ok"
builder builder
.setMessage(args.message) .setMessage(args.message)
.setPositiveButton( .setPositiveButton(okButtonLabel) { dialog, _ ->
args.okButtonLabel ?: "OK"
) { dialog, _ ->
dialog.dismiss() dialog.dismiss()
handler(false, true) handler(okButtonLabel)
} }
.setOnCancelListener { dialog -> .setOnCancelListener { dialog ->
dialog.dismiss() dialog.dismiss()
handler(true, false) handler(args.cancelButtonLabel ?: "Cancel")
}
if (args.noButtonLabel != null) {
builder.setNeutralButton(args.noButtonLabel) { dialog, _ ->
dialog.dismiss()
handler(args.noButtonLabel!!)
} }
}
if (args.cancelButtonLabel != null) { if (args.cancelButtonLabel != null) {
builder.setNegativeButton( args.cancelButtonLabel) { dialog, _ -> builder.setNegativeButton( args.cancelButtonLabel) { dialog, _ ->
dialog.dismiss() dialog.dismiss()
handler(false, false) handler(args.cancelButtonLabel!!)
} }
} }
val dialog = builder.create() val dialog = builder.create()
dialog.show() dialog.show()
} }

@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_DIALOG__=function(t){"use strict";async function n(t,n={},e){return window.__TAURI_INTERNALS__.invoke(t,n,e)}return"function"==typeof SuppressedError&&SuppressedError,t.ask=async function(t,e){const i="string"==typeof e?{title:e}:e;return await n("plugin:dialog|ask",{message:t.toString(),title:i?.title?.toString(),kind:i?.kind,yesButtonLabel:i?.okLabel?.toString(),noButtonLabel:i?.cancelLabel?.toString()})},t.confirm=async function(t,e){const i="string"==typeof e?{title:e}:e;return await n("plugin:dialog|confirm",{message:t.toString(),title:i?.title?.toString(),kind:i?.kind,okButtonLabel:i?.okLabel?.toString(),cancelButtonLabel:i?.cancelLabel?.toString()})},t.message=async function(t,e){const i="string"==typeof e?{title:e}:e;await n("plugin:dialog|message",{message:t.toString(),title:i?.title?.toString(),kind:i?.kind,okButtonLabel:i?.okLabel?.toString()})},t.open=async function(t={}){return"object"==typeof t&&Object.freeze(t),await n("plugin:dialog|open",{options:t})},t.save=async function(t={}){return"object"==typeof t&&Object.freeze(t),await n("plugin:dialog|save",{options:t})},t}({});Object.defineProperty(window.__TAURI__,"dialog",{value:__TAURI_PLUGIN_DIALOG__})} if("__TAURI__"in window){var __TAURI_PLUGIN_DIALOG__=function(t){"use strict";async function n(t,n={},e){return window.__TAURI_INTERNALS__.invoke(t,n,e)}function e(t){if(void 0!==t)return"string"==typeof t?t:"ok"in t&&"cancel"in t?{OkCancelCustom:[t.ok,t.cancel]}:"yes"in t&&"no"in t&&"cancel"in t?{YesNoCancelCustom:[t.yes,t.no,t.cancel]}:"ok"in t?{OkCustom:t.ok}:void 0}return"function"==typeof SuppressedError&&SuppressedError,t.ask=async function(t,e){const o="string"==typeof e?{title:e}:e;return await n("plugin:dialog|ask",{message:t.toString(),title:o?.title?.toString(),kind:o?.kind,yesButtonLabel:o?.okLabel?.toString(),noButtonLabel:o?.cancelLabel?.toString()})},t.confirm=async function(t,e){const o="string"==typeof e?{title:e}:e;return await n("plugin:dialog|confirm",{message:t.toString(),title:o?.title?.toString(),kind:o?.kind,okButtonLabel:o?.okLabel?.toString(),cancelButtonLabel:o?.cancelLabel?.toString()})},t.message=async function(t,o){const i="string"==typeof o?{title:o}:o;return function(t){return"string"==typeof t?t:t.Custom}(await n("plugin:dialog|message",{message:t.toString(),title:i?.title?.toString(),kind:i?.kind,okButtonLabel:i?.okLabel?.toString(),buttons:e(i?.buttons)}))},t.open=async function(t={}){return"object"==typeof t&&Object.freeze(t),await n("plugin:dialog|open",{options:t})},t.save=async function(t={}){return"object"==typeof t&&Object.freeze(t),await n("plugin:dialog|save",{options:t})},t}({});Object.defineProperty(window.__TAURI__,"dialog",{value:__TAURI_PLUGIN_DIALOG__})}

@ -77,6 +77,68 @@ interface SaveDialogOptions {
canCreateDirectories?: boolean canCreateDirectories?: boolean
} }
/**
* Default buttons for a message dialog.
*
* @since 2.3.0
*/
export type MessageDialogDefaultButtons = 'Ok' | 'OkCancel' | 'YesNo'
/**
* The Yes, No and Cancel buttons of a message dialog.
*
* @since 2.3.0
*/
export type MessageDialogButtonsYesNoCancel = {
/** The Yes button. */
yes?: string
/** The No button. */
no?: string
/** The Cancel button. */
cancel?: string
}
/**
* The Ok and Cancel buttons of a message dialog.
*
* @since 2.3.0
*/
export type MessageDialogButtonsOkCancel = {
/** The Ok button. */
ok?: string
/** The Cancel button. */
cancel?: string
}
/**
* The Ok button of a message dialog.
*
* @since 2.3.0
*/
export type MessageDialogButtonsOk = {
/** The Ok button. */
ok?: string
}
/**
* Custom buttons for a message dialog.
*
* @since 2.3.0
*/
export type MessageDialogCustomButtons =
| MessageDialogButtonsYesNoCancel
| MessageDialogButtonsOkCancel
| MessageDialogButtonsOk
/**
* The buttons of a message dialog.
*
* @since 2.3.0
*/
export type MessageDialogButtons =
| MessageDialogDefaultButtons
| MessageDialogCustomButtons
/** /**
* @since 2.0.0 * @since 2.0.0
*/ */
@ -85,8 +147,41 @@ interface MessageDialogOptions {
title?: string title?: string
/** The kind of the dialog. Defaults to `info`. */ /** The kind of the dialog. Defaults to `info`. */
kind?: 'info' | 'warning' | 'error' kind?: 'info' | 'warning' | 'error'
/** The label of the confirm button. */ /**
* The label of the Ok button.
*
* @deprecated Use {@linkcode MessageDialogOptions.buttons} instead.
*/
okLabel?: string okLabel?: string
/**
* The buttons of the dialog.
*
* @since 2.3.0
*/
buttons?: MessageDialogButtons
}
/**
* Internal function to convert the buttons to the Rust type.
*/
function buttonsToRust(buttons: MessageDialogButtons | undefined) {
if (buttons === undefined) {
return undefined
}
if (typeof buttons === 'string') {
return buttons
} else if ('ok' in buttons && 'cancel' in buttons) {
return { OkCancelCustom: [buttons.ok, buttons.cancel] }
} else if ('yes' in buttons && 'no' in buttons && 'cancel' in buttons) {
return {
YesNoCancelCustom: [buttons.yes, buttons.no, buttons.cancel]
}
} else if ('ok' in buttons) {
return { OkCustom: buttons.ok }
}
return undefined
} }
interface ConfirmDialogOptions { interface ConfirmDialogOptions {
@ -202,6 +297,32 @@ async function save(options: SaveDialogOptions = {}): Promise<string | null> {
return await invoke('plugin:dialog|save', { options }) return await invoke('plugin:dialog|save', { options })
} }
/**
* The result of a message dialog.
*
* The result is a string if the dialog has custom buttons,
* otherwise it is one of the default buttons.
*
* @since 2.3.0
*/
export type MessageDialogResult = 'Yes' | 'No' | 'Ok' | 'Cancel' | (string & {})
type MessageDialogResultRust =
| 'Yes'
| 'No'
| 'Ok'
| 'Cancel'
| { Custom: string }
/** Internal function to convert the result to JS. */
function resultToJS(res: MessageDialogResultRust): MessageDialogResult {
if (typeof res === 'string') {
return res
} else {
return res.Custom
}
}
/** /**
* Shows a message dialog with an `Ok` button. * Shows a message dialog with an `Ok` button.
* @example * @example
@ -222,14 +343,18 @@ async function save(options: SaveDialogOptions = {}): Promise<string | null> {
async function message( async function message(
message: string, message: string,
options?: string | MessageDialogOptions options?: string | MessageDialogOptions
): Promise<void> { ): Promise<MessageDialogResult> {
const opts = typeof options === 'string' ? { title: options } : options const opts = typeof options === 'string' ? { title: options } : options
await invoke('plugin:dialog|message', {
const res = await invoke<MessageDialogResultRust>('plugin:dialog|message', {
message: message.toString(), message: message.toString(),
title: opts?.title?.toString(), title: opts?.title?.toString(),
kind: opts?.kind, kind: opts?.kind,
okButtonLabel: opts?.okLabel?.toString() okButtonLabel: opts?.okLabel?.toString(),
buttons: buttonsToRust(opts?.buttons)
}) })
return resultToJS(res)
} }
/** /**

@ -20,6 +20,7 @@ struct MessageDialogOptions: Decodable {
var title: String? var title: String?
let message: String let message: String
var okButtonLabel: String? var okButtonLabel: String?
var noButtonLabel: String?
var cancelButtonLabel: String? var cancelButtonLabel: String?
} }
@ -200,36 +201,38 @@ class DialogPlugin: Plugin {
let alert = UIAlertController( let alert = UIAlertController(
title: args.title, message: args.message, preferredStyle: UIAlertController.Style.alert) title: args.title, message: args.message, preferredStyle: UIAlertController.Style.alert)
let cancelButtonLabel = args.cancelButtonLabel ?? "" if let cancelButtonLabel = args.cancelButtonLabel {
if !cancelButtonLabel.isEmpty {
alert.addAction( alert.addAction(
UIAlertAction( UIAlertAction(
title: cancelButtonLabel, style: UIAlertAction.Style.default, title: cancelButtonLabel, style: UIAlertAction.Style.default,
handler: { (_) -> Void in handler: { (_) -> Void in
Logger.error("cancel") invoke.resolve(["value": cancelButtonLabel])
}
invoke.resolve([ )
"value": false, )
"cancelled": false,
])
}))
} }
let okButtonLabel = args.okButtonLabel ?? (cancelButtonLabel.isEmpty ? "OK" : "") if let noButtonLabel = args.noButtonLabel {
if !okButtonLabel.isEmpty {
alert.addAction( alert.addAction(
UIAlertAction( UIAlertAction(
title: okButtonLabel, style: UIAlertAction.Style.default, title: noButtonLabel, style: UIAlertAction.Style.default,
handler: { (_) -> Void in handler: { (_) -> Void in
Logger.error("ok") invoke.resolve(["value": noButtonLabel])
}
invoke.resolve([ )
"value": true, )
"cancelled": false,
])
}))
} }
let okButtonLabel = args.okButtonLabel ?? "Ok"
alert.addAction(
UIAlertAction(
title: okButtonLabel, style: UIAlertAction.Style.default,
handler: { (_) -> Void in
invoke.resolve(["value": okButtonLabel])
}
)
)
manager.viewController?.present(alert, animated: true, completion: nil) manager.viewController?.present(alert, animated: true, completion: nil)
} }
} }

@ -9,8 +9,8 @@ use tauri::{command, Manager, Runtime, State, Window};
use tauri_plugin_fs::FsExt; use tauri_plugin_fs::FsExt;
use crate::{ use crate::{
Dialog, FileDialogBuilder, FilePath, MessageDialogButtons, MessageDialogKind, Result, CANCEL, Dialog, FileDialogBuilder, FilePath, MessageDialogBuilder, MessageDialogButtons,
NO, OK, YES, MessageDialogKind, MessageDialogResult, Result, CANCEL, NO, OK, YES,
}; };
#[derive(Serialize)] #[derive(Serialize)]
@ -248,7 +248,7 @@ fn message_dialog<R: Runtime>(
message: String, message: String,
kind: Option<MessageDialogKind>, kind: Option<MessageDialogKind>,
buttons: MessageDialogButtons, buttons: MessageDialogButtons,
) -> bool { ) -> MessageDialogBuilder<R> {
let mut builder = dialog.message(message); let mut builder = dialog.message(message);
builder = builder.buttons(buttons); builder = builder.buttons(buttons);
@ -266,7 +266,7 @@ fn message_dialog<R: Runtime>(
builder = builder.kind(kind); builder = builder.kind(kind);
} }
builder.blocking_show() builder
} }
#[command] #[command]
@ -277,19 +277,20 @@ pub(crate) async fn message<R: Runtime>(
message: String, message: String,
kind: Option<MessageDialogKind>, kind: Option<MessageDialogKind>,
ok_button_label: Option<String>, ok_button_label: Option<String>,
) -> Result<bool> { buttons: Option<MessageDialogButtons>,
Ok(message_dialog( ) -> Result<MessageDialogResult> {
window, let buttons = buttons.unwrap_or_else(|| {
dialog,
title,
message,
kind,
if let Some(ok_button_label) = ok_button_label { if let Some(ok_button_label) = ok_button_label {
MessageDialogButtons::OkCustom(ok_button_label) MessageDialogButtons::OkCustom(ok_button_label)
} else { } else {
MessageDialogButtons::Ok MessageDialogButtons::Ok
}, }
)) });
Ok(dbg!(message_dialog(
window, dialog, title, message, kind, buttons
)
.blocking_show_with_result()))
} }
#[command] #[command]
@ -302,7 +303,7 @@ pub(crate) async fn ask<R: Runtime>(
yes_button_label: Option<String>, yes_button_label: Option<String>,
no_button_label: Option<String>, no_button_label: Option<String>,
) -> Result<bool> { ) -> Result<bool> {
Ok(message_dialog( let dialog = message_dialog(
window, window,
dialog, dialog,
title, title,
@ -318,7 +319,9 @@ pub(crate) async fn ask<R: Runtime>(
} else { } else {
MessageDialogButtons::YesNo MessageDialogButtons::YesNo
}, },
)) );
Ok(dialog.blocking_show())
} }
#[command] #[command]
@ -331,7 +334,7 @@ pub(crate) async fn confirm<R: Runtime>(
ok_button_label: Option<String>, ok_button_label: Option<String>,
cancel_button_label: Option<String>, cancel_button_label: Option<String>,
) -> Result<bool> { ) -> Result<bool> {
Ok(message_dialog( let dialog = message_dialog(
window, window,
dialog, dialog,
title, title,
@ -347,5 +350,7 @@ pub(crate) async fn confirm<R: Runtime>(
} else { } else {
MessageDialogButtons::OkCancel MessageDialogButtons::OkCancel
}, },
)) );
Ok(dialog.blocking_show())
} }

@ -13,7 +13,7 @@ use rfd::{AsyncFileDialog, AsyncMessageDialog};
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use tauri::{plugin::PluginApi, AppHandle, Runtime}; use tauri::{plugin::PluginApi, AppHandle, Runtime};
use crate::{models::*, FileDialogBuilder, FilePath, MessageDialogBuilder, OK}; use crate::{models::*, FileDialogBuilder, FilePath, MessageDialogBuilder};
pub fn init<R: Runtime, C: DeserializeOwned>( pub fn init<R: Runtime, C: DeserializeOwned>(
app: &AppHandle<R>, app: &AppHandle<R>,
@ -115,6 +115,9 @@ impl From<MessageDialogButtons> for rfd::MessageButtons {
MessageDialogButtons::YesNo => Self::YesNo, MessageDialogButtons::YesNo => Self::YesNo,
MessageDialogButtons::OkCustom(ok) => Self::OkCustom(ok), MessageDialogButtons::OkCustom(ok) => Self::OkCustom(ok),
MessageDialogButtons::OkCancelCustom(ok, cancel) => Self::OkCancelCustom(ok, cancel), MessageDialogButtons::OkCancelCustom(ok, cancel) => Self::OkCancelCustom(ok, cancel),
MessageDialogButtons::YesNoCancelCustom(yes, no, cancel) => {
Self::YesNoCancelCustom(yes, no, cancel)
}
} }
} }
} }
@ -208,24 +211,11 @@ pub fn save_file<R: Runtime, F: FnOnce(Option<FilePath>) + Send + 'static>(
} }
/// Shows a message dialog /// Shows a message dialog
pub fn show_message_dialog<R: Runtime, F: FnOnce(bool) + Send + 'static>( pub fn show_message_dialog<R: Runtime, F: FnOnce(MessageDialogResult) + Send + 'static>(
dialog: MessageDialogBuilder<R>, dialog: MessageDialogBuilder<R>,
f: F, callback: F,
) { ) {
use rfd::MessageDialogResult; let f = move |res: rfd::MessageDialogResult| callback(res.into());
let ok_label = match &dialog.buttons {
MessageDialogButtons::OkCustom(ok) => Some(ok.clone()),
MessageDialogButtons::OkCancelCustom(ok, _) => Some(ok.clone()),
_ => None,
};
let f = move |res| {
f(match res {
MessageDialogResult::Ok | MessageDialogResult::Yes => true,
MessageDialogResult::Custom(s) => ok_label.map_or(s == OK, |ok_label| ok_label == s),
_ => false,
});
};
let handle = dialog.dialog.app_handle().to_owned(); let handle = dialog.dialog.app_handle().to_owned();
let _ = handle.run_on_main_thread(move || { let _ = handle.run_on_main_thread(move || {

@ -216,6 +216,7 @@ pub(crate) struct MessageDialogPayload<'a> {
message: &'a String, message: &'a String,
kind: &'a MessageDialogKind, kind: &'a MessageDialogKind,
ok_button_label: Option<&'a str>, ok_button_label: Option<&'a str>,
no_button_label: Option<&'a str>,
cancel_button_label: Option<&'a str>, cancel_button_label: Option<&'a str>,
} }
@ -238,13 +239,16 @@ impl<R: Runtime> MessageDialogBuilder<R> {
#[cfg(mobile)] #[cfg(mobile)]
pub(crate) fn payload(&self) -> MessageDialogPayload<'_> { pub(crate) fn payload(&self) -> MessageDialogPayload<'_> {
let (ok_button_label, cancel_button_label) = match &self.buttons { let (ok_button_label, no_button_label, cancel_button_label) = match &self.buttons {
MessageDialogButtons::Ok => (Some(OK), None), MessageDialogButtons::Ok => (Some(OK), None, None),
MessageDialogButtons::OkCancel => (Some(OK), Some(CANCEL)), MessageDialogButtons::OkCancel => (Some(OK), None, Some(CANCEL)),
MessageDialogButtons::YesNo => (Some(YES), Some(NO)), MessageDialogButtons::YesNo => (Some(YES), Some(NO), None),
MessageDialogButtons::OkCustom(ok) => (Some(ok.as_str()), Some(CANCEL)), MessageDialogButtons::OkCustom(ok) => (Some(ok.as_str()), None, None),
MessageDialogButtons::OkCancelCustom(ok, cancel) => { MessageDialogButtons::OkCancelCustom(ok, cancel) => {
(Some(ok.as_str()), Some(cancel.as_str())) (Some(ok.as_str()), None, Some(cancel.as_str()))
}
MessageDialogButtons::YesNoCancelCustom(yes, no, cancel) => {
(Some(yes.as_str()), Some(no.as_str()), Some(cancel.as_str()))
} }
}; };
MessageDialogPayload { MessageDialogPayload {
@ -252,6 +256,7 @@ impl<R: Runtime> MessageDialogBuilder<R> {
message: &self.message, message: &self.message,
kind: &self.kind, kind: &self.kind,
ok_button_label, ok_button_label,
no_button_label,
cancel_button_label, cancel_button_label,
} }
} }
@ -295,16 +300,55 @@ impl<R: Runtime> MessageDialogBuilder<R> {
} }
/// Shows a message dialog /// Shows a message dialog
///
/// Returns `true` if the user pressed the OK/Yes button,
pub fn show<F: FnOnce(bool) + Send + 'static>(self, f: F) { pub fn show<F: FnOnce(bool) + Send + 'static>(self, f: F) {
let ok_label = match &self.buttons {
MessageDialogButtons::OkCustom(ok) => Some(ok.clone()),
MessageDialogButtons::OkCancelCustom(ok, _) => Some(ok.clone()),
MessageDialogButtons::YesNoCancelCustom(yes, _, _) => Some(yes.clone()),
_ => None,
};
show_message_dialog(self, move |res| {
let sucess = match res {
MessageDialogResult::Ok | MessageDialogResult::Yes => true,
MessageDialogResult::Custom(s) => {
ok_label.map_or(s == OK, |ok_label| ok_label == s)
}
_ => false,
};
f(sucess)
})
}
/// Shows a message dialog and returns the button that was pressed.
///
/// Returns a [`MessageDialogResult`] enum that indicates which button was pressed.
pub fn show_with_result<F: FnOnce(MessageDialogResult) + Send + 'static>(self, f: F) {
show_message_dialog(self, f) show_message_dialog(self, f)
} }
/// Shows a message dialog. /// Shows a message dialog.
///
/// Returns `true` if the user pressed the OK/Yes button,
///
/// This is a blocking operation, /// This is a blocking operation,
/// and should *NOT* be used when running on the main thread context. /// and should *NOT* be used when running on the main thread context.
pub fn blocking_show(self) -> bool { pub fn blocking_show(self) -> bool {
blocking_fn!(self, show) blocking_fn!(self, show)
} }
/// Shows a message dialog and returns the button that was pressed.
///
/// Returns a [`MessageDialogResult`] enum that indicates which button was pressed.
///
/// This is a blocking operation,
/// and should *NOT* be used when running on the main thread context.
pub fn blocking_show_with_result(self) -> MessageDialogResult {
blocking_fn!(self, show_with_result)
}
} }
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub(crate) struct Filter { pub(crate) struct Filter {

@ -8,7 +8,7 @@ use tauri::{
AppHandle, Runtime, AppHandle, Runtime,
}; };
use crate::{FileDialogBuilder, FilePath, MessageDialogBuilder}; use crate::{FileDialogBuilder, FilePath, MessageDialogBuilder, MessageDialogResult};
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
const PLUGIN_IDENTIFIER: &str = "app.tauri.dialog"; const PLUGIN_IDENTIFIER: &str = "app.tauri.dialog";
@ -107,13 +107,11 @@ pub fn save_file<R: Runtime, F: FnOnce(Option<FilePath>) + Send + 'static>(
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct ShowMessageDialogResponse { struct ShowMessageDialogResponse {
#[allow(dead_code)] value: String,
cancelled: bool,
value: bool,
} }
/// Shows a message dialog /// Shows a message dialog
pub fn show_message_dialog<R: Runtime, F: FnOnce(bool) + Send + 'static>( pub fn show_message_dialog<R: Runtime, F: FnOnce(MessageDialogResult) + Send + 'static>(
dialog: MessageDialogBuilder<R>, dialog: MessageDialogBuilder<R>,
f: F, f: F,
) { ) {
@ -122,6 +120,8 @@ pub fn show_message_dialog<R: Runtime, F: FnOnce(bool) + Send + 'static>(
.dialog .dialog
.0 .0
.run_mobile_plugin::<ShowMessageDialogResponse>("showMessageDialog", dialog.payload()); .run_mobile_plugin::<ShowMessageDialogResponse>("showMessageDialog", dialog.payload());
f(res.map(|r| r.value).unwrap_or_default())
let res = res.map(|res| res.value.parse::<MessageDialogResult>().unwrap_or_default());
f(res.unwrap_or_default())
}); });
} }

@ -2,6 +2,8 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use std::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
/// Types of message, ask and confirm dialogs. /// Types of message, ask and confirm dialogs.
@ -52,7 +54,7 @@ impl Serialize for MessageDialogKind {
/// Set of button that will be displayed on the dialog /// Set of button that will be displayed on the dialog
#[non_exhaustive] #[non_exhaustive]
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub enum MessageDialogButtons { pub enum MessageDialogButtons {
#[default] #[default]
/// A single `Ok` button with OS default dialog text /// A single `Ok` button with OS default dialog text
@ -65,4 +67,44 @@ pub enum MessageDialogButtons {
OkCustom(String), OkCustom(String),
/// 2 buttons `Ok` and `Cancel` with custom texts /// 2 buttons `Ok` and `Cancel` with custom texts
OkCancelCustom(String, String), OkCancelCustom(String, String),
/// 3 buttons `Yes`, `No` and `Cancel` with custom texts
YesNoCancelCustom(String, String, String),
}
/// Result of a message dialog
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub enum MessageDialogResult {
Yes,
No,
Ok,
#[default]
Cancel,
Custom(String),
}
#[cfg(desktop)]
impl From<rfd::MessageDialogResult> for MessageDialogResult {
fn from(result: rfd::MessageDialogResult) -> Self {
match result {
rfd::MessageDialogResult::Yes => Self::Yes,
rfd::MessageDialogResult::No => Self::No,
rfd::MessageDialogResult::Ok => Self::Ok,
rfd::MessageDialogResult::Cancel => Self::Cancel,
rfd::MessageDialogResult::Custom(s) => Self::Custom(s),
}
}
}
impl FromStr for MessageDialogResult {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Yes" => Ok(Self::Yes),
"No" => Ok(Self::No),
"Ok" => Ok(Self::Ok),
"Cancel" => Ok(Self::Cancel),
_ => Ok(Self::Custom(s.to_string())),
}
}
} }

Loading…
Cancel
Save