mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-01-31 00:45:24 +01:00
feat: support message dialogs with 3 buttons (#2641)
* feat: support message dialogs with 3 buttons * change file * From<String> * untagged & YesNoCancel * revert package.json * Update plugins/dialog/src/desktop.rs Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com> * no optional * Update desktop.rs * Update plugins/dialog/src/models.rs Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com> * change to an enum * convert back into union * regen * update @since * map buttons for linux * enhance type * Add examples --------- Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com> Co-authored-by: Lucas Nogueira <lucas@tauri.app> Co-authored-by: Tony <legendmastertony@gmail.com>
This commit is contained in:
6
.changes/dialog-3-buttons.md
Normal file
6
.changes/dialog-3-buttons.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"dialog": "minor"
|
||||
"dialog-js": "minor"
|
||||
---
|
||||
|
||||
Add support for showing a message dialog with 3 buttons.
|
||||
@@ -44,6 +44,13 @@
|
||||
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() {
|
||||
open({
|
||||
title: "My wonderful open dialog",
|
||||
@@ -136,12 +143,17 @@
|
||||
<label for="dialog-directory">Directory</label>
|
||||
</div>
|
||||
<br />
|
||||
<button class="btn" id="open-dialog" on:click={openDialog}>Open dialog</button>
|
||||
<button class="btn" id="save-dialog" on:click={saveDialog}
|
||||
>Open save dialog</button
|
||||
>
|
||||
<button class="btn" id="prompt-dialog" on:click={prompt}>Prompt</button>
|
||||
<button class="btn" id="custom-prompt-dialog" on:click={promptCustom}
|
||||
>Prompt (custom)</button
|
||||
>
|
||||
<button class="btn" id="message-dialog" on:click={msg}>Message</button>
|
||||
|
||||
<div class="flex flex-wrap flex-col md:flex-row gap-2 children:flex-shrink-0">
|
||||
<button class="btn" id="open-dialog" on:click={openDialog}>Open dialog</button>
|
||||
<button class="btn" id="save-dialog" on:click={saveDialog}
|
||||
>Open save dialog</button
|
||||
>
|
||||
<button class="btn" id="prompt-dialog" on:click={prompt}>Prompt</button>
|
||||
<button class="btn" id="custom-prompt-dialog" on:click={promptCustom}
|
||||
>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
|
||||
lateinit var message: String
|
||||
var okButtonLabel: String? = null
|
||||
var noButtonLabel: String? = null
|
||||
var cancelButtonLabel: String? = null
|
||||
}
|
||||
|
||||
@@ -139,9 +140,8 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
|
||||
return
|
||||
}
|
||||
|
||||
val handler = { cancelled: Boolean, value: Boolean ->
|
||||
val handler = { value: String ->
|
||||
val ret = JSObject()
|
||||
ret.put("cancelled", cancelled)
|
||||
ret.put("value", value)
|
||||
invoke.resolve(ret)
|
||||
}
|
||||
@@ -153,24 +153,34 @@ class DialogPlugin(private val activity: Activity): Plugin(activity) {
|
||||
if (args.title != null) {
|
||||
builder.setTitle(args.title)
|
||||
}
|
||||
|
||||
val okButtonLabel = args.okButtonLabel ?: "Ok"
|
||||
|
||||
builder
|
||||
.setMessage(args.message)
|
||||
.setPositiveButton(
|
||||
args.okButtonLabel ?: "OK"
|
||||
) { dialog, _ ->
|
||||
.setPositiveButton(okButtonLabel) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
handler(false, true)
|
||||
handler(okButtonLabel)
|
||||
}
|
||||
.setOnCancelListener { dialog ->
|
||||
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) {
|
||||
builder.setNegativeButton( args.cancelButtonLabel) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
handler(false, false)
|
||||
handler(args.cancelButtonLabel!!)
|
||||
}
|
||||
}
|
||||
|
||||
val dialog = builder.create()
|
||||
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 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,80 @@ interface SaveDialogOptions {
|
||||
canCreateDirectories?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Default buttons for a message dialog.
|
||||
*
|
||||
* @since 2.4.0
|
||||
*/
|
||||
export type MessageDialogDefaultButtons =
|
||||
| 'Ok'
|
||||
| 'OkCancel'
|
||||
| 'YesNo'
|
||||
| 'YesNoCancel'
|
||||
|
||||
/** All possible button keys. */
|
||||
type ButtonKey = 'ok' | 'cancel' | 'yes' | 'no'
|
||||
|
||||
/** Ban everything except a set of keys. */
|
||||
type BanExcept<Allowed extends ButtonKey> = Partial<
|
||||
Record<Exclude<ButtonKey, Allowed>, never>
|
||||
>
|
||||
|
||||
/**
|
||||
* The Yes, No and Cancel buttons of a message dialog.
|
||||
*
|
||||
* @since 2.4.0
|
||||
*/
|
||||
export type MessageDialogButtonsYesNoCancel = {
|
||||
/** The Yes button. */
|
||||
yes: string
|
||||
/** The No button. */
|
||||
no: string
|
||||
/** The Cancel button. */
|
||||
cancel: string
|
||||
} & BanExcept<'yes' | 'no' | 'cancel'>
|
||||
|
||||
/**
|
||||
* The Ok and Cancel buttons of a message dialog.
|
||||
*
|
||||
* @since 2.4.0
|
||||
*/
|
||||
export type MessageDialogButtonsOkCancel = {
|
||||
/** The Ok button. */
|
||||
ok: string
|
||||
/** The Cancel button. */
|
||||
cancel: string
|
||||
} & BanExcept<'ok' | 'cancel'>
|
||||
|
||||
/**
|
||||
* The Ok button of a message dialog.
|
||||
*
|
||||
* @since 2.4.0
|
||||
*/
|
||||
export type MessageDialogButtonsOk = {
|
||||
/** The Ok button. */
|
||||
ok: string
|
||||
} & BanExcept<'ok'>
|
||||
|
||||
/**
|
||||
* Custom buttons for a message dialog.
|
||||
*
|
||||
* @since 2.4.0
|
||||
*/
|
||||
export type MessageDialogCustomButtons =
|
||||
| MessageDialogButtonsYesNoCancel
|
||||
| MessageDialogButtonsOkCancel
|
||||
| MessageDialogButtonsOk
|
||||
|
||||
/**
|
||||
* The buttons of a message dialog.
|
||||
*
|
||||
* @since 2.4.0
|
||||
*/
|
||||
export type MessageDialogButtons =
|
||||
| MessageDialogDefaultButtons
|
||||
| MessageDialogCustomButtons
|
||||
|
||||
/**
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@@ -85,8 +159,58 @@ interface MessageDialogOptions {
|
||||
title?: string
|
||||
/** The kind of the dialog. Defaults to `info`. */
|
||||
kind?: 'info' | 'warning' | 'error'
|
||||
/** The label of the confirm button. */
|
||||
/**
|
||||
* The label of the Ok button.
|
||||
*
|
||||
* @deprecated Use {@linkcode MessageDialogOptions.buttons} instead.
|
||||
*/
|
||||
okLabel?: string
|
||||
/**
|
||||
* The buttons of the dialog.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* // Use system default buttons texts
|
||||
* await message('Hello World!', { buttons: 'Ok' })
|
||||
* await message('Hello World!', { buttons: 'OkCancel' })
|
||||
*
|
||||
* // Or with custom button texts
|
||||
* await message('Hello World!', { buttons: { ok: 'Yes!' } })
|
||||
* await message('Take on the task?', {
|
||||
* buttons: { ok: 'Accept', cancel: 'Cancel' }
|
||||
* })
|
||||
* await message('Show the file content?', {
|
||||
* buttons: { yes: 'Show content', no: 'Show in folder', cancel: 'Cancel' }
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* @since 2.4.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 {
|
||||
@@ -202,6 +326,16 @@ async function save(options: SaveDialogOptions = {}): Promise<string | null> {
|
||||
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.4.0
|
||||
*/
|
||||
export type MessageDialogResult = 'Yes' | 'No' | 'Ok' | 'Cancel' | (string & {})
|
||||
|
||||
/**
|
||||
* Shows a message dialog with an `Ok` button.
|
||||
* @example
|
||||
@@ -222,13 +356,15 @@ async function save(options: SaveDialogOptions = {}): Promise<string | null> {
|
||||
async function message(
|
||||
message: string,
|
||||
options?: string | MessageDialogOptions
|
||||
): Promise<void> {
|
||||
): Promise<MessageDialogResult> {
|
||||
const opts = typeof options === 'string' ? { title: options } : options
|
||||
await invoke('plugin:dialog|message', {
|
||||
|
||||
return invoke<MessageDialogResult>('plugin:dialog|message', {
|
||||
message: message.toString(),
|
||||
title: opts?.title?.toString(),
|
||||
kind: opts?.kind,
|
||||
okButtonLabel: opts?.okLabel?.toString()
|
||||
okButtonLabel: opts?.okLabel?.toString(),
|
||||
buttons: buttonsToRust(opts?.buttons)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ struct MessageDialogOptions: Decodable {
|
||||
var title: String?
|
||||
let message: String
|
||||
var okButtonLabel: String?
|
||||
var noButtonLabel: String?
|
||||
var cancelButtonLabel: String?
|
||||
}
|
||||
|
||||
@@ -200,36 +201,38 @@ class DialogPlugin: Plugin {
|
||||
let alert = UIAlertController(
|
||||
title: args.title, message: args.message, preferredStyle: UIAlertController.Style.alert)
|
||||
|
||||
let cancelButtonLabel = args.cancelButtonLabel ?? ""
|
||||
if !cancelButtonLabel.isEmpty {
|
||||
if let cancelButtonLabel = args.cancelButtonLabel {
|
||||
alert.addAction(
|
||||
UIAlertAction(
|
||||
title: cancelButtonLabel, style: UIAlertAction.Style.default,
|
||||
handler: { (_) -> Void in
|
||||
Logger.error("cancel")
|
||||
|
||||
invoke.resolve([
|
||||
"value": false,
|
||||
"cancelled": false,
|
||||
])
|
||||
}))
|
||||
invoke.resolve(["value": cancelButtonLabel])
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
let okButtonLabel = args.okButtonLabel ?? (cancelButtonLabel.isEmpty ? "OK" : "")
|
||||
if !okButtonLabel.isEmpty {
|
||||
if let noButtonLabel = args.noButtonLabel {
|
||||
alert.addAction(
|
||||
UIAlertAction(
|
||||
title: okButtonLabel, style: UIAlertAction.Style.default,
|
||||
title: noButtonLabel, style: UIAlertAction.Style.default,
|
||||
handler: { (_) -> Void in
|
||||
Logger.error("ok")
|
||||
|
||||
invoke.resolve([
|
||||
"value": true,
|
||||
"cancelled": false,
|
||||
])
|
||||
}))
|
||||
invoke.resolve(["value": noButtonLabel])
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ use tauri::{command, Manager, Runtime, State, Window};
|
||||
use tauri_plugin_fs::FsExt;
|
||||
|
||||
use crate::{
|
||||
Dialog, FileDialogBuilder, FilePath, MessageDialogButtons, MessageDialogKind, Result, CANCEL,
|
||||
NO, OK, YES,
|
||||
Dialog, FileDialogBuilder, FilePath, MessageDialogBuilder, MessageDialogButtons,
|
||||
MessageDialogKind, MessageDialogResult, Result, CANCEL, NO, OK, YES,
|
||||
};
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -248,7 +248,7 @@ fn message_dialog<R: Runtime>(
|
||||
message: String,
|
||||
kind: Option<MessageDialogKind>,
|
||||
buttons: MessageDialogButtons,
|
||||
) -> bool {
|
||||
) -> MessageDialogBuilder<R> {
|
||||
let mut builder = dialog.message(message);
|
||||
|
||||
builder = builder.buttons(buttons);
|
||||
@@ -266,7 +266,7 @@ fn message_dialog<R: Runtime>(
|
||||
builder = builder.kind(kind);
|
||||
}
|
||||
|
||||
builder.blocking_show()
|
||||
builder
|
||||
}
|
||||
|
||||
#[command]
|
||||
@@ -277,19 +277,15 @@ pub(crate) async fn message<R: Runtime>(
|
||||
message: String,
|
||||
kind: Option<MessageDialogKind>,
|
||||
ok_button_label: Option<String>,
|
||||
) -> Result<bool> {
|
||||
Ok(message_dialog(
|
||||
window,
|
||||
dialog,
|
||||
title,
|
||||
message,
|
||||
kind,
|
||||
if let Some(ok_button_label) = ok_button_label {
|
||||
MessageDialogButtons::OkCustom(ok_button_label)
|
||||
} else {
|
||||
MessageDialogButtons::Ok
|
||||
},
|
||||
))
|
||||
buttons: Option<MessageDialogButtons>,
|
||||
) -> Result<MessageDialogResult> {
|
||||
let buttons = buttons.unwrap_or(if let Some(ok_button_label) = ok_button_label {
|
||||
MessageDialogButtons::OkCustom(ok_button_label)
|
||||
} else {
|
||||
MessageDialogButtons::Ok
|
||||
});
|
||||
|
||||
Ok(message_dialog(window, dialog, title, message, kind, buttons).blocking_show_with_result())
|
||||
}
|
||||
|
||||
#[command]
|
||||
@@ -302,7 +298,7 @@ pub(crate) async fn ask<R: Runtime>(
|
||||
yes_button_label: Option<String>,
|
||||
no_button_label: Option<String>,
|
||||
) -> Result<bool> {
|
||||
Ok(message_dialog(
|
||||
let dialog = message_dialog(
|
||||
window,
|
||||
dialog,
|
||||
title,
|
||||
@@ -318,7 +314,9 @@ pub(crate) async fn ask<R: Runtime>(
|
||||
} else {
|
||||
MessageDialogButtons::YesNo
|
||||
},
|
||||
))
|
||||
);
|
||||
|
||||
Ok(dialog.blocking_show())
|
||||
}
|
||||
|
||||
#[command]
|
||||
@@ -331,7 +329,7 @@ pub(crate) async fn confirm<R: Runtime>(
|
||||
ok_button_label: Option<String>,
|
||||
cancel_button_label: Option<String>,
|
||||
) -> Result<bool> {
|
||||
Ok(message_dialog(
|
||||
let dialog = message_dialog(
|
||||
window,
|
||||
dialog,
|
||||
title,
|
||||
@@ -347,5 +345,7 @@ pub(crate) async fn confirm<R: Runtime>(
|
||||
} else {
|
||||
MessageDialogButtons::OkCancel
|
||||
},
|
||||
))
|
||||
);
|
||||
|
||||
Ok(dialog.blocking_show())
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ use rfd::{AsyncFileDialog, AsyncMessageDialog};
|
||||
use serde::de::DeserializeOwned;
|
||||
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>(
|
||||
app: &AppHandle<R>,
|
||||
@@ -115,6 +115,10 @@ impl From<MessageDialogButtons> for rfd::MessageButtons {
|
||||
MessageDialogButtons::YesNo => Self::YesNo,
|
||||
MessageDialogButtons::OkCustom(ok) => Self::OkCustom(ok),
|
||||
MessageDialogButtons::OkCancelCustom(ok, cancel) => Self::OkCancelCustom(ok, cancel),
|
||||
MessageDialogButtons::YesNoCancel => Self::YesNoCancel,
|
||||
MessageDialogButtons::YesNoCancelCustom(yes, no, cancel) => {
|
||||
Self::YesNoCancelCustom(yes, no, cancel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -208,28 +212,46 @@ pub fn save_file<R: Runtime, F: FnOnce(Option<FilePath>) + Send + 'static>(
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
f: F,
|
||||
callback: F,
|
||||
) {
|
||||
use rfd::MessageDialogResult;
|
||||
|
||||
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 f = move |res: rfd::MessageDialogResult| callback(res.into());
|
||||
|
||||
let handle = dialog.dialog.app_handle().to_owned();
|
||||
let _ = handle.run_on_main_thread(move || {
|
||||
let buttons = dialog.buttons.clone();
|
||||
let dialog = AsyncMessageDialog::from(dialog).show();
|
||||
std::thread::spawn(move || f(tauri::async_runtime::block_on(dialog)));
|
||||
std::thread::spawn(move || {
|
||||
let result = tauri::async_runtime::block_on(dialog);
|
||||
// on Linux rfd does not return rfd::MessageDialogResult::Custom, so we must map manually
|
||||
let result = match (result, buttons) {
|
||||
(rfd::MessageDialogResult::Ok, MessageDialogButtons::OkCustom(s)) => {
|
||||
rfd::MessageDialogResult::Custom(s)
|
||||
}
|
||||
(
|
||||
rfd::MessageDialogResult::Ok,
|
||||
MessageDialogButtons::OkCancelCustom(ok, _cancel),
|
||||
) => rfd::MessageDialogResult::Custom(ok),
|
||||
(
|
||||
rfd::MessageDialogResult::Cancel,
|
||||
MessageDialogButtons::OkCancelCustom(_ok, cancel),
|
||||
) => rfd::MessageDialogResult::Custom(cancel),
|
||||
(
|
||||
rfd::MessageDialogResult::Yes,
|
||||
MessageDialogButtons::YesNoCancelCustom(yes, _no, _cancel),
|
||||
) => rfd::MessageDialogResult::Custom(yes),
|
||||
(
|
||||
rfd::MessageDialogResult::No,
|
||||
MessageDialogButtons::YesNoCancelCustom(_yes, no, _cancel),
|
||||
) => rfd::MessageDialogResult::Custom(no),
|
||||
(
|
||||
rfd::MessageDialogResult::Cancel,
|
||||
MessageDialogButtons::YesNoCancelCustom(_yes, _no, cancel),
|
||||
) => rfd::MessageDialogResult::Custom(cancel),
|
||||
(result, _) => result,
|
||||
};
|
||||
f(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -216,6 +216,7 @@ pub(crate) struct MessageDialogPayload<'a> {
|
||||
message: &'a String,
|
||||
kind: &'a MessageDialogKind,
|
||||
ok_button_label: Option<&'a str>,
|
||||
no_button_label: Option<&'a str>,
|
||||
cancel_button_label: Option<&'a str>,
|
||||
}
|
||||
|
||||
@@ -238,13 +239,17 @@ impl<R: Runtime> MessageDialogBuilder<R> {
|
||||
|
||||
#[cfg(mobile)]
|
||||
pub(crate) fn payload(&self) -> MessageDialogPayload<'_> {
|
||||
let (ok_button_label, cancel_button_label) = match &self.buttons {
|
||||
MessageDialogButtons::Ok => (Some(OK), None),
|
||||
MessageDialogButtons::OkCancel => (Some(OK), Some(CANCEL)),
|
||||
MessageDialogButtons::YesNo => (Some(YES), Some(NO)),
|
||||
MessageDialogButtons::OkCustom(ok) => (Some(ok.as_str()), Some(CANCEL)),
|
||||
let (ok_button_label, no_button_label, cancel_button_label) = match &self.buttons {
|
||||
MessageDialogButtons::Ok => (Some(OK), None, None),
|
||||
MessageDialogButtons::OkCancel => (Some(OK), None, Some(CANCEL)),
|
||||
MessageDialogButtons::YesNo => (Some(YES), Some(NO), None),
|
||||
MessageDialogButtons::YesNoCancel => (Some(YES), Some(NO), Some(CANCEL)),
|
||||
MessageDialogButtons::OkCustom(ok) => (Some(ok.as_str()), None, None),
|
||||
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 {
|
||||
@@ -252,6 +257,7 @@ impl<R: Runtime> MessageDialogBuilder<R> {
|
||||
message: &self.message,
|
||||
kind: &self.kind,
|
||||
ok_button_label,
|
||||
no_button_label,
|
||||
cancel_button_label,
|
||||
}
|
||||
}
|
||||
@@ -295,16 +301,55 @@ impl<R: Runtime> MessageDialogBuilder<R> {
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
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)
|
||||
}
|
||||
|
||||
/// Shows a message dialog.
|
||||
///
|
||||
/// Returns `true` if the user pressed the OK/Yes button,
|
||||
///
|
||||
/// This is a blocking operation,
|
||||
/// and should *NOT* be used when running on the main thread context.
|
||||
pub fn blocking_show(self) -> bool {
|
||||
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)]
|
||||
pub(crate) struct Filter {
|
||||
|
||||
@@ -8,7 +8,7 @@ use tauri::{
|
||||
AppHandle, Runtime,
|
||||
};
|
||||
|
||||
use crate::{FileDialogBuilder, FilePath, MessageDialogBuilder};
|
||||
use crate::{FileDialogBuilder, FilePath, MessageDialogBuilder, MessageDialogResult};
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
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)]
|
||||
struct ShowMessageDialogResponse {
|
||||
#[allow(dead_code)]
|
||||
cancelled: bool,
|
||||
value: bool,
|
||||
value: String,
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
f: F,
|
||||
) {
|
||||
@@ -122,6 +120,8 @@ pub fn show_message_dialog<R: Runtime, F: FnOnce(bool) + Send + 'static>(
|
||||
.dialog
|
||||
.0
|
||||
.run_mobile_plugin::<ShowMessageDialogResponse>("showMessageDialog", dialog.payload());
|
||||
f(res.map(|r| r.value).unwrap_or_default())
|
||||
|
||||
let res = res.map(|res| res.value.into());
|
||||
f(res.unwrap_or_default())
|
||||
});
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ impl Serialize for MessageDialogKind {
|
||||
|
||||
/// Set of button that will be displayed on the dialog
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
pub enum MessageDialogButtons {
|
||||
#[default]
|
||||
/// A single `Ok` button with OS default dialog text
|
||||
@@ -61,8 +61,49 @@ pub enum MessageDialogButtons {
|
||||
OkCancel,
|
||||
/// 2 buttons `Yes` and `No` with OS default dialog texts
|
||||
YesNo,
|
||||
/// 3 buttons `Yes`, `No` and `Cancel` with OS default dialog texts
|
||||
YesNoCancel,
|
||||
/// A single `Ok` button with custom text
|
||||
OkCustom(String),
|
||||
/// 2 buttons `Ok` and `Cancel` with custom texts
|
||||
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,
|
||||
#[serde(untagged)]
|
||||
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 From<String> for MessageDialogResult {
|
||||
fn from(value: String) -> Self {
|
||||
match value.as_str() {
|
||||
"Yes" => Self::Yes,
|
||||
"No" => Self::No,
|
||||
"Ok" => Self::Ok,
|
||||
"Cancel" => Self::Cancel,
|
||||
_ => Self::Custom(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user