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.
This commit is contained in:
kuddev committed 2026-09-13 20:33:42 +08:00
1 parent 7a26f9c380
commit 78927d3325
7 files changed
+61 -25

No files matched your search

+1 -1
View File
@@ -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
@@ -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<Self>,
) -> 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()
@@ -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);
+2
View File
@@ -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;
+43
View File
@@ -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
}
}
@@ -14,7 +14,7 @@ fn native_window(window: &Window) -> Option<Retained<NSWindow>> {
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()),
@@ -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'