From fc67a1543c23cc7d91f955c7ab967188e1338eaa Mon Sep 17 00:00:00 2001 From: dijunkun Date: Tue, 29 Sep 2026 23:57:36 +0800 Subject: [PATCH] [fix] keep macOS main window titlebar white and opaque --- .../src/gui/application/gui_application.cpp | 5 ++- .../src/platform/macos/gui/window_drag_mac.mm | 45 ++++++++++++------- apps/desktop/src/platform/window_drag.h | 7 +-- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/apps/desktop/src/gui/application/gui_application.cpp b/apps/desktop/src/gui/application/gui_application.cpp index 40d430a3..99ad17c7 100644 --- a/apps/desktop/src/gui/application/gui_application.cpp +++ b/apps/desktop/src/gui/application/gui_application.cpp @@ -1033,6 +1033,8 @@ int GuiApplication::Run() { ui_->main->show(); #if _WIN32 ConfigureWindowsWindowIcons(ui_->main->window().win32_hwnd()); +#elif defined(__APPLE__) + ConfigureMainWindowChrome(ui_->main->window().appkit_view()); #endif slint::run_event_loop(slint::EventLoopMode::RunUntilQuit); Cleanup(); @@ -1181,6 +1183,7 @@ void GuiApplication::InitializeSystemTray() { #endif ui_->main->window().set_minimized(false); #if defined(__APPLE__) + ConfigureMainWindowChrome(ui_->main->window().appkit_view()); MacActivateWindow(ui_->main->window().appkit_view()); // A window restored from the tray may only obtain its NSWindow after the // callback returns, so let the regular UI tick configure it. @@ -2407,7 +2410,7 @@ void GuiApplication::SyncMainWindow() { #endif #if defined(__APPLE__) if (ui_->main_native_titlebar_attempts > 0) { - if (HideDisabledMainWindowZoomButton()) { + if (ConfigureMainWindowChrome(ui_->main->window().appkit_view())) { ui_->main_native_titlebar_attempts = 0; } else { --ui_->main_native_titlebar_attempts; diff --git a/apps/desktop/src/platform/macos/gui/window_drag_mac.mm b/apps/desktop/src/platform/macos/gui/window_drag_mac.mm index 9309073b..fb5244a8 100644 --- a/apps/desktop/src/platform/macos/gui/window_drag_mac.mm +++ b/apps/desktop/src/platform/macos/gui/window_drag_mac.mm @@ -80,25 +80,36 @@ bool StartNativeWindowDrag() { } } -bool HideDisabledMainWindowZoomButton() { +bool ConfigureMainWindowChrome(void *opaque_slint_view) { @autoreleasepool { - for (NSWindow *window in [NSApp windows]) { - if (![window.title isEqualToString:@"CrossDesk"]) { - continue; - } - - NSButton *zoom_button = - [window standardWindowButton:NSWindowZoomButton]; - // The main window has a fixed size, so AppKit disables its zoom button. - // Stream windows remain resizable and must keep their native button. - if (zoom_button == nil || zoom_button.enabled) { - continue; - } - - zoom_button.hidden = YES; - return true; + NSView *slint_view = (__bridge NSView *)opaque_slint_view; + NSWindow *window = slint_view.window; + if (window == nil || + (window.styleMask & NSWindowStyleMaskTitled) == 0) { + return false; } - return false; + + NSButton *zoom_button = + [window standardWindowButton:NSWindowZoomButton]; + if (zoom_button == nil || zoom_button.enabled) { + return false; + } + + // Winit creates a transparent AppKit backing even when Slint paints an + // opaque client area. macOS 27 can expose that backing in the titlebar. + // Use a fixed white backing instead of AppKit's theme-dependent titlebar + // material. Aqua keeps the native title and controls readable on white. + window.styleMask &= ~NSWindowStyleMaskFullSizeContentView; + window.appearance = [NSAppearance appearanceNamed:NSAppearanceNameAqua]; + window.titlebarAppearsTransparent = YES; + window.opaque = YES; + window.backgroundColor = NSColor.whiteColor; + zoom_button.hidden = YES; + + NSView *frame_view = window.contentView.superview; + frame_view.needsLayout = YES; + [frame_view layoutSubtreeIfNeeded]; + return true; } } diff --git a/apps/desktop/src/platform/window_drag.h b/apps/desktop/src/platform/window_drag.h index 135556a1..3d62fbc3 100644 --- a/apps/desktop/src/platform/window_drag.h +++ b/apps/desktop/src/platform/window_drag.h @@ -8,9 +8,10 @@ namespace crossdesk { // left-mouse-down event. Returns false when no suitable native event exists. bool StartNativeWindowDrag(); -// Hides the disabled native zoom button on CrossDesk's fixed-size main -// window. Returns true once the matching AppKit window has been configured. -bool HideDisabledMainWindowZoomButton(); +// Sets a fixed white native titlebar and hides the disabled zoom button on +// the fixed-size main window. |slint_view| is its AppKit NSView*. Returns false +// while the native window or its fixed-size constraints are not ready. +bool ConfigureMainWindowChrome(void* slint_view); // Configures live resize and replaces the stream window's native Space // fullscreen action with an immediate, single-window fullscreen transition.