macOS: Fix a big where the color of a transparent titlebar was off when running in the release build versus the build from source. Also fix using a transparent titlebar causing the background opacity to be darkened.

There were two issues.

1) Setting window background color to a non-zero opacity causes
   darkening (essentially there were two layers of blending)

2) The titlebar background view could end up in the wrong position
   because it was a child of the content view rather than its super view

Fix both issues setting the window background to clear color and
moving the background view into the super view while making sure it is
positioned correctly using explicit constraints. Phew.
This commit is contained in:
Kovid Goyal 2025-09-28 21:24:00 +05:30
parent e024226b0c
commit e542cd8378
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
2 changed files with 30 additions and 19 deletions

View file

@ -134,6 +134,13 @@ consumption to do the same tasks.
Detailed list of changes
-------------------------------------
0.43.1 [future]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- macOS: Fix a big where the color of a transparent titlebar was off when
running in the release build versus the build from source. Also fix using a
transparent titlebar causing the background opacity to be darkened.
0.43.0 [2025-09-28]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -3280,19 +3280,20 @@ @implementation NSView (FindByIdentifier)
static
void clear_title_bar_background_views(NSWindow *window) {
#define tag @"kitty-for-transparent-titlebar"
NSView *contentView = window.contentView;
if (contentView) {
for (NSView *subview in [contentView viewsWithIdentifier:tag]) [subview removeFromSuperview];
NSView *contentView = window.contentView, *titlebarContainer = contentView ? contentView.superview : nil;
if (titlebarContainer) {
for (NSView *subview in [titlebarContainer viewsWithIdentifier:tag]) [subview removeFromSuperview];
}
}
static void
set_title_bar_background(NSWindow *window, NSColor *backgroundColor) {
// add an extra view that just renders the background color under the transparent titlebar
NSView *contentView = window.contentView, *titlebarContainer = contentView ? contentView.superview : nil;
if (!titlebarContainer) return;
for (NSView *subview in [contentView viewsWithIdentifier:tag]) [subview removeFromSuperview];
contentView.identifier = @"kitty-content-view";
for (NSView *subview in [titlebarContainer viewsWithIdentifier:tag]) [subview removeFromSuperview];
if (!backgroundColor) return;
const CGFloat height = title_bar_and_tool_bar_height(window);
debug_rendering("titlebar_height used for translucent titlebar view: %f\n", height);
NSView *bgView = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, titlebarContainer.bounds.size.width, height)];
@ -3300,7 +3301,9 @@ void clear_title_bar_background_views(NSWindow *window) {
bgView.wantsLayer = YES;
bgView.layer.backgroundColor = backgroundColor.CGColor;
bgView.identifier = tag;
[contentView addSubview:bgView];
// position the background view above the content view but below the titlebar view
[titlebarContainer addSubview:bgView positioned:NSWindowAbove relativeTo:contentView];
// for (NSView *subview in titlebarContainer.subviews) NSLog(@"sv: %@", subview.identifier);
[NSLayoutConstraint activateConstraints:@[
// Pin to the top of the content view.
[bgView.topAnchor constraintEqualToAnchor:titlebarContainer.topAnchor],
@ -3320,18 +3323,20 @@ GLFWAPI void glfwCocoaSetWindowChrome(GLFWwindow *w, unsigned int color, bool us
if (window->ns.layer_shell.is_active) return;
const bool is_transparent = _glfwPlatformFramebufferTransparent(window);
if (!is_transparent) { background_opacity = 1.0; background_blur = 0; }
NSColor *background = nil;
NSColor *window_background = [NSColor windowBackgroundColor];
if (background_opacity < 1.0) {
// use a clear color (fully transparent) so that the final color is just the color from the surface.
// prevent blurring of shadows at window corners with desktop background by setting a low alpha background
window_background = background_blur > 0 ? [NSColor colorWithWhite: 0 alpha: 0.001f] : [NSColor clearColor];
}
NSAppearance *appearance = nil;
bool titlebar_transparent = false;
NSColor *titlebar_color = nil;
const NSWindowStyleMask current_style_mask = [window->ns.object styleMask];
const bool in_fullscreen = ((current_style_mask & NSWindowStyleMaskFullScreen) != 0) || window->ns.in_traditional_fullscreen;
NSAppearance *light_appearance = is_transparent ? [NSAppearance appearanceNamed:NSAppearanceNameVibrantLight] : [NSAppearance appearanceNamed:NSAppearanceNameAqua];
NSAppearance *dark_appearance = is_transparent ? [NSAppearance appearanceNamed:NSAppearanceNameVibrantDark] : [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua];
if (use_system_color) {
if (is_transparent) {
// prevent blurring of shadows at window corners with desktop background by setting a low alpha background
background = background_blur > 0 ? [NSColor colorWithWhite: 0 alpha: 0.001f] : [NSColor clearColor];
} else background = [NSColor windowBackgroundColor];
switch (system_color) {
case 1:
appearance = light_appearance; break;
@ -3339,17 +3344,15 @@ GLFWAPI void glfwCocoaSetWindowChrome(GLFWwindow *w, unsigned int color, bool us
appearance = dark_appearance; break;
}
} else {
// set a background color and make the title bar transparent so the background color is visible
double red = ((color >> 16) & 0xFF) / 255.0;
double green = ((color >> 8) & 0xFF) / 255.0;
double blue = (color & 0xFF) / 255.0;
double luma = 0.2126 * red + 0.7152 * green + 0.0722 * blue;
CGFloat alpha = background_opacity < 1.0 ? background_opacity : 1.0;
background = [NSColor colorWithSRGBRed:red green:green blue:blue alpha:alpha];
appearance = luma < 0.5 ? dark_appearance : light_appearance;
titlebar_color = [NSColor colorWithSRGBRed:red green:green blue:blue alpha:background_opacity];
titlebar_transparent = true;
}
[window->ns.object setBackgroundColor:background];
[window->ns.object setBackgroundColor:window_background];
[window->ns.object setAppearance:appearance];
_glfwPlatformSetWindowBlur(window, background_blur);
bool has_shadow = false;
@ -3395,12 +3398,13 @@ GLFWAPI void glfwCocoaSetWindowChrome(GLFWwindow *w, unsigned int color, bool us
debug(
"Window Chrome state:\n\tbackground: %s\n\tappearance: %s color_space: %s\n\t"
"blur: %d has_shadow: %d resizable: %d decorations: %s (%d)\n\t"
"titlebar_transparent: %d title_visibility: %d hidden: %d buttons_hidden: %d"
"titlebar_transparent: %d titlebar_color: %s title_visibility: %d hidden: %d buttons_hidden: %d"
"\n",
background ? [background.description UTF8String] : "<nil>",
window_background ? [window_background.description UTF8String] : "<nil>",
appearance ? [appearance.name UTF8String] : "<nil>",
cs ? (cs.localizedName ? [cs.localizedName UTF8String] : [cs.description UTF8String]) : "<nil>",
background_blur, has_shadow, resizable, decorations_desc, window->decorated, titlebar_transparent,
titlebar_color ? [titlebar_color.description UTF8String] : "<nil>",
show_text_in_titlebar, window->ns.titlebar_hidden, hide_titlebar_buttons
);
[window->ns.object setColorSpace:cs];
@ -3416,8 +3420,8 @@ GLFWAPI void glfwCocoaSetWindowChrome(GLFWwindow *w, unsigned int color, bool us
} else {
[window->ns.object setStyleMask:window->ns.pre_full_screen_style_mask | fsmask];
}
if (background_opacity < 1.0 && !window->ns.titlebar_hidden && window->decorated) {
set_title_bar_background(window->ns.object, [background colorUsingColorSpace:(cs ? cs : [NSColorSpace deviceRGBColorSpace])]);
if (background_opacity < 1.0 && !window->ns.titlebar_hidden && window->decorated && titlebar_color != nil) {
set_title_bar_background(window->ns.object, titlebar_color);
} else clear_title_bar_background_views(window->ns.object);
// HACK: Changing the style mask can cause the first responder to be cleared
[window->ns.object makeFirstResponder:window->ns.view];