From bf32577dc80ac3579c4416252b9fb2f61f10e7bb Mon Sep 17 00:00:00 2001 From: GeekMr <140129782+WilliamWang1721@users.noreply.github.com> Date: Sat, 12 Sep 2026 22:07:27 +0800 Subject: [PATCH 1/5] fix(macos): align native window controls with titlebar buttons --- .github/workflows/preview-packages.yml | 9 ++- .github/workflows/release.yml | 8 +- INSTALL.md | 5 +- nebula_app/Cargo.toml | 3 + .../gpui_shell/workspace/window_titlebar.rs | 16 +++- .../workspace/window_titlebar/macos.rs | 80 +++++++++++++++++++ .../src/gpui_shell/workspace/windowing.rs | 14 +++- 7 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 nebula_app/src/gpui_shell/workspace/window_titlebar/macos.rs diff --git a/.github/workflows/preview-packages.yml b/.github/workflows/preview-packages.yml index 3a80e45..426ddd2 100644 --- a/.github/workflows/preview-packages.yml +++ b/.github/workflows/preview-packages.yml @@ -302,10 +302,10 @@ jobs: fail-fast: false matrix: include: - - runner: macos-15 + - runner: macos-26 architecture: aarch64 native_architecture: arm64 - - runner: macos-15-intel + - runner: macos-26-intel architecture: x86_64 native_architecture: x86_64 runs-on: ${{ matrix.runner }} @@ -323,6 +323,11 @@ jobs: - name: Verify native runner architecture run: test "$(uname -m)" = "${{ matrix.native_architecture }}" + - name: Require the modern AppKit SDK for native window controls + run: | + sdk_version="$(xcrun --sdk macosx --show-sdk-version)" + test "${sdk_version%%.*}" -ge 26 + - name: Test native DMG capacity and mounted contents run: python3 -m unittest scripts.tests.test_macos_dmg -v diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1807f4a..fddf61c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -253,11 +253,11 @@ jobs: fail-fast: false matrix: include: - - runner: macos-15 + - runner: macos-26 architecture: aarch64 native_architecture: arm64 asset_architecture: arm64 - - runner: macos-15-intel + - runner: macos-26-intel architecture: x86_64 native_architecture: x86_64 asset_architecture: x64 @@ -272,6 +272,10 @@ jobs: python-version: "3.13" - name: Verify native runner architecture run: test "$(uname -m)" = "${{ matrix.native_architecture }}" + - name: Require the modern AppKit SDK for native window controls + run: | + sdk_version="$(xcrun --sdk macosx --show-sdk-version)" + test "${sdk_version%%.*}" -ge 26 - name: Test native DMG capacity and mounted contents run: python3 -m unittest scripts.tests.test_macos_dmg -v - uses: dtolnay/rust-toolchain@stable diff --git a/INSTALL.md b/INSTALL.md index e9dcdf8..d8d415d 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -128,7 +128,10 @@ Keep the extracted directory structure intact: Install [rustup](https://rustup.rs) and the build dependencies for your platform. The repository pins Rust 1.97.1 in `rust-toolchain.toml`. Linux dependency packages are listed in `.github/workflows/release.yml`; macOS requires Xcode command-line -tools. Windows requires Windows 10 1809+ / 11 and a supported Rust linker toolchain. +tools with macOS SDK 26 or newer (`xcrun --sdk macosx --show-sdk-version`) for +the native Liquid Glass window controls. This build requirement does not raise +the macOS 14 minimum runtime version. Windows requires Windows 10 1809+ / 11 +and a supported Rust linker toolchain. ```powershell git clone https://github.com/Kuddev/pebrel diff --git a/nebula_app/Cargo.toml b/nebula_app/Cargo.toml index 8358e5d..451a293 100644 --- a/nebula_app/Cargo.toml +++ b/nebula_app/Cargo.toml @@ -181,7 +181,10 @@ objc2-foundation = { version = "0.3.1", default-features = false, features = [ objc2-app-kit = { version = "0.3.1", default-features = false, features = [ "std", "NSColorSpace", + "NSButton", + "NSControl", "NSResponder", + "NSToolbar", "NSView", "NSWindow", ] } diff --git a/nebula_app/src/gpui_shell/workspace/window_titlebar.rs b/nebula_app/src/gpui_shell/workspace/window_titlebar.rs index 2648b65..56c9377 100644 --- a/nebula_app/src/gpui_shell/workspace/window_titlebar.rs +++ b/nebula_app/src/gpui_shell/workspace/window_titlebar.rs @@ -1,5 +1,8 @@ use super::*; +#[cfg(target_os = "macos")] +pub(super) mod macos; + /// Prepaint records the actual pane column before any titlebar paint runs. Keep /// this cell for the workspace lifetime, so resizing and panel animations do not /// allocate a new shared slot or duplicate the body's layout calculations. @@ -94,8 +97,11 @@ impl NebulaWorkspace { .when(settings_active, |bar| { bar.border_b_1().border_color(crate::gpui_shell::theme::settings_hairline(cx)) }) + .when(top_tabs && !cfg!(target_os = "macos"), |bar| { + bar.pl(px(top_tabs::TOP_TAB_LEFT_INSET)) + }) .when(top_tabs, |bar| { - bar.pl(px(top_tabs::TOP_TAB_LEFT_INSET)).child(self.render_top_title_bar( + bar.child(self.render_top_title_bar( files_active, git_active, settings_active, @@ -112,6 +118,14 @@ impl NebulaWorkspace { )) }); + // AppKit owns both the control group and its geometry. Read the live + // frames so system layout, resize and full-screen transitions agree. + #[cfg(target_os = "macos")] + let bar = { + let (height, inset) = macos::layout(window); + bar.h(px(height)).pl(px(inset)) + }; + div() .relative() .flex_shrink_0() diff --git a/nebula_app/src/gpui_shell/workspace/window_titlebar/macos.rs b/nebula_app/src/gpui_shell/workspace/window_titlebar/macos.rs new file mode 100644 index 0000000..8302715 --- /dev/null +++ b/nebula_app/src/gpui_shell/workspace/window_titlebar/macos.rs @@ -0,0 +1,80 @@ +//! Keep the standard window controls in AppKit's native toolbar hierarchy. + +use gpui::Window; +use objc2::{MainThreadOnly, rc::Retained}; +use objc2_app_kit::{NSToolbar, NSView, NSWindow, NSWindowButton, NSWindowToolbarStyle}; +use objc2_foundation::{NSRect, ns_string}; +use winit::raw_window_handle::{HasWindowHandle, RawWindowHandle}; + +fn native_window(window: &Window) -> Option> { + let handle = HasWindowHandle::window_handle(window).ok()?; + let RawWindowHandle::AppKit(handle) = handle.as_raw() else { return None }; + // SAFETY: GPUI owns this live NSView and calls the adapter on the UI thread. + let view = unsafe { handle.ns_view.cast::().as_ref() }; + view.window() +} + +pub(in super::super) fn configure(window: &Window) { + let Some(native) = native_window(window) else { return }; + let toolbar = NSToolbar::initWithIdentifier( + NSToolbar::alloc(native.mtm()), + ns_string!("pebrel-window-controls"), + ); + // An empty native toolbar supplies the system's current window-control + // treatment. AppKit retains it; GPUI continues to render the app controls. + native.setToolbarStyle(NSWindowToolbarStyle::Unified); + native.setToolbar(Some(&toolbar)); +} + +pub(super) fn layout(window: &Window) -> (f32, f32) { + if window.is_fullscreen() { + return (52.0, 8.0); + } + let Some(native) = native_window(window) else { return (52.0, 96.0) }; + let Some(close) = native.standardWindowButton(NSWindowButton::CloseButton) else { + return (52.0, 96.0); + }; + let Some(zoom) = native.standardWindowButton(NSWindowButton::ZoomButton) else { + return (52.0, 96.0); + }; + let Some(content) = native.contentView() else { return (52.0, 96.0) }; + let close = close.convertRect_toView(close.bounds(), Some(&content)); + let zoom = zoom.convertRect_toView(zoom.bounds(), Some(&content)); + control_layout(close, zoom, content.bounds(), content.isFlipped()) +} + +fn control_layout(close: NSRect, zoom: NSRect, content: NSRect, flipped: bool) -> (f32, f32) { + let center = close.origin.y + close.size.height / 2.0; + let center_from_top = if flipped { + center - content.origin.y + } else { + content.origin.y + content.size.height - center + }; + ( + 2.0 * center_from_top as f32, + (zoom.origin.x + zoom.size.width - content.origin.x + 16.0) as f32, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use objc2_foundation::{NSPoint, NSSize}; + + #[test] + fn app_controls_follow_native_centers_and_leave_room_for_the_group() { + for height in [600.0, 900.0] { + for (inset, center) in [(19.0, 26.0), (12.0, 20.0)] { + for flipped in [false, true] { + let content = NSRect::new(NSPoint::new(5.0, 3.0), NSSize::new(900.0, height)); + let y = 3.0 + if flipped { center - 7.0 } else { height - center - 7.0 }; + let close = NSRect::new(NSPoint::new(5.0 + inset, y), NSSize::new(14.0, 14.0)); + let zoom = NSRect::new(NSPoint::new(5.0 + inset + 46.0, y), close.size); + let (bar_height, left_padding) = control_layout(close, zoom, content, flipped); + assert_eq!(bar_height / 2.0, center as f32); + assert_eq!(left_padding, (inset + 46.0 + 14.0 + 16.0) as f32); + } + } + } + } +} diff --git a/nebula_app/src/gpui_shell/workspace/windowing.rs b/nebula_app/src/gpui_shell/workspace/windowing.rs index 3cb2442..657159d 100644 --- a/nebula_app/src/gpui_shell/workspace/windowing.rs +++ b/nebula_app/src/gpui_shell/workspace/windowing.rs @@ -313,7 +313,17 @@ fn workspace_window_options(cx: &mut App, focus: bool, role: WindowRole) -> Wind WindowOptions { window_bounds: Some(WindowBounds::Windowed(bounds)), window_min_size: Some(size(px(760.0), px(540.0)).min(&bounds.size)), - titlebar: Some(TitleBar::title_bar_options()), + titlebar: Some(gpui::TitlebarOptions { + // A manual traffic-light position makes GPUI rewrite AppKit's + // titlebar container, breaking the system toolbar layout. + traffic_light_position: if cfg!(target_os = "macos") { + None + } else { + TitleBar::title_bar_options().traffic_light_position + }, + ..TitleBar::title_bar_options() + }), + app_owns_titlebar_drag: cfg!(target_os = "macos"), app_id: Some("pebrel".to_owned()), window_background: crate::gpui_shell::wallpaper::initial_background_appearance(), focus, @@ -382,6 +392,8 @@ fn open_workspace_window( let hwnd_out = hwnd_slot.clone(); let handle = cx.open_window(options, move |window, cx| { window.set_window_title(crate::brand::NAME); + #[cfg(target_os = "macos")] + super::window_titlebar::macos::configure(window); *hwnd_out.borrow_mut() = native_hwnd(window).unwrap_or_default(); #[cfg(windows)] crate::gpui_shell::set_native_window_icon(window); From 879db0ea478da9f779f67557eeddebd0769db7a9 Mon Sep 17 00:00:00 2001 From: GeekMr <140129782+WilliamWang1721@users.noreply.github.com> Date: Sun, 13 Sep 2026 17:19:09 +0800 Subject: [PATCH 2/5] =?UTF-8?q?ci:=20=E4=B8=BA=20PR=20#114=20=E6=8E=A5?= =?UTF-8?q?=E5=85=A5=E9=92=88=E5=AF=B9=E6=80=A7=E5=8F=8A=E5=9B=9B=E5=B9=B3?= =?UTF-8?q?=E5=8F=B0=E5=85=A8=E9=87=8F=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/linux-lua.yml | 53 +++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/.github/workflows/linux-lua.yml b/.github/workflows/linux-lua.yml index 6a5b6b2..157eb75 100644 --- a/.github/workflows/linux-lua.yml +++ b/.github/workflows/linux-lua.yml @@ -1,6 +1,7 @@ -name: Linux Lua configuration +name: Full native tests on: + workflow_dispatch: push: paths: - "**/*.rs" @@ -20,26 +21,60 @@ permissions: contents: read jobs: - ubuntu-24-04: - runs-on: ubuntu-24.04 + native-tests: + name: Tests (${{ matrix.os }}) + strategy: + fail-fast: false + matrix: + os: [ubuntu-24.04, windows-2022, macos-26, macos-26-intel] + runs-on: ${{ matrix.os }} + timeout-minutes: 120 + env: + PYTHONUTF8: "1" + CARGO_TERM_COLOR: always steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: "3.13" - name: Install native dependencies + if: runner.os == 'Linux' run: | sudo apt-get update sudo apt-get install -y --no-install-recommends \ - build-essential cmake pkg-config libasound2-dev libdbus-1-dev libudev-dev \ + build-essential cmake pkg-config zsh libasound2-dev libdbus-1-dev libudev-dev \ libfontconfig1-dev libfreetype6-dev \ libegl1-mesa-dev libgl1-mesa-dev \ libwayland-dev libxkbcommon-dev libxkbcommon-x11-dev \ libx11-xcb-dev libxcb1-dev libxcb-render0-dev \ libxcb-shape0-dev libxcb-xfixes0-dev libxcb-xinput-dev libxcb-xkb-dev libxi-dev + - name: Require the AppKit SDK used by this PR + if: runner.os == 'macOS' + run: | + sdk_version="$(xcrun --sdk macosx --show-sdk-version)" + test "${sdk_version%%.*}" -ge 26 - uses: dtolnay/rust-toolchain@stable with: toolchain: "1.97.1" - uses: Swatinem/rust-cache@v2 - - name: Test Lua configuration on Wayland and X11 build - run: | - cargo test --locked -p nebula config:: - - name: Check Linux release build - run: cargo check --workspace --release + - name: Test native titlebar alignment + if: runner.os == 'macOS' + run: >- + cargo test --locked -p nebula --bin pebrel --features gpui-test-support gpui_shell::workspace::window_titlebar::macos::tests:: + - name: Test all Python helpers + if: ${{ !cancelled() }} + run: python -m unittest discover -s scripts/tests -v + - name: Test all conformance harness helpers + if: ${{ !cancelled() }} + run: python -m unittest discover -s scripts/conformance/tests -v + - name: Test the full Rust workspace + if: ${{ !cancelled() }} + run: cargo test --locked --workspace + - name: Test all GPUI product and interaction cases + if: ${{ !cancelled() }} + run: cargo test --locked -p nebula --bin pebrel --features gpui-test-support + - name: Check the release workspace + if: ${{ !cancelled() }} + run: cargo check --locked --workspace --release From e842cf20c1967dfd3fbc7436c8f69bb0321771a1 Mon Sep 17 00:00:00 2001 From: GeekMr <140129782+WilliamWang1721@users.noreply.github.com> Date: Sun, 13 Sep 2026 17:21:19 +0800 Subject: [PATCH 3/5] =?UTF-8?q?ci:=20=E5=B0=86=E6=9E=B6=E6=9E=84=E4=B8=8E?= =?UTF-8?q?=E7=BF=BB=E8=AF=91=E5=90=88=E5=90=8C=E5=B9=B6=E5=85=A5=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E5=85=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/linux-lua.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/linux-lua.yml b/.github/workflows/linux-lua.yml index 157eb75..b8066f8 100644 --- a/.github/workflows/linux-lua.yml +++ b/.github/workflows/linux-lua.yml @@ -75,6 +75,12 @@ jobs: - name: Test all GPUI product and interaction cases if: ${{ !cancelled() }} run: cargo test --locked -p nebula --bin pebrel --features gpui-test-support + - name: Check source architecture + if: ${{ !cancelled() && runner.os == 'Linux' }} + run: python scripts/check_architecture.py + - name: Test translation and allocation contracts + if: ${{ !cancelled() && runner.os == 'Linux' }} + run: cargo test --locked --manifest-path tools/i18n-contract/Cargo.toml - name: Check the release workspace if: ${{ !cancelled() }} run: cargo check --locked --workspace --release From 7a26f9c380aefb0e648cfd457c904b99349158bf Mon Sep 17 00:00:00 2001 From: GeekMr <140129782+WilliamWang1721@users.noreply.github.com> Date: Sun, 13 Sep 2026 17:50:14 +0800 Subject: [PATCH 4/5] =?UTF-8?q?test:=20=E4=BF=AE=E5=A4=8D=E8=B7=A8?= =?UTF-8?q?=E5=B9=B3=E5=8F=B0=E4=B8=B4=E6=97=B6=E8=B7=AF=E5=BE=84=E6=AF=94?= =?UTF-8?q?=E8=BE=83=E5=B9=B6=E8=A7=A6=E5=8F=91=20Python=20=E6=94=B9?= =?UTF-8?q?=E5=8A=A8=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/linux-lua.yml | 2 ++ scripts/conformance/tests/test_harness.py | 2 +- scripts/tests/test_architecture_budgets.py | 2 +- scripts/tests/test_xplat.py | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/linux-lua.yml b/.github/workflows/linux-lua.yml index b8066f8..723b568 100644 --- a/.github/workflows/linux-lua.yml +++ b/.github/workflows/linux-lua.yml @@ -5,6 +5,7 @@ on: push: paths: - "**/*.rs" + - "scripts/**/*.py" - "**/Cargo.toml" - "Cargo.lock" - "rust-toolchain.toml" @@ -12,6 +13,7 @@ on: pull_request: paths: - "**/*.rs" + - "scripts/**/*.py" - "**/Cargo.toml" - "Cargo.lock" - "rust-toolchain.toml" diff --git a/scripts/conformance/tests/test_harness.py b/scripts/conformance/tests/test_harness.py index 950c0f0..e1b97e5 100644 --- a/scripts/conformance/tests/test_harness.py +++ b/scripts/conformance/tests/test_harness.py @@ -139,7 +139,7 @@ class ArchiveSafetyTests(unittest.TestCase): def test_macos_bundle_resolves_declared_executable_and_rejects_other_paths(self) -> None: with tempfile.TemporaryDirectory() as directory: - root = Path(directory) / "Pebrel Preview.app" + root = Path(directory).resolve() / "Pebrel Preview.app" binary = root / "Contents" / "MacOS" / "pebrel" binary.parent.mkdir(parents=True) binary.write_bytes(b"fixture") diff --git a/scripts/tests/test_architecture_budgets.py b/scripts/tests/test_architecture_budgets.py index bbfa0f6..91cd27a 100644 --- a/scripts/tests/test_architecture_budgets.py +++ b/scripts/tests/test_architecture_budgets.py @@ -13,7 +13,7 @@ class LineBudgetTests(unittest.TestCase): def setUp(self): self.temporary = tempfile.TemporaryDirectory() self.addCleanup(self.temporary.cleanup) - self.root = Path(self.temporary.name) + self.root = Path(self.temporary.name).resolve() (self.root / "app/src").mkdir(parents=True) def write(self, name, count): diff --git a/scripts/tests/test_xplat.py b/scripts/tests/test_xplat.py index 55720a1..cc238ad 100644 --- a/scripts/tests/test_xplat.py +++ b/scripts/tests/test_xplat.py @@ -18,7 +18,7 @@ class XplatIsolationTests(unittest.TestCase): def setUp(self): self.temp = tempfile.TemporaryDirectory(prefix="nebula-xplat-test-") self.addCleanup(self.temp.cleanup) - self.root = Path(self.temp.name) + self.root = Path(self.temp.name).resolve() self.repo = self.root / "repo with spaces" self.scripts = self.repo / "scripts/xplat" shutil.copytree(ROOT / "scripts/xplat", self.scripts) From 78927d3325ee25fea9c59e72bdf95d9629614867 Mon Sep 17 00:00:00 2001 From: kuddev Date: Sun, 13 Sep 2026 20:33:42 +0800 Subject: [PATCH 5/5] refactor(macos): keep native window chrome behind the platform boundary Preserve native geometry coverage and publish lifecycle fixture PID files atomically so the newly enabled native CI cannot read an empty PID. --- .github/workflows/linux-lua.yml | 2 +- .../gpui_shell/workspace/window_titlebar.rs | 12 +++--- .../src/gpui_shell/workspace/windowing.rs | 19 ++------ nebula_app/src/platform/mod.rs | 2 + nebula_app/src/platform/window_chrome.rs | 43 +++++++++++++++++++ .../window_chrome}/macos.rs | 2 +- .../conformance/tests/test_posix_lifecycle.py | 6 ++- 7 files changed, 61 insertions(+), 25 deletions(-) create mode 100644 nebula_app/src/platform/window_chrome.rs rename nebula_app/src/{gpui_shell/workspace/window_titlebar => platform/window_chrome}/macos.rs (98%) diff --git a/.github/workflows/linux-lua.yml b/.github/workflows/linux-lua.yml index 723b568..6c7ac89 100644 --- a/.github/workflows/linux-lua.yml +++ b/.github/workflows/linux-lua.yml @@ -64,7 +64,7 @@ jobs: - name: Test native titlebar alignment if: runner.os == 'macOS' run: >- - cargo test --locked -p nebula --bin pebrel --features gpui-test-support gpui_shell::workspace::window_titlebar::macos::tests:: + cargo test --locked -p nebula --bin pebrel --features gpui-test-support platform::window_chrome::macos::tests:: - name: Test all Python helpers if: ${{ !cancelled() }} run: python -m unittest discover -s scripts/tests -v diff --git a/nebula_app/src/gpui_shell/workspace/window_titlebar.rs b/nebula_app/src/gpui_shell/workspace/window_titlebar.rs index 56c9377..aa6c875 100644 --- a/nebula_app/src/gpui_shell/workspace/window_titlebar.rs +++ b/nebula_app/src/gpui_shell/workspace/window_titlebar.rs @@ -1,8 +1,5 @@ use super::*; -#[cfg(target_os = "macos")] -pub(super) mod macos; - /// Prepaint records the actual pane column before any titlebar paint runs. Keep /// this cell for the workspace lifetime, so resizing and panel animations do not /// allocate a new shared slot or duplicate the body's layout calculations. @@ -90,6 +87,7 @@ impl NebulaWorkspace { cx: &mut Context, ) -> gpui::Div { let top_tabs = self.tabs_position == nebula_settings::TabsPositionName::Top; + let native_layout = crate::platform::window_chrome::layout(window); let bar = TitleBar::new() // Leave 8px above and below the existing 32px controls. .h(px(48.0)) @@ -97,7 +95,7 @@ impl NebulaWorkspace { .when(settings_active, |bar| { bar.border_b_1().border_color(crate::gpui_shell::theme::settings_hairline(cx)) }) - .when(top_tabs && !cfg!(target_os = "macos"), |bar| { + .when(top_tabs && native_layout.is_none(), |bar| { bar.pl(px(top_tabs::TOP_TAB_LEFT_INSET)) }) .when(top_tabs, |bar| { @@ -120,10 +118,10 @@ impl NebulaWorkspace { // AppKit owns both the control group and its geometry. Read the live // frames so system layout, resize and full-screen transitions agree. - #[cfg(target_os = "macos")] - let bar = { - let (height, inset) = macos::layout(window); + let bar = if let Some((height, inset)) = native_layout { bar.h(px(height)).pl(px(inset)) + } else { + bar }; div() diff --git a/nebula_app/src/gpui_shell/workspace/windowing.rs b/nebula_app/src/gpui_shell/workspace/windowing.rs index 657159d..29265b0 100644 --- a/nebula_app/src/gpui_shell/workspace/windowing.rs +++ b/nebula_app/src/gpui_shell/workspace/windowing.rs @@ -310,25 +310,15 @@ fn workspace_window_options(cx: &mut App, focus: bool, role: WindowRole) -> Wind ) }, ); - WindowOptions { + crate::platform::window_chrome::configure_options(WindowOptions { window_bounds: Some(WindowBounds::Windowed(bounds)), window_min_size: Some(size(px(760.0), px(540.0)).min(&bounds.size)), - titlebar: Some(gpui::TitlebarOptions { - // A manual traffic-light position makes GPUI rewrite AppKit's - // titlebar container, breaking the system toolbar layout. - traffic_light_position: if cfg!(target_os = "macos") { - None - } else { - TitleBar::title_bar_options().traffic_light_position - }, - ..TitleBar::title_bar_options() - }), - app_owns_titlebar_drag: cfg!(target_os = "macos"), + titlebar: Some(TitleBar::title_bar_options()), app_id: Some("pebrel".to_owned()), window_background: crate::gpui_shell::wallpaper::initial_background_appearance(), focus, ..Default::default() - } + }) }, #[cfg(windows)] WindowRole::QuickTerminal => { @@ -392,8 +382,7 @@ fn open_workspace_window( let hwnd_out = hwnd_slot.clone(); let handle = cx.open_window(options, move |window, cx| { window.set_window_title(crate::brand::NAME); - #[cfg(target_os = "macos")] - super::window_titlebar::macos::configure(window); + crate::platform::window_chrome::configure(window); *hwnd_out.borrow_mut() = native_hwnd(window).unwrap_or_default(); #[cfg(windows)] crate::gpui_shell::set_native_window_icon(window); diff --git a/nebula_app/src/platform/mod.rs b/nebula_app/src/platform/mod.rs index a49c7db..868374f 100644 --- a/nebula_app/src/platform/mod.rs +++ b/nebula_app/src/platform/mod.rs @@ -27,6 +27,8 @@ pub mod notifications; pub mod shell; pub mod shell_integration; pub mod startup; +#[cfg(feature = "gpui-shell")] +pub(crate) mod window_chrome; pub use capabilities::CAPABILITIES; diff --git a/nebula_app/src/platform/window_chrome.rs b/nebula_app/src/platform/window_chrome.rs new file mode 100644 index 0000000..47424a0 --- /dev/null +++ b/nebula_app/src/platform/window_chrome.rs @@ -0,0 +1,43 @@ +//! Native window decoration belongs to the platform boundary. Workspace code +//! supplies ordinary GPUI options and uses optional geometry without depending +//! on AppKit types or duplicating platform detection. + +use gpui::{Window, WindowOptions}; + +#[cfg(target_os = "macos")] +mod macos; + +pub(crate) fn configure_options(options: WindowOptions) -> WindowOptions { + #[cfg(target_os = "macos")] + { + let mut options = options; + if let Some(titlebar) = options.titlebar.as_mut() { + // A manual position makes GPUI rewrite AppKit's titlebar container, + // breaking the native toolbar layout. + titlebar.traffic_light_position = None; + } + options.app_owns_titlebar_drag = true; + options + } + #[cfg(not(target_os = "macos"))] + { + options + } +} + +pub(crate) fn configure(_window: &Window) { + #[cfg(target_os = "macos")] + macos::configure(_window); +} + +/// Height and left inset when native controls determine the titlebar geometry. +pub(crate) fn layout(_window: &Window) -> Option<(f32, f32)> { + #[cfg(target_os = "macos")] + { + Some(macos::layout(_window)) + } + #[cfg(not(target_os = "macos"))] + { + None + } +} diff --git a/nebula_app/src/gpui_shell/workspace/window_titlebar/macos.rs b/nebula_app/src/platform/window_chrome/macos.rs similarity index 98% rename from nebula_app/src/gpui_shell/workspace/window_titlebar/macos.rs rename to nebula_app/src/platform/window_chrome/macos.rs index 8302715..016c80d 100644 --- a/nebula_app/src/gpui_shell/workspace/window_titlebar/macos.rs +++ b/nebula_app/src/platform/window_chrome/macos.rs @@ -14,7 +14,7 @@ fn native_window(window: &Window) -> Option> { view.window() } -pub(in super::super) fn configure(window: &Window) { +pub(super) fn configure(window: &Window) { let Some(native) = native_window(window) else { return }; let toolbar = NSToolbar::initWithIdentifier( NSToolbar::alloc(native.mtm()), diff --git a/scripts/conformance/tests/test_posix_lifecycle.py b/scripts/conformance/tests/test_posix_lifecycle.py index 8cd3cb8..817ed50 100644 --- a/scripts/conformance/tests/test_posix_lifecycle.py +++ b/scripts/conformance/tests/test_posix_lifecycle.py @@ -29,7 +29,11 @@ role = sys.argv[2] if role != 'grandchild': next_role = 'child' if role == 'parent' else 'grandchild' subprocess.Popen([sys.executable, '-I', '-S', __file__, str(root), next_role]) -(root / (role + '.pid')).write_text(str(os.getpid())) +# Publish the PID only after the contents are complete. The parent polls for +# file existence, so creating the final path first exposes an empty file. +pid_path = root / (role + '.pid.tmp') +pid_path.write_text(str(os.getpid())) +pid_path.replace(root / (role + '.pid')) while True: if role == 'parent' and (root / 'exit').exists(): code = root / 'exit-code'