widgets: modal backdrop dismissal + title-row close button
CI / orchestrate (push) Canceled after 0s
CI / fmt (push) Canceled after 0s
CI / clippy (push) Canceled after 0s
CI / typos (push) Canceled after 0s
CI / taplo (push) Canceled after 0s
CI / lockfile (push) Canceled after 0s
CI / machete (push) Canceled after 0s
CI / msrv (push) Canceled after 0s
CI / check-wasm (push) Canceled after 0s
CI / test-linux (push) Canceled after 0s
CI / test-mac (push) Canceled after 0s
CI / test-windows (push) Canceled after 0s
CI / check-examples (push) Canceled after 0s
CI / beta-test (push) Canceled after 0s
CI / tests-pass (push) Canceled after 0s
CI / orchestrate (push) Canceled after 0s
CI / fmt (push) Canceled after 0s
CI / clippy (push) Canceled after 0s
CI / typos (push) Canceled after 0s
CI / taplo (push) Canceled after 0s
CI / lockfile (push) Canceled after 0s
CI / machete (push) Canceled after 0s
CI / msrv (push) Canceled after 0s
CI / check-wasm (push) Canceled after 0s
CI / test-linux (push) Canceled after 0s
CI / test-mac (push) Canceled after 0s
CI / test-windows (push) Canceled after 0s
CI / check-examples (push) Canceled after 0s
CI / beta-test (push) Canceled after 0s
CI / tests-pass (push) Canceled after 0s
The Dismissed doc always said "escape or backdrop" but the backdrop never emitted it: the mask had no click handler and the card no close affordance, so a buttonless dialog (Help > Search Actions) could not be closed by mouse at all. - ModalOptions::dismiss_on_mask (opt-in): a backdrop click emits Dismissed; the card stops propagation so content clicks stay inside. - A ✕ button in the title row emits Dismissed on every modal (same semantics as Escape).
This commit is contained in:
@@ -64,6 +64,10 @@ pub struct ModalOptions {
|
||||
pub width: gpui::Pixels,
|
||||
/// The buttons in the footer row.
|
||||
pub buttons: Vec<DialogButton>,
|
||||
/// Whether a click on the dimmed backdrop dismisses the dialog
|
||||
/// (launcher-style dialogs opt in; form dialogs keep the accidental-
|
||||
/// click protection).
|
||||
pub dismiss_on_mask: bool,
|
||||
}
|
||||
|
||||
impl ModalOptions {
|
||||
@@ -73,6 +77,7 @@ impl ModalOptions {
|
||||
title: title.into(),
|
||||
width,
|
||||
buttons: Vec::new(),
|
||||
dismiss_on_mask: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +86,13 @@ impl ModalOptions {
|
||||
self.buttons.push(button);
|
||||
self
|
||||
}
|
||||
|
||||
/// Allow a backdrop click to dismiss the dialog (emits
|
||||
/// [`ModalEvent::Dismissed`] like Escape does).
|
||||
pub fn with_dismiss_on_mask(mut self, enabled: bool) -> Self {
|
||||
self.dismiss_on_mask = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A request emitted by a modal dialog.
|
||||
@@ -175,6 +187,18 @@ impl Render for Modal {
|
||||
})
|
||||
.occlude()
|
||||
.block_mouse_except_scroll()
|
||||
// Backdrop click dismisses when the dialog opts in (the card
|
||||
// below stops propagation, so only the true backdrop hits this).
|
||||
.on_mouse_down(
|
||||
gpui::MouseButton::Left,
|
||||
cx.listener(move |this, _event: &gpui::MouseDownEvent, _window, cx| {
|
||||
if this.options.dismiss_on_mask {
|
||||
let control = this.control;
|
||||
cx.emit(ModalEvent::Dismissed { control });
|
||||
cx.notify();
|
||||
}
|
||||
}),
|
||||
)
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
@@ -204,6 +228,11 @@ impl Render for Modal {
|
||||
.bg(colors.container)
|
||||
.debug_selector(|| "dialog-card".into())
|
||||
.shadow_lg()
|
||||
// Clicks inside the card never reach the backdrop's
|
||||
// dismiss handler.
|
||||
.on_mouse_down(gpui::MouseButton::Left, |_event, _window, cx| {
|
||||
cx.stop_propagation();
|
||||
})
|
||||
.flex()
|
||||
.flex_col()
|
||||
.child(
|
||||
@@ -213,7 +242,30 @@ impl Render for Modal {
|
||||
.border_b_1()
|
||||
.border_color(colors.border)
|
||||
.text_color(colors.text)
|
||||
.child(self.options.title.clone()),
|
||||
.flex()
|
||||
.items_center()
|
||||
.child(div().flex_1().child(self.options.title.clone()))
|
||||
.child(
|
||||
// The universal close affordance (emits
|
||||
// Dismissed, same as Escape).
|
||||
div()
|
||||
.id(ElementId::named_usize("gpui-widgets-modal-close", control))
|
||||
.debug_selector(|| "dialog-close".into())
|
||||
.px_1()
|
||||
.rounded_sm()
|
||||
.cursor_pointer()
|
||||
.text_color(colors.disabled)
|
||||
.on_mouse_down(
|
||||
gpui::MouseButton::Left,
|
||||
cx.listener(move |this, _event, _window, cx| {
|
||||
let control = this.control;
|
||||
cx.emit(ModalEvent::Dismissed { control });
|
||||
cx.notify();
|
||||
cx.stop_propagation();
|
||||
}),
|
||||
)
|
||||
.child("✕"),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
|
||||
Reference in New Issue
Block a user