AlertDialog
AlertDialog is a modal dialog component that interrupts the user with important content and expects a response. It is built on top of the Dialog component with opinionated defaults and a simplified API.
#Differences from Dialog
AlertDialog provides these defaults on top of Dialog:
- Not overlay closable by default (can be changed with
overlay_closable(true)) - No close button by default (can be changed with
close_button(true)) - Footer buttons are center-aligned (Dialog uses right-alignment)
- Simplified API focused on alert and confirmation scenarios
#Import
use gpui_kit::component::dialog::{AlertDialog, DialogAction, DialogClose};
use gpui_kit::component::WindowExt;// Installed in ui/components. That directory is on the Slint include path, so import by file name:
import { AlertDialog } from "alert-dialog.slint";use iced::keyboard;
use iced::widget::{Stack, column, container, mouse_area, row, scrollable, svg, text};
use iced::{Alignment, Element, Length, Subscription, Task, Theme};#Usage
#Default
// The default confirmation: a title, a description and a centered footer
// whose cancel action comes first and whose destructive one comes last.
let default = body_box(
column![
open_button("Discard Draft", slot::DEFAULT),
muted_line("A centered footer: Cancel first, then the destructive action."),
]
.spacing(8.0),
);
let default_dialog = Dialog::new()
.open(app.open[slot::DEFAULT])
.close_button(false)
.title("Discard unsaved changes?")
.description("Your edits since the last save will be permanently lost.")
.footer(
DialogFooter::new()
.centered(true)
.child(close_button("Cancel", slot::DEFAULT))
.child(
Button::new("Discard")
.variant(button::Variant::Danger)
.on_press(Message::Opened {
slot: slot::DEFAULT,
open: false,
}),
),
)
.on_open_change(|open| Message::Opened {
slot: slot::DEFAULT,
open,
});Like Dialog, you need to set up your application’s root view to render the dialog layer. See Dialog documentation for details.
AlertDialog::new(cx)
.trigger(Button::new("info-alert").outline().label("Discard Draft"))
.on_ok(|_, window, cx| {
window.push_notification("Draft discarded", cx);
true
})
.content(|content, _, cx| {
content
.child(
DialogHeader::new()
.child(DialogTitle::new().child("Discard unsaved changes?"))
.child(DialogDescription::new().child(
"Your edits since the last save will be permanently lost.",
)),
)
.child(
DialogFooter::new()
.child(DialogClose::new().child(
Button::new("cancel").outline().label("Cancel"),
))
.child(DialogAction::new().child(
Button::new("ok").label("Discard").danger(),
)),
)
})row: root.row-of(0);
col: root.col-of(0);
Preview {
label: "Discard Draft";
clicked => {
root.discard-open = true;
}
Dialog {
width: 100%;
height: 100%;
open <=> root.discard-open;
show-close: false;
title: "Discard unsaved changes?";
description: "Your edits since the last save will be permanently lost.";
DialogFooter {
centered: true;
Button {
text: "Cancel";
outline: true;
clicked => {
root.discard-open = false;
}
}
Button {
text: "Discard";
variant: ButtonVariant.danger;
clicked => {
root.discard-open = false;
}
}
}
}
}#Delete file
// The panel's body is the caller's, so a leading danger glyph and the two
// lines beside it are an ordinary row placed in `content`.
let delete = body_box(
column![
open_button("Delete File", slot::DELETE),
muted_line("A danger glyph leads the title and the line beside it."),
]
.spacing(8.0),
);
let delete_dialog = Dialog::new()
.open(app.open[slot::DELETE])
.close_button(false)
.content(icon_row(
INFO_XML,
theme::Status::Danger,
20.0,
"Delete File",
"Are you sure you want to delete this file? This action cannot be undone.",
))
.footer(
DialogFooter::new()
.centered(true)
.child(close_button("Cancel", slot::DELETE))
.child(
Button::new("Delete")
.variant(button::Variant::Danger)
.on_press(Message::Opened {
slot: slot::DELETE,
open: false,
}),
),
)
.on_open_change(|open| Message::Opened {
slot: slot::DELETE,
open,
});Using imperative API:
Button::new("delete")
.danger()
.label("Delete")
.on_click(|_, window, cx| {
window.open_alert_dialog(cx, |alert, _, _| {
alert
.title("Delete File?")
.description("This action cannot be undone.")
.confirm()
.ok_text("Delete")
.ok_variant(ButtonVariant::Danger)
.on_ok(|_, window, cx| {
// Perform delete operation
window.push_notification("File deleted", cx);
true
})
});
})row: root.row-of(1);
col: root.col-of(1);
Preview {
label: "Delete File";
clicked => {
root.delete-file-open = true;
}
Dialog {
width: 100%;
height: 100%;
open <=> root.delete-file-open;
show-close: false;
HorizontalLayout {
spacing: 12px;
Icon {
name: "info";
size: 20px;
color: Theme.danger;
}
VerticalLayout {
spacing: 4px;
horizontal-stretch: 1;
Text {
text: "Delete File";
color: Theme.foreground;
font-size: Theme.font-size-lg;
font-weight: 600;
}
Text {
text: "Are you sure you want to delete this file? This action cannot be undone.";
color: Theme.muted-foreground;
font-size: Theme.font-size-sm;
wrap: word-wrap;
}
}
}
DialogFooter {
centered: true;
Button {
text: "Cancel";
clicked => {
root.delete-file-open = false;
}
}
Button {
text: "Delete";
variant: ButtonVariant.danger;
clicked => {
root.delete-file-open = false;
}
}
}
}
}Or using declarative API with DialogAction/DialogClose:
AlertDialog::new(cx)
.trigger(Button::new("delete").danger().label("Delete"))
.on_ok(|_, window, cx| {
window.push_notification("File deleted", cx);
true
})
.content(|content, _, cx| {
content
.child(
DialogHeader::new()
.child(DialogTitle::new().child("Delete File?"))
.child(DialogDescription::new().child("This action cannot be undone."))
)
.child(
DialogFooter::new()
.child(
DialogClose::new().child(
Button::new("cancel").outline().label("Cancel")
)
)
.child(
DialogAction::new().child(
Button::new("delete-confirm").danger().label("Delete")
)
)
)
})#Icon
// The centered prompt: the glyph, the title and the line stacked, and the
// two full-width actions stacked under them. A `footer` accepts any element,
// so it is a column here rather than the usual row.
let icon = body_box(
column![
open_button("Request Permission", slot::ICON),
muted_line("Everything is centered, and the actions are stacked full width."),
]
.spacing(8.0),
);
let icon_dialog =
Dialog::new()
.open(app.open[slot::ICON])
.close_button(false)
.content(
column![
container(
svg(svg::Handle::from_memory(TRIANGLE_ALERT))
.width(40.0)
.height(40.0)
.style(|theme: &Theme, _status| svg::Style {
color: Some(Tokens::of(theme).warning),
})
)
.width(Length::Fill)
.center_x(Length::Fill),
container(
text("Network Permission Required")
.size(theme::FONT_SIZE_LG)
.font(iced::Font {
weight: iced::font::Weight::Semibold,
..iced::Font::DEFAULT
})
)
.width(Length::Fill)
.center_x(Length::Fill),
container(
text("We need your permission to access the network. Allow access to continue.")
.size(theme::FONT_SIZE_SM)
.style(|theme: &Theme| text::Style {
color: Some(Tokens::of(theme).muted_foreground),
})
)
.width(Length::Fill)
.center_x(Length::Fill),
]
.spacing(8.0),
)
.footer(
DialogFooter::new()
.centered(true)
.child(close_button("Cancel", slot::DELETE))
.child(Button::new("Delete").on_press(Message::Opened {
slot: slot::DELETE,
open: false,
})),
)
.on_open_change(|open| Message::Opened {
slot: slot::ICON,
open,
});Using icon in declarative API:
use gpui_kit::component::{Icon, IconName, ActiveTheme};
AlertDialog::new(cx)
.w(px(320.))
.trigger(Button::new("permission").outline().label("Request Permission"))
.on_ok(|_, window, cx| {
window.push_notification("Permission granted", cx);
true
})
.content(|content, _, cx| {
content
.child(
DialogHeader::new()
.items_center()
.child(
Icon::new(IconName::TriangleAlert)
.size_10()
.text_color(cx.theme().warning)
)
.child(DialogTitle::new().child("Network Permission Required"))
.child(DialogDescription::new().child(
"We need your permission to access the network to provide better services."
))
)
.child(
DialogFooter::new()
.v_flex()
.child(
DialogAction::new().child(
Button::new("allow").w_full().primary().label("Allow")
)
)
.child(
DialogClose::new().child(
Button::new("deny").w_full().outline().label("Don't Allow")
)
)
)
})row: root.row-of(2);
col: root.col-of(2);
Preview {
label: "Request Permission";
clicked => {
root.permission-open = true;
}
Dialog {
width: 100%;
height: 100%;
open <=> root.permission-open;
show-close: false;
VerticalLayout {
alignment: center;
spacing: 8px;
Icon {
name: "triangle-alert";
size: 40px;
color: Theme.warning;
}
Text {
text: "Network Permission Required";
color: Theme.foreground;
font-size: Theme.font-size-lg;
font-weight: 600;
horizontal-alignment: center;
wrap: word-wrap;
}
Text {
text: "We need your permission to access the network. Allow access to continue.";
color: Theme.muted-foreground;
font-size: Theme.font-size-sm;
horizontal-alignment: center;
wrap: word-wrap;
}
}
VerticalLayout {
spacing: 8px;
Button {
text: "Allow";
variant: ButtonVariant.primary;
horizontal-stretch: 1;
clicked => {
root.permission-open = false;
}
}
Button {
text: "Deny";
outline: true;
horizontal-stretch: 1;
clicked => {
root.permission-open = false;
}
}
}
}
}Using icon in imperative API:
window.open_alert_dialog(cx, |alert, _, cx| {
alert
.title("Warning")
.description("This action requires confirmation.")
.icon(
Icon::new(IconName::AlertTriangle)
.size_8()
.text_color(cx.theme().warning)
)
})#Destructive
AlertDialog::new(cx)
.trigger(
Button::new("delete-account")
.outline()
.danger()
.label("Delete Account")
)
.on_ok(|_, window, cx| {
window.push_notification("Account deletion initiated", cx);
true
})
.content(|content, _, _| {
content
.child(
DialogHeader::new()
.child(DialogTitle::new().child("Delete Account"))
.child(DialogDescription::new().child(
"This will permanently delete your account \
and all associated data. This action cannot be undone."
))
)
.child(
DialogFooter::new()
.child(
DialogClose::new().child(
Button::new("cancel").flex_1().outline().label("Cancel")
)
)
.child(
DialogAction::new().child(
Button::new("delete")
.flex_1()
.outline()
.danger()
.label("Delete Forever")
)
)
)
})row: root.row-of(3);
col: root.col-of(3);
Preview {
label: "Delete Account";
variant: ButtonVariant.danger;
clicked => {
root.account-open = true;
}
Dialog {
width: 100%;
height: 100%;
open <=> root.account-open;
show-close: false;
title: "Delete Account";
description: "This will permanently delete your account and all associated data. This action cannot be undone.";
DialogFooter {
centered: true;
Button {
text: "Cancel";
outline: true;
horizontal-stretch: 1;
clicked => {
root.account-open = false;
}
}
Button {
text: "Delete";
outline: true;
variant: ButtonVariant.danger;
horizontal-stretch: 1;
clicked => {
root.account-open = false;
}
}
}
}
}// Both actions are outlined and share the row, which is the Slint example's
// `horizontal-stretch` on each button.
let destructive = body_box(
column![
open_button("Delete Account", slot::DESTRUCTIVE),
muted_line("Two outlined actions share the row."),
]
.spacing(8.0),
);
let destructive_dialog = Dialog::new()
.open(app.open[slot::DESTRUCTIVE])
.close_button(false)
.title("Delete Account")
.description(
"This will permanently delete your account and all associated data. \
This action cannot be undone.",
)
.footer(
DialogFooter::new()
.centered(true)
.child(
Button::new("Cancel")
.outline()
.width(Length::Fill)
.on_press(Message::Opened {
slot: slot::DESTRUCTIVE,
open: false,
}),
)
.child(
Button::new("Delete")
.outline()
.variant(button::Variant::Danger)
.width(Length::Fill)
.on_press(Message::Opened {
slot: slot::DESTRUCTIVE,
open: false,
}),
),
)
.on_open_change(|open| Message::Opened {
slot: slot::DESTRUCTIVE,
open,
});#Without title
// The component itself: `AlertDialog` builds the centered footer from its
// two labels, and an empty title simply draws no title.
let untitled = body_box(
column![
open_button("Continue without Title", slot::UNTITLED),
muted_line("`AlertDialog` with a description and no title."),
]
.spacing(8.0),
);
let untitled_dialog = AlertDialog::new()
.open(app.open[slot::UNTITLED])
.description("Continue with this action?")
.confirm_text("Continue")
.on_confirm(Message::Opened {
slot: slot::UNTITLED,
open: false,
})
.on_open_change(|open| Message::Opened {
slot: slot::UNTITLED,
open,
});Create a fully declarative AlertDialog using trigger and content:
use gpui_kit::component::dialog::{AlertDialog, DialogHeader, DialogTitle, DialogDescription, DialogFooter};
AlertDialog::new(cx)
.trigger(
Button::new("show-alert")
.outline()
.label("Show Alert")
)
.content(|content, _, cx| {
content
.child(
DialogHeader::new()
.child(DialogTitle::new().child("Are you absolutely sure?"))
.child(DialogDescription::new().child(
"This action cannot be undone. \
This will permanently delete your account from our servers."
))
)
.child(
DialogFooter::new()
.child(
Button::new("cancel")
.outline()
.label("Cancel")
.on_click(|_, window, cx| {
window.close_dialog(cx);
})
)
.child(
Button::new("ok")
.primary()
.label("Continue")
.on_click(|_, window, cx| {
window.push_notification("Confirmed", cx);
window.close_dialog(cx);
})
)
)
})row: root.row-of(4);
col: root.col-of(4);
Preview {
label: "Continue without Title";
clicked => {
root.untitled-open = true;
}
AlertDialog {
width: 100%;
height: 100%;
open <=> root.untitled-open;
description: "Continue with this action?";
confirm-text: "Continue";
}
}#Custom footer
// A single full-width action where the footer would be: the panel's body is
// the caller's, so the "footer" is just the last child.
let footer = body_box(
column![
open_button("Show Session Expiry", slot::FOOTER),
muted_line("A single full-width action stands in for the footer."),
]
.spacing(8.0),
);
let footer_dialog = Dialog::new()
.open(app.open[slot::FOOTER])
.close_button(false)
.title("Session Expired")
.description("Your session has expired due to inactivity. Please log in again to continue.")
.content(
Button::new("Sign in")
.variant(button::Variant::Primary)
.width(Length::Fill)
.on_press(Message::Opened {
slot: slot::FOOTER,
open: false,
}),
)
.on_open_change(|open| Message::Opened {
slot: slot::FOOTER,
open,
});Set the button text and variant directly on the dialog:
use gpui_kit::component::button::ButtonVariant;
window.open_alert_dialog(cx, |alert, _, _| {
alert
.title("Delete Account")
.description("This will permanently delete your account and all associated data.")
.confirm()
.ok_text("Delete")
.ok_variant(ButtonVariant::Danger)
.cancel_text("Keep")
.on_ok(|_, window, cx| {
window.push_notification("Account deleted", cx);
true
})
})row: root.row-of(5);
col: root.col-of(5);
Preview {
label: "Show Session Expiry";
clicked => {
root.session-open = true;
}
Dialog {
width: 100%;
height: 100%;
open <=> root.session-open;
show-close: false;
title: "Session Expired";
description: "Your session has expired due to inactivity. Please log in again to continue.";
Button {
text: "Sign in";
variant: ButtonVariant.primary;
horizontal-stretch: 1;
clicked => {
root.session-open = false;
}
}
}
}button_props takes the same properties as one value, for a configuration you
want to build up or pass around. It overrides only the fields the value sets,
so everything the dialog already carries — the Cancel button confirm asked
for, a callback an earlier on_ok installed — survives, whatever the call
order:
use gpui_kit::component::dialog::DialogButtonProps;
window.open_alert_dialog(cx, move |alert, _, _| {
alert
.title("Delete Account")
.confirm()
.button_props(
DialogButtonProps::default()
.ok_text("Delete")
.ok_variant(ButtonVariant::Danger)
)
})#Custom content
AlertDialog::new(cx)
.width(px(500.))
.trigger(Button::new("custom-width").label("Custom Width"))
.content(|content, _, _| {
// ... dialog content
})row: root.row-of(6);
col: root.col-of(6);
Preview {
label: "Install Update";
clicked => {
root.update-open = true;
}
Dialog {
width: 100%;
height: 100%;
open <=> root.update-open;
show-close: false;
title: "Update Available";
description: "A new version (v2.0.0) is available. This update includes new features and bug fixes.";
Rectangle {
background: Theme.muted;
border-radius: Theme.radius-md;
HorizontalLayout {
padding: 12px;
spacing: 8px;
Button {
text: "Later";
outline: true;
horizontal-stretch: 1;
clicked => {
root.update-open = false;
}
}
Button {
text: "Update";
variant: ButtonVariant.primary;
horizontal-stretch: 1;
clicked => {
root.update-open = false;
}
}
}
}
}
}// A muted trough holding the two actions, which is the Slint example's
// `Rectangle { background: Theme.muted }` around a row.
let content = body_box(
column![
open_button("Install Update", slot::CONTENT),
muted_line("A muted trough holds the two actions."),
]
.spacing(8.0),
);
let content_dialog = Dialog::new()
.open(app.open[slot::CONTENT])
.close_button(false)
.title("Update Available")
.description(
"A new version (v2.0.0) is available. This update includes new features \
and bug fixes.",
)
.content(
container(
row![
Button::new("Later")
.outline()
.width(Length::Fill)
.on_press(Message::Opened {
slot: slot::CONTENT,
open: false,
}),
Button::new("Update")
.variant(button::Variant::Primary)
.width(Length::Fill)
.on_press(Message::Opened {
slot: slot::CONTENT,
open: false,
}),
]
.spacing(8.0),
)
.width(Length::Fill)
.padding(12.0)
.style(|theme: &Theme| container::Style {
background: Some(Tokens::of(theme).muted.into()),
border: iced::border::rounded(theme::RADIUS_MD),
..container::Style::default()
}),
)
.on_open_change(|open| Message::Opened {
slot: slot::CONTENT,
open,
});#Notice
// An acknowledgement: one full-width action in the centered footer.
let notice = body_box(
column![
open_button("Review Notice", slot::NOTICE),
muted_line("An acknowledgement with one action to dismiss it."),
]
.spacing(8.0),
);
let notice_dialog = Dialog::new()
.open(app.open[slot::NOTICE])
.close_button(false)
.title("Important Notice")
.description("Please read this important notice carefully before proceeding.")
.footer(
DialogFooter::new().centered(true).child(
Button::new("Got It")
.variant(button::Variant::Primary)
.on_press(Message::Opened {
slot: slot::NOTICE,
open: false,
}),
),
)
.on_open_change(|open| Message::Opened {
slot: slot::NOTICE,
open,
});DialogAction and DialogClose are wrapper components that simplify button click handling by automatically triggering the appropriate actions:
- DialogClose: Wraps a button to trigger the
Cancelaction, invokingon_cancelcallback - DialogAction: Wraps a button to trigger the
Confirmaction, invokingon_okcallback
These components eliminate the need to manually call window.close_dialog(cx):
AlertDialog::new(cx)
.trigger(Button::new("show-alert").outline().label("Show Alert"))
.on_ok(|_, window, cx| {
window.push_notification("You confirmed!", cx);
true // Return true to close dialog
})
.on_cancel(|_, window, cx| {
window.push_notification("You cancelled!", cx);
true
})
.content(|content, _, cx| {
content
.child(
DialogHeader::new()
.child(DialogTitle::new().child("Confirm Action"))
.child(DialogDescription::new().child("Do you want to proceed?"))
)
.child(
DialogFooter::new()
.child(
DialogClose::new().child(
Button::new("cancel").outline().label("Cancel")
)
)
.child(
DialogAction::new().child(
Button::new("ok").primary().label("Confirm")
)
)
)
})row: root.row-of(7);
col: root.col-of(7);
Preview {
label: "Review Notice";
clicked => {
root.notice-open = true;
}
Dialog {
width: 100%;
height: 100%;
open <=> root.notice-open;
show-close: false;
title: "Important Notice";
description: "Please read this important notice carefully before proceeding.";
DialogFooter {
centered: true;
Button {
text: "Got It";
variant: ButtonVariant.primary;
clicked => {
root.notice-open = false;
}
}
}
}
}Benefits:
- No need to manually close the dialog
- Automatically connects to
on_okandon_cancelcallbacks - Cleaner, more declarative code
- Supports returning
falsefrom callbacks to prevent closing
#Confirm
// `AlertDialog` again, this time with the title and the primary confirm
// label the caller chose.
let confirm = body_box(
column![
open_button("Open Confirmation", slot::CONFIRM),
muted_line("`AlertDialog` with a title and a confirm label."),
]
.spacing(8.0),
);
let confirm_dialog = AlertDialog::new()
.open(app.open[slot::CONFIRM])
.title("Are you sure?")
.description("Continue with this action?")
.confirm_text("OK")
.on_confirm(Message::Opened {
slot: slot::CONFIRM,
open: false,
})
.on_open_change(|open| Message::Opened {
slot: slot::CONFIRM,
open,
});Open a dialog imperatively using WindowExt::open_alert_dialog:
window.open_alert_dialog(cx, |alert, _, _| {
alert
.title("Delete File")
.description("Are you sure you want to delete this file? This action cannot be undone.")
.show_cancel(true)
.on_ok(|_, window, cx| {
window.push_notification("File deleted", cx);
true // Return true to close dialog
})
})row: root.row-of(8);
col: root.col-of(8);
Preview {
label: "Open Confirmation";
clicked => {
root.confirm-open = true;
}
AlertDialog {
width: 100%;
height: 100%;
open <=> root.confirm-open;
title: "Are you sure?";
description: "Continue with this action?";
confirm-text: "OK";
}
}#Prevent close
// The footer buttons write the dialog's line instead of closing it; only the
// close button and the backdrop ask the caller to close, which is what
// `on_open_change` reports.
let sync = body_box(
column![
open_button("Close During Sync", slot::SYNC),
muted_line("Nothing in the footer closes it; its buttons write the line."),
]
.spacing(8.0),
);
let sync_note = if app.sync_note.is_empty() {
"Your changes are still syncing. The dialog stays open until syncing finishes.".to_string()
} else {
app.sync_note.clone()
};
let sync_dialog = Dialog::new()
.open(app.open[slot::SYNC])
.title("Sync in progress")
.description(sync_note)
.footer(
DialogFooter::new()
.centered(true)
.child(
Button::new("Wait")
.on_press(Message::Note("Waiting for sync to finish.".to_string())),
)
.child(
Button::new("Close")
.variant(button::Variant::Primary)
.on_press(Message::Note("Cannot close while syncing.".to_string())),
),
)
.on_open_change(|open| Message::Opened {
slot: slot::SYNC,
open,
});#Allow Overlay Click to Close
window.open_alert_dialog(cx, |alert, _, _| {
alert
.title("Notice")
.description("Click outside this dialog or press ESC to close it.")
.overlay_closable(true)
})#Disable Keyboard ESC to Close
window.open_alert_dialog(cx, |alert, _, _| {
alert
.title("Important Notice")
.description("Please read this carefully before proceeding.")
.keyboard(false)
})#Show Close Button
window.open_alert_dialog(cx, |alert, _, _| {
alert
.title("Information")
.description("Some information...")
.close_button(true)
})#Prevent Dialog from Closing
Return false from on_ok or on_cancel callbacks to prevent the dialog from closing:
window.open_alert_dialog(cx, |alert, _, _| {
alert
.title("Processing")
.description("A process is running. Click Continue to stop it or Cancel to keep waiting.")
.confirm()
.ok_text("Continue")
.on_ok(|_, window, cx| {
// Return false to prevent closing
window.push_notification("Cannot close: Process still running", cx);
false
})
.on_cancel(|_, window, cx| {
window.push_notification("Waiting...", cx);
false
})
})row: root.row-of(9);
col: root.col-of(9);
Preview {
label: "Close During Sync";
clicked => {
root.sync-open = true;
}
Dialog {
width: 100%;
height: 100%;
open <=> root.sync-open;
title: "Sync in progress";
description: root.sync-note == "" ? "Your changes are still syncing. The dialog stays open until syncing finishes." : root.sync-note;
DialogFooter {
centered: true;
Button {
text: "Wait";
clicked => {
root.sync-note = "Waiting for sync to finish.";
}
}
Button {
text: "Close";
variant: ButtonVariant.primary;
clicked => {
root.sync-note = "Cannot close while syncing.";
}
}
}
}
}#Dialog Close Callback
Use on_close to execute actions after the dialog closes (called after on_ok or on_cancel):
window.open_alert_dialog(cx, |alert, _, _| {
alert
.title("Confirm")
.description("Are you sure?")
.on_close(|_, window, cx| {
window.push_notification("Dialog closed", cx);
})
})#Session Timeout
window.open_alert_dialog(cx, |alert, _, _| {
alert
.content(|content, _, _| {
content
.child(
DialogHeader::new()
.items_center()
.child(DialogTitle::new().child("Session Expired"))
.child(DialogDescription::new().child(
"Your session has expired due to inactivity. \
Please log in again to continue."
))
)
.child(
DialogFooter::new()
.child(
Button::new("sign-in")
.label("Sign in")
.primary()
.flex_1()
.on_click(|_, window, cx| {
window.push_notification("Redirecting to login...", cx);
window.close_dialog(cx);
})
)
)
})
})#Update Available
AlertDialog::new(cx)
.trigger(Button::new("update").outline().label("Update Available"))
.on_cancel(|_, window, cx| {
window.push_notification("Update postponed", cx);
true
})
.on_ok(|_, window, cx| {
window.push_notification("Starting update...", cx);
true
})
.content(|content, _, _| {
content
.child(
DialogHeader::new()
.child(DialogTitle::new().child("Update Available"))
.child(DialogDescription::new().child(
"A new version (v2.0.0) is available. \
This update includes new features and bug fixes."
))
)
.child(
DialogFooter::new()
.child(
DialogClose::new().child(
Button::new("later").flex_1().outline().label("Later")
)
)
.child(
DialogAction::new().child(
Button::new("update-now").flex_1().primary().label("Update Now")
)
)
)
})#Best Practices
- Choose the Right API: Use imperative API (
open_alert_dialog) for simple confirmations; use declarative API (trigger+content) for complex layouts or integration with other components - Use DialogAction and DialogClose: Prefer wrapping buttons with
DialogActionandDialogCloseover manualwindow.close_dialog()calls for cleaner, more declarative code - Clarify Intent: Use appropriate button variants (e.g.,
ButtonVariant::Dangerfor delete operations) to communicate the importance of actions - Provide Clear Descriptions: Ensure users understand the consequences of their actions, especially for destructive operations
- Use Icons Wisely: Icons can enhance attention for warnings and errors, but use them appropriately
- Prevent Closing Carefully: Only prevent dialog closing when user confirmation is truly necessary (e.g., a process is running)
- Maintain Consistency: Keep dialog button order and styles consistent throughout your application
#Related Components
- Dialog - More flexible dialog component
- DialogHeader - Dialog header component
- DialogTitle - Dialog title component
- DialogDescription - Dialog description component
- DialogFooter - Dialog footer component
- DialogAction - Wrapper component for confirm/OK buttons
- DialogClose - Wrapper component for cancel/close buttons
- Dialog - More flexible dialog component
- DialogHeader - Dialog header component
- DialogTitle - Dialog title component
- DialogDescription - Dialog description component
- DialogFooter - Dialog footer component
- DialogAction - Wrapper component for confirm/OK buttons
- DialogClose - Wrapper component for cancel/close buttons
#API Reference
#AlertDialog
| Method | Description |
|---|---|
new(cx) | Create a new AlertDialog |
trigger(element) | Set trigger element that opens the dialog when clicked |
content(builder) | Set dialog content using a builder function (declarative API) |
title(title) | Set dialog title (imperative API) |
description(desc) | Set dialog description (imperative API) |
icon(icon) | Set dialog icon (imperative API) |
confirm() | Show OK and Cancel buttons |
ok_text(text) | Set OK button text, default “OK” |
ok_variant(variant) | Set OK button variant, default Primary |
cancel_text(text) | Set cancel button text, default “Cancel” |
cancel_variant(variant) | Set cancel button variant |
button_props(props) | Override the button properties the value sets, keep the rest |
show_cancel(bool) | Show/hide cancel button, default false |
width(px) | Set dialog width, default 420px |
overlay_closable(bool) | Allow clicking overlay to close, default false |
close_button(bool) | Show/hide close button, default false |
keyboard(bool) | Support ESC key to close, default true |
on_ok(callback) | Set OK button callback, return true to close dialog |
on_cancel(callback) | Set cancel button callback, return true to close dialog |
on_close(callback) | Set callback after dialog closes |
#DialogButtonProps
Every property is unset until a builder sets it, and an unset property keeps whatever the dialog already carries.
| Method | Description |
|---|---|
ok_text(text) | Set OK button text, default “OK” |
cancel_text(text) | Set cancel button text, default “Cancel” |
ok_variant(variant) | Set OK button variant |
cancel_variant(variant) | Set cancel button variant |
show_cancel(bool) | Show/hide cancel button |
on_ok(callback) | Set OK callback |
on_cancel(callback) | Set cancel callback |
#DialogAction
A wrapper component that automatically triggers the Confirm action when its child element is clicked. This invokes the on_ok callback set on the AlertDialog.
Usage:
DialogAction::new().child(
Button::new("ok").primary().label("Confirm")
)Behavior:
- Dispatches
Confirmaction on click - Invokes the
on_okcallback - Dialog closes if callback returns
true - Dialog stays open if callback returns
false
#DialogClose
A wrapper component that automatically triggers the Cancel action when its child element is clicked. This invokes the on_cancel callback set on the AlertDialog.
Usage:
DialogClose::new().child(
Button::new("cancel").outline().label("Cancel")
)Behavior:
- Dispatches
Cancelaction on click - Invokes the
on_cancelcallback - Dialog closes if callback returns
true(or if no callback is set) - Dialog stays open if callback returns
false
#AlertDialog
A confirmation dialog. Mirrors GPUI Component's `AlertDialog`.
| Name | Kind | Type | Default |
|---|---|---|---|
open | in-out | bool | |
title | in | string | |
description | in | string | |
cancel-text | in | string | "Cancel" |
confirm-text | in | string | "Continue" |
destructive | in | bool | |
cancelled | callback | () | |
confirmed | callback | () |