Skip to content

feat: support message dialogs with 3 buttons #2641

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: v2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/dialog-3-buttons.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"dialog": "minor"
"dialog-js": "minor"
---

Add support for showing a message dialog with 3 buttons.
30 changes: 21 additions & 9 deletions examples/api/src/views/Dialog.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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>
26 changes: 18 additions & 8 deletions plugins/dialog/android/src/main/java/DialogPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -169,9 +170,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)
}
Expand All @@ -183,24 +183,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()
}
Expand Down
2 changes: 1 addition & 1 deletion plugins/dialog/api-iife.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

119 changes: 115 additions & 4 deletions plugins/dialog/guest-js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,72 @@ interface SaveDialogOptions {
canCreateDirectories?: boolean
}

/**
* Default buttons for a message dialog.
*
* @since 2.3.0
*/
export type MessageDialogDefaultButtons =
| 'Ok'
| 'OkCancel'
| 'YesNo'
| 'YesNoCancel'

/**
* 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
}
Comment on lines +96 to +125
Copy link
Contributor

@Legend-Master Legend-Master Apr 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a small suggestion, maybe we can change these to something like this, so typescript doesn't hint ok and yes at the same type

Suggested change
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
}
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. */
yes: string
/** The Cancel button. */
cancel: string
}
/**
* The Ok button of a message dialog.
*
* @since 2.3.0
*/
export type MessageDialogButtonsOk = {
/** The Ok button. */
yes: 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
*/
Expand All @@ -85,8 +151,41 @@ 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.
*
* @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 {
Expand Down Expand Up @@ -202,6 +301,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.3.0
*/
export type MessageDialogResult = 'Yes' | 'No' | 'Ok' | 'Cancel' | (string & {})

/**
* Shows a message dialog with an `Ok` button.
* @example
Expand All @@ -222,13 +331,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)
})
}

Expand Down
41 changes: 22 additions & 19 deletions plugins/dialog/ios/Sources/DialogPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ struct MessageDialogOptions: Decodable {
var title: String?
let message: String
var okButtonLabel: String?
var noButtonLabel: String?
var cancelButtonLabel: String?
}

Expand Down Expand Up @@ -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)
}
}
Expand Down
Loading
Loading