From b091cc0c803c2c5006bc5b3a825a25698ff2fe2a Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Thu, 26 Jan 2023 02:22:57 +0100 Subject: [PATCH] Fix glfwSetSystemThemeCallback and its Cocoa implementation --- src/cocoa_init.m | 97 ++++++++++++++++++++++++++++++++++++++++++++ src/cocoa_platform.h | 4 ++ src/cocoa_window.m | 46 +-------------------- src/init.c | 9 ++-- src/input.c | 7 ++++ src/internal.h | 3 ++ 6 files changed, 118 insertions(+), 48 deletions(-) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 3d3245f5..46d9925b 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -175,6 +175,50 @@ static void createMenuBar(void) [NSApp performSelector:setAppleMenuSelector withObject:appMenu]; } +void nsAppearanceToGLFWTheme(NSAppearance* appearance, GLFWtheme* theme) +{ + NSAppearanceName name = appearance.name; + + if (name == NSAppearanceNameAqua) + { + theme->baseTheme = GLFW_BASE_THEME_LIGHT; + } + else if (name == NSAppearanceNameDarkAqua) + { + theme->baseTheme = GLFW_BASE_THEME_DARK; + } + else if (name == NSAppearanceNameVibrantLight) + { + theme->baseTheme = GLFW_BASE_THEME_LIGHT; + theme->flags |= GLFW_THEME_FLAG_VIBRANT; + } + else if (name == NSAppearanceNameVibrantDark) + { + theme->baseTheme = GLFW_BASE_THEME_DARK; + theme->flags |= GLFW_THEME_FLAG_VIBRANT; + } + if (name == NSAppearanceNameAccessibilityHighContrastAqua) + { + theme->baseTheme = GLFW_BASE_THEME_LIGHT; + theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; + } + else if (name == NSAppearanceNameAccessibilityHighContrastDarkAqua) + { + theme->baseTheme = GLFW_BASE_THEME_DARK; + theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; + } + else if (name == NSAppearanceNameAccessibilityHighContrastVibrantLight) + { + theme->baseTheme = GLFW_BASE_THEME_LIGHT; + theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; + } + else if (name == NSAppearanceNameAccessibilityHighContrastVibrantDark) + { + theme->baseTheme = GLFW_BASE_THEME_DARK; + theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; + } +} + // Create key code translation tables // static void createKeyTables(void) @@ -393,6 +437,41 @@ static GLFWbool initializeTIS(void) - (void)doNothing:(id)object { } +/* +- (void)themeChanged:(NSNotification*)notification +{ + _glfwInputSystemTheme(NULL); +} + +- (void)accentColorChanged +{ + _glfwInputSystemTheme(NULL); +}*/ + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context +{ + // This class is never subclassed, so it's safe to ignore the context parameter + + GLFWtheme theme = { 0, 0 }; + nsAppearanceToGLFWTheme(NSApp.effectiveAppearance, &theme); + + NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; + + theme.flags |= GLFW_THEME_FLAG_HAS_COLOR; + theme.color[0] = color.redComponent * 255; + theme.color[1] = color.greenComponent * 255; + theme.color[2] = color.blueComponent * 255; + theme.color[3] = color.alphaComponent * 255; + + _glfwInputSystemTheme(&theme); + + /*if ([keyPath isEqualToString:@"controlAccentColor"]) { + [self accentColorChanged]; + }*/ +} @end // GLFWHelper @@ -641,6 +720,24 @@ int _glfwInitCocoa(void) return GLFW_FALSE; _glfwPollMonitorsCocoa(); + + /* + [[NSNotificationCenter defaultCenter] + addObserver:_glfw.ns.helper + selector:@selector(themeChanged:) + name:@"AppleInterfaceThemeChangedNotification" + object:nil]; + + [NSColor addObserver:_glfw.ns.helper + forKeyPath:@"controlAccentColor" + options:0 + context:nil]; + */ + + [NSApp addObserver:_glfw.ns.helper + forKeyPath:@"effectiveAppearance" + options:0 + context:nil]; if (![[NSRunningApplication currentApplication] isFinishedLaunching]) [NSApp run]; diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index 3535615d..d22ca54b 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -39,8 +39,10 @@ #import #else typedef void* id; +typedef void NSAppearance; #endif + // NOTE: Many Cocoa enum values have been renamed and we need to build across // SDK versions where one is unavailable or deprecated. // We use the newer names in code and replace them with the older names if @@ -291,6 +293,8 @@ void _glfwRestoreVideoModeCocoa(_GLFWmonitor* monitor); float _glfwTransformYCocoa(float y); +void nsAppearanceToGLFWTheme(NSAppearance* appearance, GLFWtheme* theme); + void* _glfwLoadLocalVulkanLoaderCocoa(void); GLFWbool _glfwInitNSGL(void); diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 1551b2b4..b1416201 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1930,58 +1930,16 @@ void _glfwSetThemeCocoa(_GLFWwindow* window, GLFWtheme* theme) GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window) { - _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); // TODO: remove - - // TODO: must use KVO to observe NSApplication NSAppearance for theme callback. - GLFWtheme* theme = &window->theme; - NSAppearanceName name = [window->ns.object appearance].name; theme->baseTheme = 0; theme->flags = 0; - if (name == NSAppearanceNameAqua) - { - theme->baseTheme = GLFW_BASE_THEME_LIGHT; - } - else if (name == NSAppearanceNameDarkAqua) - { - theme->baseTheme = GLFW_BASE_THEME_DARK; - } - else if (name == NSAppearanceNameVibrantLight) - { - theme->baseTheme = GLFW_BASE_THEME_LIGHT; - theme->flags |= GLFW_THEME_FLAG_VIBRANT; - } - else if (name == NSAppearanceNameVibrantDark) - { - theme->baseTheme = GLFW_BASE_THEME_DARK; - theme->flags |= GLFW_THEME_FLAG_VIBRANT; - } - if (name == NSAppearanceNameAccessibilityHighContrastAqua) - { - theme->baseTheme = GLFW_BASE_THEME_LIGHT; - theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; - } - else if (name == NSAppearanceNameAccessibilityHighContrastDarkAqua) - { - theme->baseTheme = GLFW_BASE_THEME_DARK; - theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; - } - else if (name == NSAppearanceNameAccessibilityHighContrastVibrantLight) - { - theme->baseTheme = GLFW_BASE_THEME_LIGHT; - theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; - } - else if (name == NSAppearanceNameAccessibilityHighContrastVibrantDark) - { - theme->baseTheme = GLFW_BASE_THEME_DARK; - theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; - } + nsAppearanceToGLFWTheme([window->ns.object appearance], theme); // TODO: this is not settable. Is there any reason in overriding a similar value? Does it apply to menu item highlights? If yes, then it must be overridden. - // TODO: must use KVO to observe the controlAccentColor for the theme callback. NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; + // TODO: Cannot use the accent color directly, for window themes, because the accent color is never overridden. theme->flags |= GLFW_THEME_FLAG_HAS_COLOR; theme->color[0] = color.redComponent * 255; diff --git a/src/init.c b/src/init.c index 52c708eb..41107ee8 100644 --- a/src/init.c +++ b/src/init.c @@ -554,8 +554,9 @@ GLFWAPI GLFWthemefun glfwSetSystemThemeCallback(GLFWthemefun callback) _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - //return _glfw.platform.setSystemThemeCallback(callback); - - _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); - return NULL; // TODO: implement + //return _glfw.platform.setSystemThemeCallback(callback + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP(GLFWthemefun, _glfw.callbacks.theme, callback); + return callback; } diff --git a/src/input.c b/src/input.c index 36128e10..dc2ab137 100644 --- a/src/input.c +++ b/src/input.c @@ -477,6 +477,13 @@ void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value) js->hats[hat] = value; } +void _glfwInputSystemTheme(GLFWtheme* theme) +{ + assert(theme != NULL); + + if (_glfw.callbacks.theme) + _glfw.callbacks.theme(theme); +} ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// diff --git a/src/internal.h b/src/internal.h index 1024e193..6dbe5d01 100644 --- a/src/internal.h +++ b/src/internal.h @@ -868,6 +868,7 @@ struct _GLFWlibrary struct { GLFWmonitorfun monitor; GLFWjoystickfun joystick; + GLFWthemefun theme; } callbacks; // These are defined in platform.h @@ -934,6 +935,8 @@ void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value); void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value); void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value); +void _glfwInputSystemTheme(GLFWtheme* theme); + void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement); void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window);