From 0a9301f1317005e26246a7f340cae559c67be7fd Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Tue, 29 Sep 2026 10:46:16 +0700 Subject: [PATCH] refactor dialogs --- Cargo.lock | 109 +++++++++--------- Cargo.toml | 2 +- crates/chat_ui/src/lib.rs | 8 +- crates/theme/src/lib.rs | 2 +- crates/ui/src/{modal.rs => dialog.rs} | 87 +++++++------- crates/ui/src/dock/mod.rs | 14 ++- crates/ui/src/lib.rs | 2 +- crates/ui/src/root.rs | 86 +++++++------- crates/ui/src/window_ext.rs | 40 +++---- crates/workspace/src/dialogs/import.rs | 4 +- crates/workspace/src/dialogs/new_chat.rs | 6 +- crates/workspace/src/dialogs/new_community.rs | 8 +- crates/workspace/src/dialogs/restore.rs | 4 +- crates/workspace/src/dialogs/screening.rs | 6 +- crates/workspace/src/lib.rs | 18 +-- crates/workspace/src/sidebar/mod.rs | 6 +- 16 files changed, 202 insertions(+), 200 deletions(-) rename crates/ui/src/{modal.rs => dialog.rs} (84%) diff --git a/Cargo.lock b/Cargo.lock index 44fc120f..7683cbd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2756,9 +2756,9 @@ dependencies = [ [[package]] name = "gpui-base" -version = "0.6.6" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e904ee0d351f4e85b967e26ae986d3aa195a8d9d35fcd18184a2c40989694e" +checksum = "64bb52b29a8dcdc8d3f595b8b0839c076f237572ab601ba4577d3da8ae085728" dependencies = [ "aho-corasick", "anyhow", @@ -2772,9 +2772,11 @@ dependencies = [ "gpui-pre-sum-tree", "html5ever", "instant", + "itertools 0.13.0", "lsp-types", "markdown", "markup5ever_rcdom", + "num-traits", "objc2 0.6.4", "objc2-app-kit 0.3.2", "objc2-foundation 0.3.2", @@ -2794,9 +2796,9 @@ dependencies = [ [[package]] name = "gpui-pre" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0437c0b83e636a92bd1a39fa1d05fb632ae671289537497b35871ffbe231b84" +checksum = "0e87a42bb37c7cb4e76dd1ac0ce88851e46e976e0373a47ab3e0757abffee54d" dependencies = [ "accesskit", "anyhow", @@ -2864,12 +2866,12 @@ dependencies = [ [[package]] name = "gpui-pre-apple" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3af534f0746eb9e014114bcc0ee00dc250c4b8d75df2e437180c403aa65649c1" +checksum = "00053551517815ab169fda28fff78dbebf2712722593c7671c511785cb3f79d5" dependencies = [ "anyhow", - "block", + "block2 0.6.2", "cbindgen", "cocoa 0.26.1", "core-foundation 0.10.1", @@ -2888,9 +2890,9 @@ dependencies = [ [[package]] name = "gpui-pre-collections" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e7a65204039187d1f4b3d4c2e0233eeac54d4acb0b13f9dc1a3b5adad26c1f" +checksum = "f79760925c31ed06924d28d68981b5b22b2ce25665f58f0a33dfea11170e6e28" dependencies = [ "gpui-pre-util", "indexmap", @@ -2899,9 +2901,9 @@ dependencies = [ [[package]] name = "gpui-pre-derive-refineable" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79dfb4a05f3cd9893d7768dbf5dba1bbbe99cab7394e89b360ccb3b2bcb6b34e" +checksum = "d1aea3f941581a6a9dd545ecd351ef2d336fe6e2a1a4555cb6414944a4984260" dependencies = [ "proc-macro2", "quote", @@ -2910,9 +2912,9 @@ dependencies = [ [[package]] name = "gpui-pre-http-client" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd5a7542c77887a5aff997d78c95884119c8463409569b63fcf060956da1682" +checksum = "ddce27e989e877a3a1a243f97d7ea4f1ade2532abec418d5a0be9cca802702c7" dependencies = [ "anyhow", "async-compression", @@ -2931,9 +2933,9 @@ dependencies = [ [[package]] name = "gpui-pre-http-client-tls" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72aed510adb5e7723f8f57b9751007e3451d46a84edb59b92227178cf59cc19c" +checksum = "0076f6cf87b87b5629d55692638df4edc04fab3da92f897c2294d520efac17d3" dependencies = [ "log", "rustls", @@ -2943,9 +2945,9 @@ dependencies = [ [[package]] name = "gpui-pre-linux" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aab655cda1d6dcd549ac869e8678babc84cf573ab6794a1fe1a39c6c70c60b48" +checksum = "0aac7022e347409454777701082201742710052813964a1e25160f7e22c965ad" dependencies = [ "accesskit", "accesskit_unix", @@ -2990,9 +2992,9 @@ dependencies = [ [[package]] name = "gpui-pre-macos" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "951d17e41a72067ad2d35c60e08a62e8ad7fa9400b41c802e26cfb459f6c05da" +checksum = "5a43af845b260b09393e923c4f1e1a67a10fcfc847b4815192bbfd02ed9fe725" dependencies = [ "accesskit", "accesskit_macos", @@ -3037,9 +3039,9 @@ dependencies = [ [[package]] name = "gpui-pre-macros" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6324092b40ea8ec7c327a4a28427e366a489b4d5d23b5d1bd0243fc2f4ca7a9e" +checksum = "e1627c5dd3c3351e39e0621d3f3e31dbbe7c7658df90b1d2ec12bfe95d54cd37" dependencies = [ "heck 0.5.0", "proc-macro-crate", @@ -3050,9 +3052,9 @@ dependencies = [ [[package]] name = "gpui-pre-perf" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94bff26ccd8105f6206144f8dab16a2dc02e7a68f39dada02cdf492f0afd940" +checksum = "01954497dd02ba96ab4c252a8668643b0cc3085e7a75dca62e7ba0e3dcd83b02" dependencies = [ "gpui-pre-collections", "serde", @@ -3061,10 +3063,11 @@ dependencies = [ [[package]] name = "gpui-pre-platform" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8423d1e0a693d2f9f16c0326bb01b5e81ec0531854d596a18476934ac6880478" +checksum = "0112105c2ac6757fbf527c9c92a753be9bcbce9f4cf26959a11eaa1db6565aa5" dependencies = [ + "anyhow", "console_error_panic_hook", "gpui-pre", "gpui-pre-linux", @@ -3075,9 +3078,9 @@ dependencies = [ [[package]] name = "gpui-pre-refineable" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93b4b0a8d296a3ee5beb1136bc48f8167a6028eb1e243b56851dcd8c9fc6d7a1" +checksum = "ffd14639e5dfab906bbaeabfe0f94289120dcf2e6ed37217deb13964867e89a5" dependencies = [ "gpui-pre-derive-refineable", ] @@ -3134,9 +3137,9 @@ dependencies = [ [[package]] name = "gpui-pre-reqwest-client" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f32ccea5d1a7c8d2868d7b346c2c1f76aa3f86ec9e0575c4e7340af3afd9e9d" +checksum = "53fa47d680f9e186b33af323f97345d67b6150c0e4c0bc894406bff822a4d9b4" dependencies = [ "anyhow", "bytes", @@ -3152,9 +3155,9 @@ dependencies = [ [[package]] name = "gpui-pre-scheduler" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff7b10b3d2fc3fda24847a04436eca9531612c8414874a37ddc005f20e521e12" +checksum = "8049963b716df01b67b58f86000597f222d5b31b15b35069b23a3b07f3c68352" dependencies = [ "async-task", "backtrace", @@ -3169,9 +3172,9 @@ dependencies = [ [[package]] name = "gpui-pre-shared-string" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe17f81bd98a0253140ea79f7f76442d7499bd8d17db302861af8e46c11d38df" +checksum = "8ef6314bdf5ab162c1913712ec4a91b013ce2a9cfb0b557ecdfdd5793c8b61d3" dependencies = [ "schemars", "serde", @@ -3180,9 +3183,9 @@ dependencies = [ [[package]] name = "gpui-pre-sum-tree" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6d35e998a51d24248dad731de1f09d386cbbf46a04edde3b67f469b2ce3f16" +checksum = "5cca7e5fad9d53b2d265860fefdffc5f171966cb99c3fd99ecac15c033f19209" dependencies = [ "gpui-pre-ztracing", "heapless 0.9.3", @@ -3193,9 +3196,9 @@ dependencies = [ [[package]] name = "gpui-pre-util" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a026a30c578930d20974fd8c3d77de36223ac7ba193bd4259e7238929bc8806" +checksum = "d82e06359ae76f8bb07ed713c54adb80cea6a9a2d3dc715f41aec30cd2aadb4d" dependencies = [ "anyhow", "log", @@ -3204,9 +3207,9 @@ dependencies = [ [[package]] name = "gpui-pre-util-macros" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cbf79627a6afc626d4e887dad9d2d92bb069413e8cc692ba3e90bff219eb7a9" +checksum = "b8a0da4143bb0e7eb926eed7edd3a61c9d17344207543dcc25fb398f0cb96b2f" dependencies = [ "gpui-pre-perf", "quote", @@ -3215,9 +3218,9 @@ dependencies = [ [[package]] name = "gpui-pre-web" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34be433b9fcb370b1fe9a1c7fe911f0eb6058791ef8167dcf6c5a5deb624975c" +checksum = "ab3870cc909471bb830c3d79496770c754401aad6cfa122147ccb13e8908ad08" dependencies = [ "anyhow", "console_error_panic_hook", @@ -3244,9 +3247,9 @@ dependencies = [ [[package]] name = "gpui-pre-wgpu" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05515b32799b1767d69300921c1b562cd53104965211a0e8905acdc7aa18e693" +checksum = "f0b02657b56b09140ce542f5e4434fb96d9fdaba0fd0035ba7dba13c171ddb9b" dependencies = [ "anyhow", "bytemuck", @@ -3271,9 +3274,9 @@ dependencies = [ [[package]] name = "gpui-pre-windows" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27662e65bfcf4445d2cccfad91f1570ec07f4b1b5de98400a9f207fd19c464cf" +checksum = "f05592a6f9e3e6bb7a9f2a0d4779271cf747a948db1c07b02448de777ffff8f3" dependencies = [ "accesskit", "accesskit_windows", @@ -3300,9 +3303,9 @@ dependencies = [ [[package]] name = "gpui-pre-zlog" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a5af0b734a391a84872eb770b3ee9830091ec2487b79d06371fe14eefb2b5d" +checksum = "a7506302091e66d7fb5e04a5b3480e90cff0bbe3157458d7df91a41c59238c48" dependencies = [ "anyhow", "chrono", @@ -3312,9 +3315,9 @@ dependencies = [ [[package]] name = "gpui-pre-ztracing" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a24cb19f78c5bc3aab8ddf1e8e0bd6b402fbcb9c7f112f64ddbedef8f8768a59" +checksum = "4ed1bc6d53b3ca93785a8573d1066d555b67bc475b344fb28871f9811eb6f218" dependencies = [ "gpui-pre-zlog", "gpui-pre-ztracing-macro", @@ -3324,9 +3327,9 @@ dependencies = [ [[package]] name = "gpui-pre-ztracing-macro" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1bb25850ad31b88bbdef454afb93ec4cdb81f5e200ba5b3f1a0368111780d2c" +checksum = "829258540ea51bb9ea4a71686beffe5c99586901ba9f39944d06e4a180ab63fe" [[package]] name = "gpui-updater-core" @@ -7685,9 +7688,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.5" +version = "0.26.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c85f2c3ef0b1cd58b36682f4b17aaa995f0e5db534d85692b4903abce21f67" +checksum = "c9cc2678c2cdd569ef8215e2afd7954ada2ae20b4fdd2c5fe6139a3b02d105db" dependencies = [ "rustls", "tokio", diff --git a/Cargo.toml b/Cargo.toml index c7968032..50839b5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ reqwest_client = { package = "gpui-pre-reqwest-client", version = "0.3.5" } gpui_tokio = { path = "crates/gpui_tokio" } # Unstyled behavior, state, and infrastructure from GPUI Kit -gpui-base = "0.6.1" +gpui-base = "0.7.0" # Nostr nostr-lmdb = { git = "https://github.com/rust-nostr/nostr" } diff --git a/crates/chat_ui/src/lib.rs b/crates/chat_ui/src/lib.rs index a2c876d2..e054c119 100644 --- a/crates/chat_ui/src/lib.rs +++ b/crates/chat_ui/src/lib.rs @@ -633,7 +633,7 @@ impl ChatPanel { self.reports_by_id.read().unwrap().contains_key(id) } - /// Clone reports for a message (used for modal display, not called during render) + /// Clone reports for a message (used for dialog display, not called during render) fn sent_reports(&self, id: &EventId) -> Option> { self.reports_by_id.read().unwrap().get(id).cloned() } @@ -978,7 +978,7 @@ impl ChatPanel { let chat = ChatRegistry::global(cx); let seen_on = chat.read(cx).rumor_seen_on(id); - window.open_modal(cx, move |this, _window, cx| { + window.open_dialog(cx, move |this, _window, cx| { this.title("Seen on").show_close(true).child( v_flex() .gap_1() @@ -1021,7 +1021,7 @@ impl ChatPanel { fn open_relays(&mut self, public_key: &PublicKey, window: &mut Window, cx: &mut Context) { let profile = self.profile(public_key, cx); - window.open_modal(cx, move |this, _window, cx| { + window.open_dialog(cx, move |this, _window, cx| { let relays = profile.messaging_relays(); this.title("Messaging Relays") @@ -1422,7 +1422,7 @@ impl ChatPanel { this.on_click(move |_e, window, cx| { let reports = reports.clone(); - window.open_modal(cx, move |this, _window, cx| { + window.open_dialog(cx, move |this, _window, cx| { this.title(SharedString::from("Sent Reports")) .show_close(true) .child(v_flex().gap_4().children({ diff --git a/crates/theme/src/lib.rs b/crates/theme/src/lib.rs index e768504d..e21198f1 100644 --- a/crates/theme/src/lib.rs +++ b/crates/theme/src/lib.rs @@ -134,7 +134,7 @@ pub struct Theme { /// Radius for the general elements. pub radius: Pixels, - /// Radius for the large elements, e.g.: modal, notification. + /// Radius for the large elements, e.g.: dialog, notification. pub radius_lg: Pixels, /// Enable shadow for the general elements. default is true diff --git a/crates/ui/src/modal.rs b/crates/ui/src/dialog.rs similarity index 84% rename from crates/ui/src/modal.rs rename to crates/ui/src/dialog.rs index cf0cad5a..f63dffd5 100644 --- a/crates/ui/src/modal.rs +++ b/crates/ui/src/dialog.rs @@ -6,7 +6,6 @@ use gpui::{ InteractiveElement as _, IntoElement, ParentElement, Pixels, RenderOnce, SharedString, StyleRefinement, Styled, Window, div, hsla, point, px, size, }; -use gpui_base::Dialog; use instant::Duration; use theme::ActiveTheme; @@ -22,15 +21,15 @@ type RenderButtonFn = Box AnyElement>; type FooterFn = Box Vec>; -/// Modal button props. -pub struct ModalButtonProps { +/// Dialog button props. +pub struct DialogButtonProps { ok_text: Option, ok_variant: ButtonVariant, cancel_text: Option, cancel_variant: ButtonVariant, } -impl Default for ModalButtonProps { +impl Default for DialogButtonProps { fn default() -> Self { Self { ok_text: None, @@ -41,7 +40,7 @@ impl Default for ModalButtonProps { } } -impl ModalButtonProps { +impl DialogButtonProps { /// Sets the text of the OK button. Default is `OK`. pub fn ok_text(mut self, ok_text: impl Into) -> Self { self.ok_text = Some(ok_text.into()); @@ -68,7 +67,7 @@ impl ModalButtonProps { } #[derive(IntoElement)] -pub struct Modal { +pub struct Dialog { style: StyleRefinement, title: Option, footer: Option, @@ -85,15 +84,15 @@ pub struct Modal { overlay_closable: bool, keyboard: bool, show_close: bool, - button_props: ModalButtonProps, + button_props: DialogButtonProps, - /// This will be change when open the modal, the focus handle is create when open the modal. + /// This will be change when open the dialog, the focus handle is create when open the dialog. pub focus_handle: FocusHandle, pub layer_ix: usize, pub overlay_visible: bool, } -impl Modal { +impl Dialog { pub fn new(_window: &mut Window, cx: &mut App) -> Self { Self { style: StyleRefinement::default(), @@ -111,19 +110,19 @@ impl Modal { on_close: Rc::new(|_, _, _| {}), on_ok: None, on_cancel: Rc::new(|_, _, _| true), - button_props: ModalButtonProps::default(), + button_props: DialogButtonProps::default(), show_close: true, overlay_closable: true, } } - /// Sets the title of the modal. + /// Sets the title of the dialog. pub fn title(mut self, title: impl IntoElement) -> Self { self.title = Some(title.into_any_element()); self } - /// Set the footer of the modal. + /// Set the footer of the dialog. /// /// The `footer` is a function that takes two `RenderButtonFn` and a `WindowContext` and returns a list of `AnyElement`. /// @@ -145,7 +144,7 @@ impl Modal { self } - /// Set to use confirm modal, with OK and Cancel buttons. + /// Set to use confirm dialog, with OK and Cancel buttons. /// /// See also [`Self::alert`] pub fn confirm(self) -> Self { @@ -154,7 +153,7 @@ impl Modal { .show_close(false) } - /// Set to as a alter modal, with OK button. + /// Set to as a alter dialog, with OK button. /// /// See also [`Self::confirm`] pub fn alert(self) -> Self { @@ -163,13 +162,13 @@ impl Modal { .show_close(false) } - /// Set the button props of the modal. - pub fn button_props(mut self, button_props: ModalButtonProps) -> Self { + /// Set the button props of the dialog. + pub fn button_props(mut self, button_props: DialogButtonProps) -> Self { self.button_props = button_props; self } - /// Sets the callback for when the modal is closed. + /// Sets the callback for when the dialog is closed. /// /// Called after [`Self::on_ok`] or [`Self::on_cancel`] callback. pub fn on_close( @@ -180,9 +179,9 @@ impl Modal { self } - /// Sets the callback for when the modal is has been confirmed. + /// Sets the callback for when the dialog is has been confirmed. /// - /// The callback should return `true` to close the modal, if return `false` the modal will not be closed. + /// The callback should return `true` to close the dialog, if return `false` the dialog will not be closed. pub fn on_ok( mut self, on_ok: impl Fn(&ClickEvent, &mut Window, &mut App) -> bool + 'static, @@ -191,9 +190,9 @@ impl Modal { self } - /// Sets the callback for when the modal is has been canceled. + /// Sets the callback for when the dialog is has been canceled. /// - /// The callback should return `true` to close the modal, if return `false` the modal will not be closed. + /// The callback should return `true` to close the dialog, if return `false` the dialog will not be closed. pub fn on_cancel( mut self, on_cancel: impl Fn(&ClickEvent, &mut Window, &mut App) -> bool + 'static, @@ -208,39 +207,39 @@ impl Modal { self } - /// Set the top offset of the modal, defaults to None, will use the 1/10 of the viewport height. + /// Shift the dialog down from the window centre, defaults to `None`. pub fn margin_top(mut self, margin_top: Pixels) -> Self { self.margin_top = Some(margin_top); self } - /// Sets the width of the modal, defaults to 480px. + /// Sets the width of the dialog, defaults to 480px. pub fn width(mut self, width: Pixels) -> Self { self.width = width; self } - /// Set the maximum width of the modal, defaults to `None`. + /// Set the maximum width of the dialog, defaults to `None`. pub fn max_w(mut self, max_width: Pixels) -> Self { self.max_width = Some(max_width); self } - /// Set the overlay of the modal, defaults to `true`. + /// Set the overlay of the dialog, defaults to `true`. pub fn overlay(mut self, overlay: bool) -> Self { self.overlay = overlay; self } - /// Set the overlay closable of the modal, defaults to `true`. + /// Set the overlay closable of the dialog, defaults to `true`. /// - /// When the overlay is clicked, the modal will be closed. + /// When the overlay is clicked, the dialog will be closed. pub fn overlay_closable(mut self, overlay_closable: bool) -> Self { self.overlay_closable = overlay_closable; self } - /// Set whether to support keyboard esc to close the modal, defaults to `true`. + /// Set whether to support keyboard esc to close the dialog, defaults to `true`. pub fn keyboard(mut self, keyboard: bool) -> Self { self.keyboard = keyboard; self @@ -251,22 +250,22 @@ impl Modal { } } -impl ParentElement for Modal { +impl ParentElement for Dialog { fn extend(&mut self, elements: impl IntoIterator) { self.content.extend(elements); } } -impl Styled for Modal { +impl Styled for Dialog { fn style(&mut self) -> &mut gpui::StyleRefinement { &mut self.style } } -impl RenderOnce for Modal { +impl RenderOnce for Dialog { fn render(self, window: &mut Window, cx: &mut App) -> impl gpui::IntoElement { let layer_ix = self.layer_ix; - let is_topmost = layer_ix + 1 == Root::read(window, cx).active_modals.len(); + let is_topmost = layer_ix + 1 == Root::read(window, cx).active_dialogs.len(); let has_footer = self.footer.is_some(); let on_close = self.on_close.clone(); let on_ok = self.on_ok.clone(); @@ -296,7 +295,7 @@ impl RenderOnce for Modal { } on_close(&ClickEvent::default(), window, cx); - window.close_modal(cx); + window.close_dialog(cx); } }) .into_any_element() @@ -327,7 +326,7 @@ impl RenderOnce for Modal { } on_close(&ClickEvent::default(), window, cx); - window.close_modal(cx); + window.close_dialog(cx); } }) .into_any_element() @@ -344,10 +343,7 @@ impl RenderOnce for Modal { ); let offset_top = px(layer_ix as f32 * 16.); - let y = self.margin_top.unwrap_or(view_size.height / 10.) + offset_top; - let x = view_size.width / 2. - self.width / 2.; - let card_top = window_paddings.top + y; - let card_left = window_paddings.left + x; + let top_offset = self.margin_top.unwrap_or(px(0.)) + offset_top; let mut padding_right = px(16.); let mut padding_left = px(16.); @@ -386,11 +382,9 @@ impl RenderOnce for Modal { .min_h_24() .refine_style(&self.style) // There style is high priority, can't be overridden. - .absolute() .occlude() .relative() - .left(card_left) - .top(card_top) + .top(top_offset) .w(self.width) .when_some(self.max_width, |this, w| this.max_w(w)) .child( @@ -430,7 +424,7 @@ impl RenderOnce for Modal { .on_click(move |_, window, cx| { on_cancel(&ClickEvent::default(), window, cx); on_close(&ClickEvent::default(), window, cx); - window.close_modal(cx); + window.close_dialog(cx); }), ), ) @@ -466,7 +460,8 @@ impl RenderOnce for Modal { ) }) .with_animation("slide-down", animation, move |this, delta| { - let y_offset = px(0.) + delta * px(30.); + // Settle at the target offset; only the entrance is animated. + let y_offset = px(30.) * (delta - 1.); // This is equivalent to `shadow_xl` with an extra opacity. let shadow = vec![ BoxShadow { @@ -484,10 +479,10 @@ impl RenderOnce for Modal { inset: false, }, ]; - this.top(card_top + y_offset).shadow(shadow) + this.top(top_offset + y_offset).shadow(shadow) }); - Dialog::new(cx) + gpui_base::Dialog::new(cx) .layer(layer_ix, is_topmost) .focus_handle(self.focus_handle.clone()) .close_on_escape(self.keyboard) @@ -499,7 +494,7 @@ impl RenderOnce for Modal { .on_cancel(move |event, window, cx| on_cancel(event, window, cx)) .on_close(move |event, window, cx| { on_close(event, window, cx); - window.close_modal(cx); + window.close_dialog(cx); }) .backdrop(backdrop) .popup(card) diff --git a/crates/ui/src/dock/mod.rs b/crates/ui/src/dock/mod.rs index 4b6a32ce..f5a1bbdf 100644 --- a/crates/ui/src/dock/mod.rs +++ b/crates/ui/src/dock/mod.rs @@ -16,7 +16,7 @@ use gpui_base::dock::{ DockAreaRenderer, DockContext, DragPanel, DropIndicator, InsertTarget, NodeId, PaneNode, PaneRef, PanelId, TabGroupContext, TabGroupRenderer, }; -use gpui_base::{Placement, ResizeHandleContext, Side}; +use gpui_base::{HandleEdge, Placement, ResizeHandleContext}; use theme::{ActiveTheme, TABBAR_HEIGHT}; use crate::button::{Button, ButtonVariants as _}; @@ -225,12 +225,14 @@ impl DockSkin { DockPlacement::Center => "dock-resize-handle-center", }; + let edge = match placement { + DockPlacement::Left => Some(HandleEdge::Trailing), + DockPlacement::Right | DockPlacement::Bottom => Some(HandleEdge::Leading), + DockPlacement::Center => None, + }; + resize_handle(id, placement.axis()) - .placement(if placement.is_left() { - Side::Left - } else { - Side::Right - }) + .when_some(edge, |this, edge| this.inside(edge)) .with_appearance(resize_handle_appearance()) .on_drag(DockResizeHandle, move |info, _, _, cx| { cx.stop_propagation(); diff --git a/crates/ui/src/lib.rs b/crates/ui/src/lib.rs index d4ac3f1c..1824ad95 100644 --- a/crates/ui/src/lib.rs +++ b/crates/ui/src/lib.rs @@ -11,6 +11,7 @@ pub use crate::Disableable; pub mod animation; pub mod avatar; pub mod button; +pub mod dialog; pub mod divider; pub mod dock; pub mod group_box; @@ -19,7 +20,6 @@ pub mod input; pub mod markdown; pub mod menu; pub mod message; -pub mod modal; pub mod nav; pub mod nav_item; pub mod notification; diff --git a/crates/ui/src/root.rs b/crates/ui/src/root.rs index d1e44182..8da9baa1 100644 --- a/crates/ui/src/root.rs +++ b/crates/ui/src/root.rs @@ -13,23 +13,23 @@ use theme::{ CLIENT_SIDE_DECORATION_SHADOW, }; -use crate::modal::Modal; +use crate::dialog::Dialog; use crate::notification::{Notification, NotificationList}; #[derive(Clone)] #[allow(clippy::type_complexity)] -pub struct ActiveModal { +pub struct ActiveDialog { focus_handle: FocusHandle, - /// The previous focused handle before opening the modal. + /// The previous focused handle before opening the dialog. previous_focused_handle: Option, - builder: Rc Modal + 'static>, + builder: Rc Dialog + 'static>, } -impl ActiveModal { +impl ActiveDialog { fn new( focus_handle: FocusHandle, previous_focused_handle: Option, - builder: impl Fn(Modal, &mut Window, &mut App) -> Modal + 'static, + builder: impl Fn(Dialog, &mut Window, &mut App) -> Dialog + 'static, ) -> Self { Self { focus_handle, @@ -41,10 +41,10 @@ impl ActiveModal { /// Root is a view for the App window for as the top level view (Must be the first view in the window). /// -/// It is used to manage the Modal, and Notification. +/// It is used to manage the Dialog, and Notification. pub struct Root { - /// All active models - pub(crate) active_modals: Vec, + /// All active dialogs + pub(crate) active_dialogs: Vec, /// Notification layer pub(crate) notification: Entity, @@ -56,7 +56,7 @@ pub struct Root { impl Root { pub fn new(view: AnyView, window: &mut Window, cx: &mut Context) -> Self { Self { - active_modals: Vec::new(), + active_dialogs: Vec::new(), notification: cx.new(|cx| NotificationList::new(window, cx)), view, } @@ -98,63 +98,63 @@ impl Root { ) } - /// Render the modal layer. - pub fn render_modal_layer( + /// Render the dialog layer. + pub fn render_dialog_layer( window: &mut Window, cx: &mut App, ) -> Option> { let root = window.root::()??; - let active_modals = root.read(cx).active_modals.clone(); + let active_dialogs = root.read(cx).active_dialogs.clone(); - if active_modals.is_empty() { + if active_dialogs.is_empty() { return None; } let mut show_overlay_ix = None; - let mut modals = active_modals + let mut dialogs = active_dialogs .iter() .enumerate() - .map(|(i, active_modal)| { - let mut modal = Modal::new(window, cx); + .map(|(i, active_dialog)| { + let mut dialog = Dialog::new(window, cx); - modal = (active_modal.builder)(modal, window, cx); + dialog = (active_dialog.builder)(dialog, window, cx); - // Give the modal the focus handle, because `modal` is a temporary value, is not possible to - // keep the focus handle in the modal. + // Give the dialog the focus handle, because `dialog` is a temporary value, is not possible to + // keep the focus handle in the dialog. // - // So we keep the focus handle in the `active_modal`, this is owned by the `Root`. - modal.focus_handle = active_modal.focus_handle.clone(); + // So we keep the focus handle in the `active_dialog`, this is owned by the `Root`. + dialog.focus_handle = active_dialog.focus_handle.clone(); - modal.layer_ix = i; - // Find the modal which one needs to show overlay. - if modal.has_overlay() { + dialog.layer_ix = i; + // Find the dialog which one needs to show overlay. + if dialog.has_overlay() { show_overlay_ix = Some(i); } - modal + dialog }) .collect::>(); if let Some(ix) = show_overlay_ix - && let Some(modal) = modals.get_mut(ix) + && let Some(dialog) = dialogs.get_mut(ix) { - modal.overlay_visible = true; + dialog.overlay_visible = true; } - Some(div().children(modals)) + Some(div().children(dialogs)) } - /// Open a modal. - pub fn open_modal(&mut self, builder: F, window: &mut Window, cx: &mut Context<'_, Self>) + /// Open a dialog. + pub fn open_dialog(&mut self, builder: F, window: &mut Window, cx: &mut Context<'_, Self>) where - F: Fn(Modal, &mut Window, &mut App) -> Modal + 'static, + F: Fn(Dialog, &mut Window, &mut App) -> Dialog + 'static, { let previous_focused_handle = window.focused(cx).map(|h| h.downgrade()); let focus_handle = cx.focus_handle(); focus_handle.focus(window, cx); - self.active_modals.push(ActiveModal::new( + self.active_dialogs.push(ActiveDialog::new( focus_handle, previous_focused_handle, builder, @@ -163,10 +163,10 @@ impl Root { cx.notify(); } - /// Close the topmost modal. - pub fn close_modal(&mut self, window: &mut Window, cx: &mut Context) { + /// Close the topmost dialog. + pub fn close_dialog(&mut self, window: &mut Window, cx: &mut Context) { if let Some(handle) = self - .active_modals + .active_dialogs .pop() .and_then(|d| d.previous_focused_handle) .and_then(|h| h.upgrade()) @@ -177,12 +177,12 @@ impl Root { cx.notify(); } - /// Close all modals. - pub fn close_all_modals(&mut self, window: &mut Window, cx: &mut Context) { - self.active_modals.clear(); + /// Close all dialogs. + pub fn close_all_dialogs(&mut self, window: &mut Window, cx: &mut Context) { + self.active_dialogs.clear(); let previous_focused_handle = self - .active_modals + .active_dialogs .first() .and_then(|d| d.previous_focused_handle.clone()); @@ -193,9 +193,9 @@ impl Root { cx.notify(); } - /// Check if there are any active modals. - pub fn has_active_modals(&self) -> bool { - !self.active_modals.is_empty() + /// Check if there are any active dialogs. + pub fn has_active_dialogs(&self) -> bool { + !self.active_dialogs.is_empty() } /// Push a notification to the notification layer. diff --git a/crates/ui/src/window_ext.rs b/crates/ui/src/window_ext.rs index e042c448..4877d7bd 100644 --- a/crates/ui/src/window_ext.rs +++ b/crates/ui/src/window_ext.rs @@ -3,24 +3,24 @@ use std::rc::Rc; use gpui::{App, ElementId, Entity, Window}; use crate::Root; -use crate::modal::Modal; +use crate::dialog::Dialog; use crate::notification::Notification; -/// Extension trait for [`Window`] to add modal, notification .. functionality. +/// Extension trait for [`Window`] to add dialog, notification .. functionality. pub trait WindowExtension: Sized { - /// Opens a Modal. - fn open_modal(&mut self, cx: &mut App, builder: F) + /// Opens a Dialog. + fn open_dialog(&mut self, cx: &mut App, builder: F) where - F: Fn(Modal, &mut Window, &mut App) -> Modal + 'static; + F: Fn(Dialog, &mut Window, &mut App) -> Dialog + 'static; - /// Return true, if there is an active Modal. - fn has_active_modal(&mut self, cx: &mut App) -> bool; + /// Return true, if there is an active Dialog. + fn has_active_dialog(&mut self, cx: &mut App) -> bool; - /// Closes the last active Modal. - fn close_modal(&mut self, cx: &mut App); + /// Closes the last active Dialog. + fn close_dialog(&mut self, cx: &mut App); - /// Closes all active Modals. - fn close_all_modals(&mut self, cx: &mut App); + /// Closes all active Dialogs. + fn close_all_dialogs(&mut self, cx: &mut App); /// Returns number of notifications. fn notifications(&mut self, cx: &mut App) -> Rc>>; @@ -46,31 +46,31 @@ pub trait WindowExtension: Sized { impl WindowExtension for Window { #[inline] - fn open_modal(&mut self, cx: &mut App, builder: F) + fn open_dialog(&mut self, cx: &mut App, builder: F) where - F: Fn(Modal, &mut Window, &mut App) -> Modal + 'static, + F: Fn(Dialog, &mut Window, &mut App) -> Dialog + 'static, { Root::update(self, cx, move |root, window, cx| { - root.open_modal(builder, window, cx); + root.open_dialog(builder, window, cx); }) } #[inline] - fn has_active_modal(&mut self, cx: &mut App) -> bool { - Root::read(self, cx).has_active_modals() + fn has_active_dialog(&mut self, cx: &mut App) -> bool { + Root::read(self, cx).has_active_dialogs() } #[inline] - fn close_modal(&mut self, cx: &mut App) { + fn close_dialog(&mut self, cx: &mut App) { Root::update(self, cx, move |root, window, cx| { - root.close_modal(window, cx); + root.close_dialog(window, cx); }) } #[inline] - fn close_all_modals(&mut self, cx: &mut App) { + fn close_all_dialogs(&mut self, cx: &mut App) { Root::update(self, cx, |root, window, cx| { - root.close_all_modals(window, cx); + root.close_all_dialogs(window, cx); }) } diff --git a/crates/workspace/src/dialogs/import.rs b/crates/workspace/src/dialogs/import.rs index ed118245..20a0963c 100644 --- a/crates/workspace/src/dialogs/import.rs +++ b/crates/workspace/src/dialogs/import.rs @@ -15,7 +15,7 @@ use ui::{Disableable, StyledExt, WindowExtension, divider, v_flex}; pub fn open(window: &mut Window, cx: &mut App) { let import = cx.new(|cx| ImportIdentity::new(window, cx)); - window.open_modal(cx, move |this, _window, _cx| { + window.open_dialog(cx, move |this, _window, _cx| { this.width(px(450.)) .show_close(false) .overlay_closable(false) @@ -138,7 +138,7 @@ impl ImportIdentity { Ok(keys) => { nostr.update_in(cx, |this, window, cx| { this.set_signer(keys, cx); - window.close_modal(cx); + window.close_dialog(cx); })?; } Err(e) => { diff --git a/crates/workspace/src/dialogs/new_chat.rs b/crates/workspace/src/dialogs/new_chat.rs index f9d86cbb..8b039d98 100644 --- a/crates/workspace/src/dialogs/new_chat.rs +++ b/crates/workspace/src/dialogs/new_chat.rs @@ -13,9 +13,9 @@ use person::PersonRegistry; use state::NostrRegistry; use theme::ActiveTheme; use ui::button::{Button, ButtonVariants}; +use ui::dialog::DialogButtonProps; use ui::dock::{DockArea, DockPlacement, PanelHandle}; use ui::input::{Input, InputEvent, InputState}; -use ui::modal::ModalButtonProps; use ui::nav::Nav; use ui::{Disableable, IconName, Selectable, Sizable, WindowExtension, h_flex, v_flex}; @@ -27,14 +27,14 @@ const MAX_MEMBERS: usize = 3; pub fn open(dock: WeakEntity, window: &mut Window, cx: &mut App) { let view = cx.new(|cx| NewChat::new(dock, window, cx)); - window.open_modal(cx, { + window.open_dialog(cx, { let view = view.clone(); move |this, _window, _cx| { this.width(px(420.)) .title("New chat") .confirm() .button_props( - ModalButtonProps::default() + DialogButtonProps::default() .cancel_text("Cancel") .ok_text("Start"), ) diff --git a/crates/workspace/src/dialogs/new_community.rs b/crates/workspace/src/dialogs/new_community.rs index 4b4f36ba..dea1a10a 100644 --- a/crates/workspace/src/dialogs/new_community.rs +++ b/crates/workspace/src/dialogs/new_community.rs @@ -6,7 +6,7 @@ use ui::input::{Input, InputState}; pub fn open(window: &mut Window, cx: &mut App) { let name_input = cx.new(|cx| InputState::new(window, cx).placeholder("Community name")); - window.open_modal(cx, move |this, _window, _cx| { + window.open_dialog(cx, move |this, _window, _cx| { let name_input = name_input.clone(); this.width(px(380.)) @@ -14,6 +14,7 @@ pub fn open(window: &mut Window, cx: &mut App) { .title("New community") .child(Input::new(&name_input)) .on_ok(move |_event, _window, cx| { + let community = CommunityRegistry::global(cx); let name = name_input.read(cx).value().trim().to_owned(); if name.is_empty() { @@ -25,8 +26,9 @@ pub fn open(window: &mut Window, cx: &mut App) { ..CommunityMetadata::default() }; - CommunityRegistry::global(cx) - .update(cx, |registry, cx| registry.create(metadata, cx)); + community.update(cx, |registry, cx| { + registry.create(metadata, cx); + }); true }) diff --git a/crates/workspace/src/dialogs/restore.rs b/crates/workspace/src/dialogs/restore.rs index 958d387b..7196bc00 100644 --- a/crates/workspace/src/dialogs/restore.rs +++ b/crates/workspace/src/dialogs/restore.rs @@ -65,8 +65,8 @@ impl RestoreEncryption { this.set_announcement(Keys::new(secret), cx); }); - // Close the current modal - window.close_modal(cx); + // Close the current dialog + window.close_dialog(cx); } fn set_error(&mut self, message: S, cx: &mut Context) diff --git a/crates/workspace/src/dialogs/screening.rs b/crates/workspace/src/dialogs/screening.rs index 79738788..8a61cfd6 100644 --- a/crates/workspace/src/dialogs/screening.rs +++ b/crates/workspace/src/dialogs/screening.rs @@ -52,7 +52,7 @@ impl Screening { subscriptions.push(cx.on_release_in(window, move |this, window, cx| { this.tasks.clear(); - window.close_all_modals(cx); + window.close_all_dialogs(cx); })); cx.defer_in(window, |this, _window, cx| { @@ -259,7 +259,7 @@ impl Screening { self.tasks.push(cx.spawn_in(window, async move |_, cx| { if task.await.is_ok() { cx.update(|window, cx| { - window.close_modal(cx); + window.close_dialog(cx); window.push_notification("Report submitted successfully", cx); }) .ok(); @@ -270,7 +270,7 @@ impl Screening { fn mutual_contacts(&mut self, window: &mut Window, cx: &mut Context) { let contacts = self.mutual_contacts.clone(); - window.open_modal(cx, move |this, _window, _cx| { + window.open_dialog(cx, move |this, _window, _cx| { let contacts = contacts.clone(); let total = contacts.len(); diff --git a/crates/workspace/src/lib.rs b/crates/workspace/src/lib.rs index dff8213a..113974b4 100644 --- a/crates/workspace/src/lib.rs +++ b/crates/workspace/src/lib.rs @@ -93,7 +93,7 @@ impl Workspace { // Subscribe to the nostr events cx.subscribe_in(&nostr, window, move |_this, _state, event, window, cx| { if let StateEvent::SignerChanged = event { - window.close_all_modals(cx); + window.close_all_dialogs(cx); } }), ); @@ -242,7 +242,7 @@ impl Workspace { Command::ShowSettings => { let view = settings::init(window, cx); - window.open_modal(cx, move |this, _window, _cx| { + window.open_dialog(cx, move |this, _window, _cx| { this.width(px(520.)) .show_close(true) .pb_2() @@ -387,7 +387,7 @@ impl Workspace { let device = DeviceRegistry::global(cx); let ent = device.downgrade(); - window.open_modal(cx, move |this, _window, cx| { + window.open_dialog(cx, move |this, _window, cx| { let ent = ent.clone(); this.confirm() @@ -410,7 +410,7 @@ impl Workspace { this.set_announcement(Keys::generate(), cx); }) .ok(); - // true to close modal + // true to close dialog true }) }); @@ -418,7 +418,7 @@ impl Workspace { fn import_encryption(&mut self, window: &mut Window, cx: &mut Context) { let restore = cx.new(|cx| RestoreEncryption::new(window, cx)); - window.open_modal(cx, move |this, _window, _cx| { + window.open_dialog(cx, move |this, _window, _cx| { this.width(px(420.)) .title("Restore Encryption") .child(restore.clone()) @@ -426,7 +426,7 @@ impl Workspace { } fn theme_selector(&mut self, window: &mut Window, cx: &mut Context) { - window.open_modal(cx, move |this, _window, cx| { + window.open_dialog(cx, move |this, _window, cx| { let registry = ThemeRegistry::global(cx); let themes = registry.read(cx).themes(); @@ -508,7 +508,7 @@ impl Workspace { impl Render for Workspace { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - let modal_layer = Root::render_modal_layer(window, cx); + let dialog_layer = Root::render_dialog_layer(window, cx); let notification_layer = Root::render_notification_layer(window, cx); div() @@ -519,7 +519,7 @@ impl Render for Workspace { .child(self.dock.clone()) // Notifications .children(notification_layer) - // Modals - .children(modal_layer) + // Dialogs + .children(dialog_layer) } } diff --git a/crates/workspace/src/sidebar/mod.rs b/crates/workspace/src/sidebar/mod.rs index 8a13df02..aa79b110 100644 --- a/crates/workspace/src/sidebar/mod.rs +++ b/crates/workspace/src/sidebar/mod.rs @@ -22,10 +22,10 @@ use state::{NostrRegistry, StateEvent}; use theme::{ActiveTheme, TABBAR_HEIGHT}; use ui::avatar::Avatar; use ui::button::{Button, ButtonCustomVariant, ButtonVariants}; +use ui::dialog::DialogButtonProps; use ui::dock::{ClosePanel, DockArea, DockPlacement, Panel, PanelEvent, PanelHandle}; use ui::indicator::Indicator; use ui::menu::{DropdownMenu, PopupMenuItem}; -use ui::modal::ModalButtonProps; use ui::nav::Nav; use ui::nav_item::NavItem; use ui::notification::Notification; @@ -876,11 +876,11 @@ impl Sidebar { if kind != RoomKind::Ongoing && AppSettings::get_screening(cx) { let screening = screening::init(peer, window, cx); - window.open_modal(cx, move |this, _window, _cx| { + window.open_dialog(cx, move |this, _window, _cx| { this.confirm() .child(screening.clone()) .button_props( - ModalButtonProps::default() + DialogButtonProps::default() .cancel_text("Ignore") .ok_text("Response"), )