From be460dcbedda57a286a957d5bdffd7a14367354d Mon Sep 17 00:00:00 2001 From: FlyingYu Date: Mon, 3 Nov 2025 16:51:38 +0800 Subject: [PATCH] modal, drawer: Block background interaction when Modal, Drawer is active. (#1483) Co-authored-by: Jason Lee --- Cargo.lock | 9 +++ Cargo.toml | 1 + crates/ui/src/drawer.rs | 14 ++-- crates/ui/src/modal.rs | 19 ++++-- examples/modal_overlay/Cargo.toml | 14 ++++ examples/modal_overlay/src/main.rs | 103 +++++++++++++++++++++++++++++ 6 files changed, 148 insertions(+), 12 deletions(-) create mode 100644 examples/modal_overlay/Cargo.toml create mode 100644 examples/modal_overlay/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index fc4ffda8..5d78abaa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4367,6 +4367,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "modal_overlay" +version = "0.4.0-preview0" +dependencies = [ + "anyhow", + "gpui", + "gpui-component", +] + [[package]] name = "naga" version = "25.0.1" diff --git a/Cargo.toml b/Cargo.toml index eabdc301..69da5b7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "examples/hello_world", "examples/input", "examples/window_title", + "examples/modal_overlay", ] resolver = "2" diff --git a/crates/ui/src/drawer.rs b/crates/ui/src/drawer.rs index 58a4bc19..4a03c16f 100644 --- a/crates/ui/src/drawer.rs +++ b/crates/ui/src/drawer.rs @@ -144,12 +144,16 @@ impl RenderOnce for Drawer { .w(size.width) .h(size.height - titlebar_height) .bg(overlay_color(self.overlay, cx)) - .when(self.overlay_closable, |this| { - this.on_mouse_down(MouseButton::Left, { + .when(self.overlay, |this| { + this.on_any_mouse_down({ let on_close = self.on_close.clone(); - move |_, window, cx| { - on_close(&ClickEvent::default(), window, cx); - window.close_drawer(cx); + move |event, window, cx| { + cx.stop_propagation(); + + if self.overlay_closable && event.button == MouseButton::Left { + on_close(&ClickEvent::default(), window, cx); + window.close_drawer(cx); + } } }) }) diff --git a/crates/ui/src/modal.rs b/crates/ui/src/modal.rs index f3722a02..40412426 100644 --- a/crates/ui/src/modal.rs +++ b/crates/ui/src/modal.rs @@ -380,24 +380,29 @@ impl RenderOnce for Modal { .child( div() .id("modal") + .occlude() .w(view_size.width) .h(view_size.height) .when(self.overlay_visible, |this| { - this.occlude().bg(overlay_color(self.overlay, cx)) + this.bg(overlay_color(self.overlay, cx)) }) - .when(self.overlay_closable, |this| { + .when(self.overlay, |this| { // Only the last modal owns the `mouse down - close modal` event. if (self.layer_ix + 1) != Root::read(window, cx).active_modals.len() { return this; } - this.on_mouse_down(MouseButton::Left, { + this.on_any_mouse_down({ let on_cancel = on_cancel.clone(); let on_close = on_close.clone(); - move |_, window, cx| { - on_cancel(&ClickEvent::default(), window, cx); - on_close(&ClickEvent::default(), window, cx); - window.close_modal(cx); + move |event, window, cx| { + cx.stop_propagation(); + + if self.overlay_closable && event.button == MouseButton::Left { + on_cancel(&ClickEvent::default(), window, cx); + on_close(&ClickEvent::default(), window, cx); + window.close_modal(cx); + } } }) }) diff --git a/examples/modal_overlay/Cargo.toml b/examples/modal_overlay/Cargo.toml new file mode 100644 index 00000000..6b263bdf --- /dev/null +++ b/examples/modal_overlay/Cargo.toml @@ -0,0 +1,14 @@ +[package] +edition = "2021" +name = "modal_overlay" +description = "An example of using gpui-component to create a modal with overlay." +publish = false +version = "0.4.0-preview0" + +[dependencies] +anyhow.workspace = true +gpui.workspace = true +gpui-component = { workspace = true } + +[lints] +workspace = true diff --git a/examples/modal_overlay/src/main.rs b/examples/modal_overlay/src/main.rs new file mode 100644 index 00000000..fcf2c188 --- /dev/null +++ b/examples/modal_overlay/src/main.rs @@ -0,0 +1,103 @@ +use gpui::*; +use gpui_component::{button::*, menu::ContextMenuExt, *}; + +actions!(class_menu, [Open, Delete, Export, Info]); + +pub struct HelloWorld; + +impl HelloWorld { + fn show_modal(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context) { + window.open_modal(cx, move |modal, _, _| { + modal.title("Test Modal").child("Hello from Modal!") + }); + } + + fn show_drawer(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context) { + window.open_drawer(cx, move |drawer, _, _| { + drawer.title("Test Drawer").child("Hello from Drawer!") + }); + } +} + +impl Render for HelloWorld { + fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { + div() + .bg(gpui::white()) + .size_full() + .child(TitleBar::new().child("Modal & Drawer")) + .child( + div() + .p_8() + .v_flex() + .gap_2() + .size_full() + .child( + h_flex() + .gap_4() + .child( + Button::new("btn1") + .outline() + .label("Open Modal") + .on_click(cx.listener(Self::show_modal)), + ) + .child( + Button::new("btn2") + .outline() + .label("Open Drawer") + .on_click(cx.listener(Self::show_drawer)), + ), + ) + .child( + div() + .id("second-area") + .v_flex() + .h_40() + .border_1() + .border_dashed() + .border_color(gpui::black()) + .items_center() + .justify_center() + .hover(|this| this.bg(gpui::yellow().opacity(0.2))) + .child("Hover test here.") + .child("Right click to show Context Menu") + .context_menu({ + move |this, _, _| { + this.separator() + .menu("Open", Box::new(Open)) + .menu("Delete", Box::new(Delete)) + .menu("Export", Box::new(Export)) + .menu("Info", Box::new(Info)) + .separator() + } + }), + ), + ) + .children(Root::render_modal_layer(window, cx)) + .children(Root::render_drawer_layer(window, cx)) + } +} + +fn main() { + let app = Application::new(); + + app.run(move |cx| { + gpui_component::init(cx); + + cx.spawn(async move |cx| { + cx.open_window( + WindowOptions { + titlebar: Some(TitleBar::title_bar_options()), + ..Default::default() + }, + |window, cx| { + let view = cx.new(|_| HelloWorld); + // This first level on the window, should be a Root. + cx.new(|cx| Root::new(view.into(), window, cx)) + }, + )?; + + Ok::<_, anyhow::Error>(()) + }) + .detach(); + }); +}