From 39be9e5949483e8964322d9878e7d5cb794872cb Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Tue, 1 Oct 2024 09:25:02 +0800 Subject: [PATCH] gpui: Fix `show: false` support on Windows to create an invisible window (#18161) Release Notes: - N/A - The `show` of WindowOptions is valid on macOS but not on Windows, this changes to fix it to support create an invisible window. ```bash cargo run -p gpui --example window ``` ## Before https://github.com/user-attachments/assets/4157bdaa-39a7-44df-bbdc-30b00e9c61e9 ## After https://github.com/user-attachments/assets/d48fa524-0caa-4f87-932d-01d7a468c488 https://github.com/user-attachments/assets/dd052f15-c8db-4a2a-a6af-a7c0ffecca84 --- crates/gpui/examples/window.rs | 168 +++++++++++++++++++++ crates/gpui/src/platform/mac/window.rs | 2 +- crates/gpui/src/platform/windows/window.rs | 13 +- 3 files changed, 180 insertions(+), 3 deletions(-) create mode 100644 crates/gpui/examples/window.rs diff --git a/crates/gpui/examples/window.rs b/crates/gpui/examples/window.rs new file mode 100644 index 0000000000..0f0d4287da --- /dev/null +++ b/crates/gpui/examples/window.rs @@ -0,0 +1,168 @@ +use gpui::*; +use prelude::FluentBuilder as _; + +struct SubWindow { + custom_titlebar: bool, +} + +fn button(text: &str, on_click: impl Fn(&mut WindowContext) + 'static) -> impl IntoElement { + div() + .id(SharedString::from(text.to_string())) + .flex_none() + .px_2() + .bg(rgb(0xf7f7f7)) + .active(|this| this.opacity(0.85)) + .border_1() + .border_color(rgb(0xe0e0e0)) + .rounded_md() + .cursor_pointer() + .child(text.to_string()) + .on_click(move |_, cx| on_click(cx)) +} + +impl Render for SubWindow { + fn render(&mut self, _: &mut ViewContext) -> impl IntoElement { + div() + .flex() + .flex_col() + .bg(rgb(0xffffff)) + .size_full() + .gap_2() + .when(self.custom_titlebar, |cx| { + cx.child( + div() + .flex() + .h(px(32.)) + .px_4() + .bg(gpui::blue()) + .text_color(gpui::white()) + .w_full() + .child( + div() + .flex() + .items_center() + .justify_center() + .size_full() + .child("Custom Titlebar"), + ), + ) + }) + .child( + div() + .p_8() + .gap_2() + .child("SubWindow") + .child(button("Close", |cx| { + cx.remove_window(); + })), + ) + } +} + +struct WindowDemo {} + +impl Render for WindowDemo { + fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { + let window_bounds = + WindowBounds::Windowed(Bounds::centered(None, size(px(300.0), px(300.0)), cx)); + + div() + .p_4() + .flex() + .flex_wrap() + .bg(rgb(0xffffff)) + .size_full() + .justify_center() + .items_center() + .gap_2() + .child(button("Normal", move |cx| { + cx.open_window( + WindowOptions { + window_bounds: Some(window_bounds), + ..Default::default() + }, + |cx| { + cx.new_view(|_cx| SubWindow { + custom_titlebar: false, + }) + }, + ) + .unwrap(); + })) + .child(button("Popup", move |cx| { + cx.open_window( + WindowOptions { + window_bounds: Some(window_bounds), + kind: WindowKind::PopUp, + ..Default::default() + }, + |cx| { + cx.new_view(|_cx| SubWindow { + custom_titlebar: false, + }) + }, + ) + .unwrap(); + })) + .child(button("Custom Titlebar", move |cx| { + cx.open_window( + WindowOptions { + titlebar: None, + window_bounds: Some(window_bounds), + ..Default::default() + }, + |cx| { + cx.new_view(|_cx| SubWindow { + custom_titlebar: true, + }) + }, + ) + .unwrap(); + })) + .child(button("Invisible", move |cx| { + cx.open_window( + WindowOptions { + show: false, + window_bounds: Some(window_bounds), + ..Default::default() + }, + |cx| { + cx.new_view(|_cx| SubWindow { + custom_titlebar: false, + }) + }, + ) + .unwrap(); + })) + .child(button("Unmovable", move |cx| { + cx.open_window( + WindowOptions { + is_movable: false, + titlebar: None, + window_bounds: Some(window_bounds), + ..Default::default() + }, + |cx| { + cx.new_view(|_cx| SubWindow { + custom_titlebar: false, + }) + }, + ) + .unwrap(); + })) + } +} + +fn main() { + App::new().run(|cx: &mut AppContext| { + let bounds = Bounds::centered(None, size(px(800.0), px(600.0)), cx); + cx.open_window( + WindowOptions { + window_bounds: Some(WindowBounds::Windowed(bounds)), + ..Default::default() + }, + |cx| cx.new_view(|_cx| WindowDemo {}), + ) + .unwrap(); + }); +} diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 885c3565cc..5f9ee43dec 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -707,7 +707,7 @@ impl MacWindow { } } - if focus { + if focus && show { native_window.makeKeyAndOrderFront_(nil); } else if show { native_window.orderFront_(nil); diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index b212a03a98..d7b9a469b7 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -287,7 +287,7 @@ impl WindowsWindow { .map(|title| title.as_ref()) .unwrap_or(""), ); - let (dwexstyle, dwstyle) = if params.kind == WindowKind::PopUp { + let (dwexstyle, mut dwstyle) = if params.kind == WindowKind::PopUp { (WS_EX_TOOLWINDOW, WINDOW_STYLE(0x0)) } else { ( @@ -295,6 +295,10 @@ impl WindowsWindow { WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, ) }; + if !params.show { + dwstyle |= WS_MINIMIZE; + } + let hinstance = get_module_handle(); let display = if let Some(display_id) = params.display_id { // if we obtain a display_id, then this ID must be valid. @@ -357,7 +361,12 @@ impl WindowsWindow { drop(lock); SetWindowPlacement(raw_hwnd, &placement)?; } - unsafe { ShowWindow(raw_hwnd, SW_SHOW).ok()? }; + + if params.show { + unsafe { ShowWindow(raw_hwnd, SW_SHOW).ok()? }; + } else { + unsafe { ShowWindow(raw_hwnd, SW_HIDE).ok()? }; + } Ok(Self(state_ptr)) }