Update theming API

This commit is contained in:
ws909 2023-02-23 22:48:21 +01:00
parent 8ba39bca81
commit decb018a31
18 changed files with 335 additions and 159 deletions

View File

@ -5879,9 +5879,12 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string);
*/ */
GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window);
// TODO: consider requiring GLFW to be initialized for these GLFWTheme functions to work. That way, implementations can add extra data to the themes, and process them in a platform-specific way.
GLFWAPI GLFWtheme* glfwCreateTheme(void); GLFWAPI GLFWtheme* glfwCreateTheme(void);
GLFWAPI void glfwDestroyTheme(GLFWtheme* theme); GLFWAPI void glfwDestroyTheme(GLFWtheme* theme);
GLFWAPI void glfwCopyTheme(const GLFWtheme* source, GLFWtheme* target); GLFWAPI void glfwCopyTheme(const GLFWtheme* source, GLFWtheme* target);
GLFWAPI int glfwThemeEqual(const GLFWtheme* first, const GLFWtheme* second);
GLFWAPI int glfwThemeGetVariation(const GLFWtheme* theme); GLFWAPI int glfwThemeGetVariation(const GLFWtheme* theme);
GLFWAPI void glfwThemeSetVariation(GLFWtheme* theme, int value); GLFWAPI void glfwThemeSetVariation(GLFWtheme* theme, int value);
@ -5889,10 +5892,12 @@ GLFWAPI void glfwThemeSetVariation(GLFWtheme* theme, int value);
GLFWAPI int glfwThemeGetAttribute(const GLFWtheme* theme, int attribute); GLFWAPI int glfwThemeGetAttribute(const GLFWtheme* theme, int attribute);
GLFWAPI void glfwThemeSetAttribute(GLFWtheme* theme, int attribute, int value); GLFWAPI void glfwThemeSetAttribute(GLFWtheme* theme, int attribute, int value);
// If the return value of glfwGetAttribute(specifier) is GLFW_FALSE, the return values of this function are undefined.
GLFWAPI void glfwThemeGetColor(const GLFWtheme* theme, GLFWAPI void glfwThemeGetColor(const GLFWtheme* theme,
int specifier, int specifier,
float* red, float* green, float* blue, float* alpha); float* red, float* green, float* blue, float* alpha);
// Must execute glfwThemeSetAttribute(specifier, GLFW_TRUE) for this to have an effect.
GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, GLFWAPI void glfwThemeSetColor(GLFWtheme* theme,
int specifier, int specifier,
float red, float green, float blue, float alpha); float red, float green, float blue, float alpha);
@ -5934,11 +5939,19 @@ GLFWAPI void glfwThemeSetColor(GLFWtheme* theme,
/*! @brief Theme attribute. /*! @brief Theme attribute.
* *
* Specifies that a theme is vibrant. * Specifies that a theme requests reduced transparency.
* *
* @ingroup theme * @ingroup theme
*/ */
#define GLFW_THEME_ATTRIBUTE_VIBRANT 2 #define GLFW_THEME_ATTRIBUTE_REDUCE_TRANSPARENCY 2
/*! @brief Theme attribute.
*
* Specifies that a theme requests reduced motion.
*
* @ingroup theme
*/
#define GLFW_THEME_ATTRIBUTE_REDUCE_MOTION 4
/*! @brief Theme color attribute. /*! @brief Theme color attribute.
* *
@ -5949,7 +5962,7 @@ GLFWAPI void glfwThemeSetColor(GLFWtheme* theme,
* *
* @ingroup theme * @ingroup theme
*/ */
#define GLFW_THEME_COLOR_MAIN 4 #define GLFW_THEME_COLOR_MAIN 8
/*! @brief Sets the system theme callback. /*! @brief Sets the system theme callback.
* *
@ -6005,8 +6018,24 @@ GLFWAPI GLFWthemefun glfwSetSystemThemeCallback(GLFWthemefun callback);
GLFWAPI void glfwSetTheme(GLFWwindow* handle, const GLFWtheme* theme); GLFWAPI void glfwSetTheme(GLFWwindow* handle, const GLFWtheme* theme);
/*! @brief Returns the currently active system theme. /*! @brief Returns the currently active system theme.
*
* Executing this yields no changes to a window's theme settings:
* @code
* glfwSetTheme(window, glfwGetTheme(window, GLFW_FALSE))
* @endcode
*
* Executing this sets a window's theme to the current system theme, and disables
* automatic changes to the window's theme for when the system changes its theme.
* @code
* glfwSetTheme(window, glfwGetTheme(window, GLFW_TRUE))
* @endcode
*
* *
* @param[in] window The [window](@ref window) to retrieve the current theme for. * @param[in] window The [window](@ref window) to retrieve the current theme for.
* @param[in] inlineDefaults Specifies whether or not GLFW should replace unspecified
* theme attributes with the currently active system ones. If `GLFW_TRUE`, the returned
* theme describes the active style of the window. If `GLFW_FALSE`, the returned theme
* describes the window's theme settings.
* *
* @return A mutable [theme](@ref theme) object, or `NULL` if an * @return A mutable [theme](@ref theme) object, or `NULL` if an
* [error](@ref error_handling) occurred. * [error](@ref error_handling) occurred.
@ -6027,7 +6056,7 @@ GLFWAPI void glfwSetTheme(GLFWwindow* handle, const GLFWtheme* theme);
* *
* @ingroup theme * @ingroup theme
*/ */
GLFWAPI GLFWtheme* glfwGetTheme(GLFWwindow* handle); GLFWAPI GLFWtheme* glfwGetTheme(GLFWwindow* handle, int inlineDefaults);
/*! @brief Returns the currently active system theme. /*! @brief Returns the currently active system theme.
* *
@ -6051,7 +6080,7 @@ GLFWAPI GLFWtheme* glfwGetTheme(GLFWwindow* handle);
* *
* @ingroup theme * @ingroup theme
*/ */
GLFWAPI GLFWtheme* glfwGetSystemDefaultTheme(); GLFWAPI const GLFWtheme* glfwGetSystemDefaultTheme();
/*! @brief Returns the GLFW time. /*! @brief Returns the GLFW time.
* *

View File

@ -175,7 +175,7 @@ static void createMenuBar(void)
[NSApp performSelector:setAppleMenuSelector withObject:appMenu]; [NSApp performSelector:setAppleMenuSelector withObject:appMenu];
} }
void nsAppearanceToGLFWTheme(NSAppearance* appearance, _GLFWtheme* theme) void _glfwNSAppearanceToTheme(NSAppearance* appearance, _GLFWtheme* theme)
{ {
NSAppearanceName name; NSAppearanceName name;
@ -188,10 +188,9 @@ void nsAppearanceToGLFWTheme(NSAppearance* appearance, _GLFWtheme* theme)
NSAppearanceNameVibrantDark, NSAppearanceNameVibrantDark,
NSAppearanceNameAccessibilityHighContrastAqua, NSAppearanceNameAccessibilityHighContrastAqua,
NSAppearanceNameAccessibilityHighContrastDarkAqua, NSAppearanceNameAccessibilityHighContrastDarkAqua,
NSAppearanceNameAccessibilityHighContrastVibrantLight,
NSAppearanceNameAccessibilityHighContrastVibrantDark
]]; ]];
} else { } else
{
name = appearance.name; name = appearance.name;
} }
@ -205,13 +204,11 @@ void nsAppearanceToGLFWTheme(NSAppearance* appearance, _GLFWtheme* theme)
if ([name isEqualToString:NSAppearanceNameVibrantLight]) if ([name isEqualToString:NSAppearanceNameVibrantLight])
{ {
theme->variation = GLFW_THEME_LIGHT; theme->variation = GLFW_THEME_LIGHT;
theme->flags |= GLFW_THEME_ATTRIBUTE_VIBRANT;
return; return;
} }
if ([name isEqualToString:NSAppearanceNameVibrantDark]) if ([name isEqualToString:NSAppearanceNameVibrantDark])
{ {
theme->variation = GLFW_THEME_DARK; theme->variation = GLFW_THEME_DARK;
theme->flags |= GLFW_THEME_ATTRIBUTE_VIBRANT;
return; return;
} }
} }
@ -235,24 +232,12 @@ void nsAppearanceToGLFWTheme(NSAppearance* appearance, _GLFWtheme* theme)
theme->flags |= GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST; theme->flags |= GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST;
return; return;
} }
if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastVibrantLight])
{
theme->variation = GLFW_THEME_LIGHT;
theme->flags |= GLFW_THEME_ATTRIBUTE_VIBRANT | GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST;
return;
}
if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastVibrantDark])
{
theme->variation = GLFW_THEME_DARK;
theme->flags |= GLFW_THEME_ATTRIBUTE_VIBRANT | GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST;
return;
}
} }
theme->variation = GLFW_THEME_LIGHT; theme->variation = GLFW_THEME_LIGHT;
} }
static void getSystemTheme(_GLFWtheme* theme) void _glfwGetSystemThemeCocoa(_GLFWtheme* theme)
{ {
theme->variation = GLFW_THEME_LIGHT; theme->variation = GLFW_THEME_LIGHT;
theme->flags = 0; theme->flags = 0;
@ -262,7 +247,7 @@ static void getSystemTheme(_GLFWtheme* theme)
// effectiveAppearance is actually not the system appearance, but the application appearance. // effectiveAppearance is actually not the system appearance, but the application appearance.
// As long as NSApplication.appearance is never set, using the effective appearance is fine // As long as NSApplication.appearance is never set, using the effective appearance is fine
// to get and observe the system appearance. // to get and observe the system appearance.
nsAppearanceToGLFWTheme(NSApp.effectiveAppearance, theme); _glfwNSAppearanceToTheme(NSApp.effectiveAppearance, theme);
NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace];
@ -272,6 +257,35 @@ static void getSystemTheme(_GLFWtheme* theme)
theme->color[2] = color.blueComponent; theme->color[2] = color.blueComponent;
theme->color[3] = color.alphaComponent; theme->color[3] = color.alphaComponent;
} }
// TODO: return the standard blue accent color if running in 10.13 or earlier.
// TODO: optimize by reading multiple values at once.
const CFStringRef applicationID = CFSTR("com.apple.universalaccess");
Boolean keyIsValid = false;
Boolean highContrast = CFPreferencesGetAppBooleanValue(CFSTR("increaseContrast"),
applicationID,
&keyIsValid);
if (keyIsValid && highContrast)
theme->flags |= GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST;
keyIsValid = false;
Boolean reduceTransparency = CFPreferencesGetAppBooleanValue(CFSTR("reduceTransparency"),
applicationID,
&keyIsValid);
if (keyIsValid && reduceTransparency)
theme->flags |= GLFW_THEME_ATTRIBUTE_REDUCE_TRANSPARENCY;
keyIsValid = false;
Boolean reduceMotion = CFPreferencesGetAppBooleanValue(CFSTR("reduceMotion"),
applicationID,
&keyIsValid);
if (keyIsValid && reduceMotion)
theme->flags |= GLFW_THEME_ATTRIBUTE_REDUCE_MOTION;
} }
// Create key code translation tables // Create key code translation tables
@ -511,11 +525,16 @@ static GLFWbool initializeTIS(void)
{ {
// This class is never subclassed, so it's safe to ignore the context parameter // This class is never subclassed, so it's safe to ignore the context parameter
// TODO: FIXME: this method is invoked twice when the high contrast setting is edited in the preferences.
_GLFWtheme theme; _GLFWtheme theme;
getSystemTheme(&theme); _glfwGetSystemThemeCocoa(&theme);
// The observer for the effective appearance is invoked more often than the appearance name itself changes.
// For instance, it's invoked when various accesibility settings are changed in the system preferences.
// Not all of these properties are included in the GLFW themes, so those updates must be filtered out.
if (glfwThemeEqual((GLFWtheme*) &theme, (GLFWtheme*) &_glfw.theme))
return;
memcpy(&_glfw.theme, &theme, sizeof(_GLFWtheme));
_glfwInputSystemTheme(&theme); _glfwInputSystemTheme(&theme);
/*if ([keyPath isEqualToString:@"controlAccentColor"]) { /*if ([keyPath isEqualToString:@"controlAccentColor"]) {
@ -785,7 +804,10 @@ int _glfwInitCocoa(void)
options:0 options:0
context:nil]; context:nil];
*/ */
_glfwInitDefaultTheme(&_glfw.theme);
// TODO: add observer for properties other than effectiveAppearance, for observing prior to 10.14. I assume the accesibility options were available then.
if (@available(macOS 10.14, *)) if (@available(macOS 10.14, *))
{ {
[NSApp addObserver:_glfw.ns.helper [NSApp addObserver:_glfw.ns.helper
@ -857,7 +879,7 @@ void _glfwTerminateCocoa(void)
_GLFWtheme* _glfwGetSystemDefaultThemeCocoa(void) _GLFWtheme* _glfwGetSystemDefaultThemeCocoa(void)
{ {
_GLFWtheme* theme = &_glfw.theme; _GLFWtheme* theme = &_glfw.theme;
getSystemTheme(theme); _glfwGetSystemThemeCocoa(theme);
return theme; return theme;
} }

View File

@ -297,7 +297,8 @@ void _glfwRestoreVideoModeCocoa(_GLFWmonitor* monitor);
float _glfwTransformYCocoa(float y); float _glfwTransformYCocoa(float y);
void nsAppearanceToGLFWTheme(NSAppearance* appearance, _GLFWtheme* theme); void _glfwNSAppearanceToTheme(NSAppearance* appearance, _GLFWtheme* theme);
void _glfwGetSystemThemeCocoa(_GLFWtheme* theme);
void* _glfwLoadLocalVulkanLoaderCocoa(void); void* _glfwLoadLocalVulkanLoaderCocoa(void);
@ -308,7 +309,6 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
const _GLFWfbconfig* fbconfig); const _GLFWfbconfig* fbconfig);
void _glfwDestroyContextNSGL(_GLFWwindow* window); void _glfwDestroyContextNSGL(_GLFWwindow* window);
_GLFWtheme* _glfwGetSystemDefaultThemeCocoa(void); _GLFWtheme* _glfwGetSystemDefaultThemeCocoa(void);
void _glfwSetThemeCocoa(_GLFWwindow* window, _GLFWtheme* theme); void _glfwSetThemeCocoa(_GLFWwindow* window, const _GLFWtheme* theme);
_GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window); _GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window, int inlineDefaults);

View File

@ -1876,59 +1876,90 @@ const char* _glfwGetClipboardStringCocoa(void)
} // autoreleasepool } // autoreleasepool
} }
void _glfwSetThemeCocoa(_GLFWwindow* window, _GLFWtheme* theme) // TODO: move
static void replaceTheme(_GLFWwindow* window, _GLFWtheme* currentTheme, const _GLFWtheme* newTheme)
{ {
if (!theme || theme->variation == GLFW_THEME_DEFAULT) const int dissimilarFlags = currentTheme->flags ^ newTheme->flags;
if (currentTheme->variation != newTheme->variation
|| (dissimilarFlags & GLFW_THEME_ATTRIBUTE_REDUCE_TRANSPARENCY))
{ {
[(NSWindow*)window->ns.object setAppearance:nil]; if (newTheme->variation == GLFW_THEME_DEFAULT)
return;
}
if (@available(macOS 10.10, *)) {} else
{
return;
}
// TODO: support color
// TODO: fix vibrancy
// As per the Cocoa documentation, passing the high contrast names to
// appearanceNamed: will result in nil, so these can not be used.
NSAppearanceName name;
if (theme->variation == GLFW_THEME_LIGHT)
{
if (theme->flags & GLFW_THEME_ATTRIBUTE_VIBRANT)
{ {
name = NSAppearanceNameVibrantLight; [(NSWindow*)window->ns.object setAppearance:nil];
} }
else else
{ {
name = NSAppearanceNameAqua; if (@available(macOS 10.10, *))
{
// TODO: support color
// TODO: fix vibrancy
// As per the Cocoa documentation, passing the high contrast names to
// appearanceNamed: will result in nil, so these can not be used.
NSAppearanceName name;
if (newTheme->variation == GLFW_THEME_LIGHT)
{
if (newTheme->flags & GLFW_THEME_ATTRIBUTE_REDUCE_TRANSPARENCY)
name = NSAppearanceNameVibrantLight;
else
name = NSAppearanceNameAqua;
}
else
{
if (newTheme->flags & GLFW_THEME_ATTRIBUTE_REDUCE_TRANSPARENCY)
{
name = NSAppearanceNameVibrantDark;
}
else if (@available(macOS 10.14, *))
{
name = NSAppearanceNameDarkAqua;
}
else
{
name = NSAppearanceNameAqua;
}
}
NSAppearance* appearance = [NSAppearance appearanceNamed:name];
[(NSWindow*)window->ns.object setAppearance:appearance];
}
} }
} }
else
{
if (theme->flags & GLFW_THEME_ATTRIBUTE_VIBRANT)
{
name = NSAppearanceNameVibrantDark;
}
else if (@available(macOS 10.14, *))
{
name = NSAppearanceNameDarkAqua;
} else
{
name = NSAppearanceNameAqua;
}
}
NSAppearance* appearance = [NSAppearance appearanceNamed:name];
[(NSWindow*)window->ns.object setAppearance:appearance];
} }
_GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window) void _glfwSetThemeCocoa(_GLFWwindow* window, const _GLFWtheme* theme)
{ {
_GLFWtheme* theme = &window->theme; _GLFWtheme* currentTheme = &window->theme.internal;
_GLFWtheme newTheme;
if (!theme)
_glfwInitDefaultTheme(&newTheme);
else
memcpy(&newTheme, theme, sizeof(_GLFWtheme));
replaceTheme(window, currentTheme, &newTheme);
memcpy(currentTheme, &newTheme, sizeof(_GLFWtheme));
// Not available for setting in Cocoa.
currentTheme->flags &= ~(GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST |
GLFW_THEME_ATTRIBUTE_REDUCE_TRANSPARENCY |
GLFW_THEME_ATTRIBUTE_REDUCE_MOTION|
GLFW_THEME_COLOR_MAIN);
// TODO: NSColor controlAccentColor 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.
}
_GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window, int inlineDefaults)
{
_GLFWtheme* theme = &window->theme.external;
memcpy(theme, &window->theme.internal, sizeof(_GLFWtheme));
// FIXME: fix not overriding specified properties.
if (!inlineDefaults)
return theme;
theme->variation = GLFW_THEME_LIGHT; theme->variation = GLFW_THEME_LIGHT;
theme->flags = 0; theme->flags = 0;
@ -1940,21 +1971,21 @@ _GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window)
if (appearance == NULL) if (appearance == NULL)
appearance = [NSApp effectiveAppearance]; appearance = [NSApp effectiveAppearance];
nsAppearanceToGLFWTheme(appearance, theme); _glfwNSAppearanceToTheme(appearance, theme);
} }
if (@available(macOS 10.14, *)) { _GLFWtheme systemTheme;
// 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.
NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; _glfwGetSystemThemeCocoa(&systemTheme);
// TODO: Cannot use the accent color directly, for window themes, because the accent color is never overridden.
if ((theme->flags & GLFW_THEME_COLOR_MAIN) == 0)
theme->flags |= GLFW_THEME_COLOR_MAIN; {
theme->color[0] = color.redComponent; assert(systemTheme.flags & GLFW_THEME_COLOR_MAIN);
theme->color[1] = color.greenComponent; memcpy(&theme->color, &systemTheme.color, sizeof(float) * 4);
theme->color[2] = color.blueComponent;
theme->color[3] = color.alphaComponent;
} }
theme->flags |= systemTheme.flags;
return theme; return theme;
} }

View File

@ -543,7 +543,7 @@ GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun)
return cbfun; return cbfun;
} }
GLFWAPI GLFWtheme* glfwGetSystemDefaultTheme() GLFWAPI const GLFWtheme* glfwGetSystemDefaultTheme()
{ {
_GLFWtheme* theme; _GLFWtheme* theme;
_GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
@ -551,7 +551,7 @@ GLFWAPI GLFWtheme* glfwGetSystemDefaultTheme()
theme = _glfw.platform.getSystemDefaultTheme(); theme = _glfw.platform.getSystemDefaultTheme();
assert(theme->variation != GLFW_THEME_DEFAULT); assert(theme->variation != GLFW_THEME_DEFAULT);
return (GLFWtheme*) theme; return (const GLFWtheme*) theme;
} }
GLFWAPI GLFWthemefun glfwSetSystemThemeCallback(GLFWthemefun callback) GLFWAPI GLFWthemefun glfwSetSystemThemeCallback(GLFWthemefun callback)

View File

@ -586,8 +586,13 @@ struct _GLFWwindow
GLFWdropfun drop; GLFWdropfun drop;
} callbacks; } callbacks;
// Clients can mutate this theme data at any time struct {
_GLFWtheme theme; // Clients can mutate this theme data at any time
_GLFWtheme external;
// The window's user-specified theme settings.
_GLFWtheme internal; // TODO: init to defaults on window creation. TODO: set in glfwSetTheme
} theme;
// This is defined in platform.h // This is defined in platform.h
GLFW_PLATFORM_WINDOW_STATE GLFW_PLATFORM_WINDOW_STATE
@ -754,8 +759,8 @@ struct _GLFWplatform
void (*setWindowFloating)(_GLFWwindow*,GLFWbool); void (*setWindowFloating)(_GLFWwindow*,GLFWbool);
void (*setWindowOpacity)(_GLFWwindow*,float); void (*setWindowOpacity)(_GLFWwindow*,float);
void (*setWindowMousePassthrough)(_GLFWwindow*,GLFWbool); void (*setWindowMousePassthrough)(_GLFWwindow*,GLFWbool);
_GLFWtheme* (*getTheme)(_GLFWwindow*); _GLFWtheme* (*getTheme)(_GLFWwindow*,int);
void (*setTheme)(_GLFWwindow*,_GLFWtheme*); void (*setTheme)(_GLFWwindow*,const _GLFWtheme*);
// Events // Events
void (*pollEvents)(void); void (*pollEvents)(void);
void (*waitEvents)(void); void (*waitEvents)(void);
@ -888,7 +893,6 @@ struct _GLFWlibrary
GLFWthemefun theme; GLFWthemefun theme;
} callbacks; } callbacks;
// Clients can mutate this theme data at any time
_GLFWtheme theme; _GLFWtheme theme;
// These are defined in platform.h // These are defined in platform.h
@ -1035,3 +1039,4 @@ void* _glfw_calloc(size_t count, size_t size);
void* _glfw_realloc(void* pointer, size_t size); void* _glfw_realloc(void* pointer, size_t size);
void _glfw_free(void* pointer); void _glfw_free(void* pointer);
void _glfwInitDefaultTheme(_GLFWtheme* theme);

View File

@ -142,5 +142,10 @@ void _glfwTerminateNull(void)
_GLFWtheme* _glfwGetSystemDefaultThemeNull(void) _GLFWtheme* _glfwGetSystemDefaultThemeNull(void)
{ {
return NULL; // TODO: should probably return a colorless GLFWtheme with baseTheme set to GLFW_THEME_LIGHT _glfwInitDefaultTheme(&_glfw.theme);
// DEFAULT is not a valid return value.
_glfw.theme.variation = GLFW_THEME_LIGHT;
return &_glfw.theme;
} }

View File

@ -148,5 +148,5 @@ VkResult _glfwCreateWindowSurfaceNull(VkInstance instance, _GLFWwindow* window,
void _glfwPollMonitorsNull(void); void _glfwPollMonitorsNull(void);
_GLFWtheme* _glfwGetSystemDefaultThemeNull(void); _GLFWtheme* _glfwGetSystemDefaultThemeNull(void);
void _glfwSetThemeNull(_GLFWwindow* window, _GLFWtheme* theme); void _glfwSetThemeNull(_GLFWwindow* window, const _GLFWtheme* theme);
_GLFWtheme* _glfwGetThemeNull(_GLFWwindow* window); _GLFWtheme* _glfwGetThemeNull(_GLFWwindow* window, int inlineDefaults);

View File

@ -551,13 +551,16 @@ const char* _glfwGetClipboardStringNull(void)
return _glfw.null.clipboardString; return _glfw.null.clipboardString;
} }
void _glfwSetThemeNull(_GLFWwindow* window, _GLFWtheme* theme) void _glfwSetThemeNull(_GLFWwindow* window, const _GLFWtheme* theme)
{ {
memcpy(&window->theme.internal, theme, sizeof(_GLFWtheme));
} }
_GLFWtheme* _glfwGetThemeNull(_GLFWwindow* window) _GLFWtheme* _glfwGetThemeNull(_GLFWwindow* window, int inlineDefaults)
{ {
return NULL; // TODO: see to-do in _glfwGetSystemDefaultThemeNull memcpy(&window->theme.external, &window->theme.internal, sizeof(_GLFWtheme));
return &window->theme.external;
} }
EGLenum _glfwGetEGLPlatformNull(EGLint** attribs) EGLenum _glfwGetEGLPlatformNull(EGLint** attribs)

View File

@ -31,12 +31,25 @@
#include <assert.h> #include <assert.h>
static GLFWbool attributesAreValid(int attributes)
{
return (attributes & ~(GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST |
GLFW_THEME_ATTRIBUTE_REDUCE_TRANSPARENCY |
GLFW_THEME_ATTRIBUTE_REDUCE_MOTION |
GLFW_THEME_COLOR_MAIN)) == 0;
}
void _glfwInitDefaultTheme(_GLFWtheme* theme)
{
theme->variation = GLFW_THEME_DEFAULT;
theme->flags = 0;
}
GLFWAPI GLFWtheme* glfwCreateTheme(void) GLFWAPI GLFWtheme* glfwCreateTheme(void)
{ {
_GLFWtheme* theme = _glfw_calloc(1, sizeof(_GLFWtheme)); _GLFWtheme* theme = _glfw_calloc(1, sizeof(_GLFWtheme));
_glfwInitDefaultTheme(theme);
theme->variation = GLFW_THEME_DEFAULT;
theme->flags = 0;
return (GLFWtheme*) theme; return (GLFWtheme*) theme;
} }
@ -51,6 +64,23 @@ GLFWAPI void glfwCopyTheme(const GLFWtheme* source, GLFWtheme* target)
memcpy(target, source, sizeof(_GLFWtheme)); memcpy(target, source, sizeof(_GLFWtheme));
} }
GLFWAPI int glfwThemeEqual(const GLFWtheme* first, const GLFWtheme* second)
{
_GLFWtheme* _first = (_GLFWtheme*) first;
_GLFWtheme* _second = (_GLFWtheme*) second;
if (_first->variation != _second->variation)
return GLFW_FALSE;
if (_first->flags != _second->flags)
return GLFW_FALSE;
if (_first->flags & GLFW_THEME_COLOR_MAIN)
return memcmp(&_first->color, &_second->color, sizeof(float) * 4);
return GLFW_TRUE;
}
GLFWAPI int glfwThemeGetVariation(const GLFWtheme* theme) GLFWAPI int glfwThemeGetVariation(const GLFWtheme* theme)
{ {
assert(theme != NULL); assert(theme != NULL);
@ -68,22 +98,18 @@ GLFWAPI void glfwThemeSetVariation(GLFWtheme* theme, int value)
GLFWAPI int glfwThemeGetAttribute(const GLFWtheme* theme, int attribute) GLFWAPI int glfwThemeGetAttribute(const GLFWtheme* theme, int attribute)
{ {
assert(theme != NULL); assert(theme != NULL);
assert((attribute & ~(GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST | assert(attributesAreValid(attribute));
GLFW_THEME_ATTRIBUTE_VIBRANT |
GLFW_THEME_COLOR_MAIN)) == 0);
return ((_GLFWtheme*) theme)->flags & attribute ? GLFW_TRUE : GLFW_FALSE; return ((_GLFWtheme*) theme)->flags & attribute ? GLFW_TRUE : GLFW_FALSE;
} }
GLFWAPI void glfwThemeSetAttribute(GLFWtheme* theme, int attribute, int value) GLFWAPI void glfwThemeSetAttribute(GLFWtheme* theme, int attribute, int value)
{ {
_GLFWtheme* _theme = (_GLFWtheme*) theme;
assert(theme != NULL); assert(theme != NULL);
assert(value == GLFW_TRUE || value == GLFW_FALSE); assert(value == GLFW_TRUE || value == GLFW_FALSE);
assert((attribute & ~(GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST | assert(attributesAreValid(attribute));
GLFW_THEME_ATTRIBUTE_VIBRANT |
GLFW_THEME_COLOR_MAIN)) == 0);
_GLFWtheme* _theme = (_GLFWtheme*) theme;
if (value == GLFW_TRUE) if (value == GLFW_TRUE)
_theme->flags |= attribute; _theme->flags |= attribute;
@ -93,12 +119,12 @@ GLFWAPI void glfwThemeSetAttribute(GLFWtheme* theme, int attribute, int value)
GLFWAPI void glfwThemeGetColor(const GLFWtheme* theme, int specifier, float* red, float* green, float* blue, float* alpha) GLFWAPI void glfwThemeGetColor(const GLFWtheme* theme, int specifier, float* red, float* green, float* blue, float* alpha)
{ {
const float* color = ((_GLFWtheme*) theme)->color;
assert(theme != NULL); assert(theme != NULL);
assert(specifier == GLFW_THEME_COLOR_MAIN); assert(specifier == GLFW_THEME_COLOR_MAIN);
assert(red != NULL && green != NULL && blue != NULL && alpha != NULL); assert(red != NULL && green != NULL && blue != NULL && alpha != NULL);
const float* color = ((_GLFWtheme*) theme)->color;
*red = color[0]; *red = color[0];
*green = color[1]; *green = color[1];
*blue = color[2]; *blue = color[2];
@ -107,11 +133,11 @@ GLFWAPI void glfwThemeGetColor(const GLFWtheme* theme, int specifier, float* red
GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, int specifier, float red, float green, float blue, float alpha) GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, int specifier, float red, float green, float blue, float alpha)
{ {
float* color = ((_GLFWtheme*) theme)->color;
assert(theme != NULL); assert(theme != NULL);
assert(specifier == GLFW_THEME_COLOR_MAIN); assert(specifier == GLFW_THEME_COLOR_MAIN);
float* color = ((_GLFWtheme*) theme)->color;
color[0] = red; color[0] = red;
color[1] = green; color[1] = green;
color[2] = blue; color[2] = blue;

View File

@ -642,5 +642,5 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window,
const _GLFWfbconfig* fbconfig); const _GLFWfbconfig* fbconfig);
_GLFWtheme* _glfwGetSystemDefaultThemeWin32(void); _GLFWtheme* _glfwGetSystemDefaultThemeWin32(void);
void _glfwSetThemeWin32(_GLFWwindow* window, _GLFWtheme* theme); void _glfwSetThemeWin32(_GLFWwindow* window, const _GLFWtheme* theme);
_GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window); _GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window, int inlineDefaults);

View File

@ -56,12 +56,11 @@ static void applySystemTheme(HWND handle)
} }
} }
static void getAccentColor(float color[4])
static int getAccentColor(float color[4])
{ {
if (!_glfw.win32.uxtheme.uxThemeAvailable) if (!_glfw.win32.uxtheme.uxThemeAvailable)
{ return GLFW_FALSE;
return;
}
UINT dwImmersiveColorType = _glfw.win32.uxtheme.GetImmersiveColorTypeFromName(L"ImmersiveSystemAccent"); UINT dwImmersiveColorType = _glfw.win32.uxtheme.GetImmersiveColorTypeFromName(L"ImmersiveSystemAccent");
UINT dwImmersiveColorSet = _glfw.win32.uxtheme.GetImmersiveUserColorSetPreference(FALSE, FALSE); UINT dwImmersiveColorSet = _glfw.win32.uxtheme.GetImmersiveUserColorSetPreference(FALSE, FALSE);
@ -71,10 +70,12 @@ static void getAccentColor(float color[4])
FALSE, FALSE,
0); 0);
color[0] = (0xFF & rgba); color[0] = (double) (0xFF & rgba);
color[1] = ((0xFF00 & rgba) >> 8) ; color[1] = (double) ((0xFF00 & rgba) >> 8);
color[2] = ((0xFF0000 & rgba) >> 16); color[2] = (double) ((0xFF0000 & rgba) >> 16);
color[3] = ((0xFF000000 & rgba) >> 24); color[3] = (double) ((0xFF000000 & rgba) >> 24);
return GLFW_TRUE;
} }
// Returns the window style for the specified window // Returns the window style for the specified window
@ -2423,7 +2424,7 @@ const char* _glfwGetClipboardStringWin32(void)
return _glfw.win32.clipboardString; return _glfw.win32.clipboardString;
} }
void _glfwSetThemeWin32(_GLFWwindow* window, _GLFWtheme* theme) void _glfwSetThemeWin32(_GLFWwindow* window, const _GLFWtheme* theme)
{ {
if (!theme || theme->variation == GLFW_THEME_DEFAULT) if (!theme || theme->variation == GLFW_THEME_DEFAULT)
{ {
@ -2437,22 +2438,34 @@ void _glfwSetThemeWin32(_GLFWwindow* window, _GLFWtheme* theme)
DWMWA_USE_IMMERSIVE_DARK_MODE, DWMWA_USE_IMMERSIVE_DARK_MODE,
&darkMode, &darkMode,
sizeof(darkMode)); sizeof(darkMode));
// TODO: set accent color
} }
_GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window) _GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window, int inlineDefaults)
{ {
_GLFWtheme* theme = &window->theme; _GLFWtheme* theme = &window->theme.external;
memcpy(theme, &window->theme.internal, sizeof(_GLFWtheme));
// FIXME: fix not overriding specified properties.
if (!inlineDefaults)
return theme;
theme->variation = GLFW_THEME_LIGHT; if (theme->variation == GLFW_THEME_DEFAULT)
theme->flags = 0;
if (_glfw.win32.uxtheme.uxThemeAvailable && _glfw.win32.uxtheme.darkTitleAvailable)
{ {
theme->variation = GLFW_THEME_DARK; theme->variation = GLFW_THEME_LIGHT;
}
memset(theme->color, 0, sizeof(float) * 4); if (_glfw.win32.uxtheme.uxThemeAvailable && _glfw.win32.uxtheme.darkTitleAvailable)
getAccentColor(theme->color); theme->variation = GLFW_THEME_DARK;
}
if ((theme->flags & GLFW_THEME_COLOR_MAIN) == 0)
{
if (getAccentColor(&theme->color))
theme->flags |= GLFW_THEME_COLOR_MAIN;
else
memset(&theme->color, 0, sizeof(float) * 4);
}
return theme; return theme;
} }

View File

@ -244,6 +244,8 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
window->maxheight = GLFW_DONT_CARE; window->maxheight = GLFW_DONT_CARE;
window->numer = GLFW_DONT_CARE; window->numer = GLFW_DONT_CARE;
window->denom = GLFW_DONT_CARE; window->denom = GLFW_DONT_CARE;
_glfwInitDefaultTheme(&window->theme.internal);
if (!_glfw.platform.createWindow(window, &wndconfig, &ctxconfig, &fbconfig)) if (!_glfw.platform.createWindow(window, &wndconfig, &ctxconfig, &fbconfig))
{ {
@ -1163,11 +1165,11 @@ GLFWAPI void glfwSetTheme(GLFWwindow* handle, const GLFWtheme* theme)
_glfw.platform.setTheme(window, (_GLFWtheme*) theme); _glfw.platform.setTheme(window, (_GLFWtheme*) theme);
} }
GLFWAPI GLFWtheme* glfwGetTheme(GLFWwindow* handle) GLFWAPI GLFWtheme* glfwGetTheme(GLFWwindow* handle, int inlineDefaults)
{ {
_GLFWwindow* window = (_GLFWwindow*) handle; _GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL); assert(window != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
return (GLFWtheme*) _glfw.platform.getTheme(window); return (GLFWtheme*) _glfw.platform.getTheme(window, inlineDefaults);
} }

View File

@ -520,5 +520,5 @@ void _glfwAddSeatListenerWayland(struct wl_seat* seat);
void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device); void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device);
_GLFWtheme* _glfwGetSystemDefaultThemeWayland(void); _GLFWtheme* _glfwGetSystemDefaultThemeWayland(void);
void _glfwSetThemeWayland(_GLFWwindow* window, _GLFWtheme* theme); void _glfwSetThemeWayland(_GLFWwindow* window, const _GLFWtheme* theme);
_GLFWtheme* _glfwGetThemeWayland(_GLFWwindow* window); _GLFWtheme* _glfwGetThemeWayland(_GLFWwindow* window, int inlineDefaults);

View File

@ -2780,7 +2780,7 @@ void _glfwSetThemeWayland(_GLFWwindow* window, _GLFWtheme* theme)
_glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL);
} }
_GLFWtheme* _glfwGetThemeWayland(_GLFWwindow* window) _GLFWtheme* _glfwGetThemeWayland(_GLFWwindow* window, int inlineDefaults)
{ {
_glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL);
return NULL; // TODO: implement return NULL; // TODO: implement

View File

@ -1003,5 +1003,5 @@ GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig,
Visual** visual, int* depth); Visual** visual, int* depth);
_GLFWtheme* _glfwGetSystemDefaultThemeX11(void); _GLFWtheme* _glfwGetSystemDefaultThemeX11(void);
void _glfwSetThemeX11(_GLFWwindow* window, _GLFWtheme* theme); void _glfwSetThemeX11(_GLFWwindow* window, const _GLFWtheme* theme);
_GLFWtheme* _glfwGetThemeX11(_GLFWwindow* window); _GLFWtheme* _glfwGetThemeX11(_GLFWwindow* window, int inlineDefaults);

View File

@ -3082,12 +3082,12 @@ const char* _glfwGetClipboardStringX11(void)
return getSelectionString(_glfw.x11.CLIPBOARD); return getSelectionString(_glfw.x11.CLIPBOARD);
} }
void _glfwSetThemeX11(_GLFWwindow* window, _GLFWtheme* theme) void _glfwSetThemeX11(_GLFWwindow* window, const _GLFWtheme* theme)
{ {
_glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL);
} }
_GLFWtheme* _glfwGetThemeX11(_GLFWwindow* window) _GLFWtheme* _glfwGetThemeX11(_GLFWwindow* window, int inlineDefaults)
{ {
_glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL);
return NULL; // TODO: implement return NULL; // TODO: implement

View File

@ -50,26 +50,29 @@ static int cur_theme_color = 0;
static GLFWtheme* theme; static GLFWtheme* theme;
static void print_theme(GLFWtheme* theme, const char* title) static void print_theme(const GLFWtheme* theme, const char* title)
{ {
int n = 0; int n = 0;
int variation = glfwThemeGetVariation(theme);
printf("%s: {\n", title); printf("%s: {\n", title);
printf(" Variation: %s\n", glfwThemeGetVariation(theme) == GLFW_THEME_LIGHT ? "light" : "dark"); printf(" Variation: %s\n", variation == GLFW_THEME_DEFAULT ? "default" : variation == GLFW_THEME_LIGHT ? "light" : "dark");
printf(" Attributes: ["); printf(" Attributes: [");
if (glfwThemeGetAttribute(theme, GLFW_THEME_COLOR_MAIN)) if (glfwThemeGetAttribute(theme, GLFW_THEME_COLOR_MAIN))
{
printf(n++ > 0 ? ", %s" : "%s", "COLOR_MAIN"); printf(n++ > 0 ? ", %s" : "%s", "COLOR_MAIN");
}
if (glfwThemeGetAttribute(theme, GLFW_THEME_ATTRIBUTE_VIBRANT))
{
printf(n++ > 0 ? ", %s" : "%s", "VIBRANT");
}
if (glfwThemeGetAttribute(theme, GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST)) if (glfwThemeGetAttribute(theme, GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST))
{
printf(n++ > 0 ? ", %s" : "%s", "HIGH_CONTRAST"); printf(n++ > 0 ? ", %s" : "%s", "HIGH_CONTRAST");
}
if (glfwThemeGetAttribute(theme, GLFW_THEME_ATTRIBUTE_REDUCE_TRANSPARENCY))
printf(n++ > 0 ? ", %s" : "%s", "REDUCE_TRANSPARENCY");
if (glfwThemeGetAttribute(theme, GLFW_THEME_ATTRIBUTE_REDUCE_MOTION))
printf(n++ > 0 ? ", %s" : "%s", "REDUCE_MOTION");
printf("]\n"); printf("]\n");
if (glfwThemeGetAttribute(theme, GLFW_THEME_COLOR_MAIN)) if (glfwThemeGetAttribute(theme, GLFW_THEME_COLOR_MAIN))
{ {
float r, g, b, a; float r, g, b, a;
@ -90,7 +93,7 @@ static void set_theme(GLFWwindow* window, int theme_color)
theme_colors[theme_color][3] theme_colors[theme_color][3]
); );
if (theme_color == 6) if (theme_color == 5)
glfwThemeSetAttribute(theme, GLFW_THEME_COLOR_MAIN, GLFW_FALSE); glfwThemeSetAttribute(theme, GLFW_THEME_COLOR_MAIN, GLFW_FALSE);
else else
glfwThemeSetAttribute(theme, GLFW_THEME_COLOR_MAIN, GLFW_TRUE); glfwThemeSetAttribute(theme, GLFW_THEME_COLOR_MAIN, GLFW_TRUE);
@ -113,6 +116,11 @@ static void set_theme(GLFWwindow* window, int theme_color)
glfwSetTheme(window, theme); glfwSetTheme(window, theme);
} }
static void flip_attribute(int attribute)
{
glfwThemeSetAttribute(theme, attribute, !glfwThemeGetAttribute(theme, attribute));
}
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{ {
if (action != GLFW_PRESS) if (action != GLFW_PRESS)
@ -120,31 +128,59 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action,
switch (key) switch (key)
{ {
// No theme specified
case GLFW_KEY_0: case GLFW_KEY_0:
glfwSetWindowTitle(window, "Default theme (NULL)"); glfwSetWindowTitle(window, "Default theme (NULL)");
glfwSetTheme(window, NULL); glfwSetTheme(window, NULL);
break; break;
// Theme with default variation
case GLFW_KEY_1: case GLFW_KEY_1:
glfwThemeSetVariation(theme, GLFW_THEME_DEFAULT); glfwThemeSetVariation(theme, GLFW_THEME_DEFAULT);
set_theme(window, cur_theme_color); set_theme(window, cur_theme_color);
break; break;
// Theme with light variation
case GLFW_KEY_2: case GLFW_KEY_2:
glfwThemeSetVariation(theme, GLFW_THEME_LIGHT); glfwThemeSetVariation(theme, GLFW_THEME_LIGHT);
set_theme(window, cur_theme_color); set_theme(window, cur_theme_color);
break; break;
// Theme with dark variation
case GLFW_KEY_3: case GLFW_KEY_3:
glfwThemeSetVariation(theme, GLFW_THEME_DARK); glfwThemeSetVariation(theme, GLFW_THEME_DARK);
set_theme(window, cur_theme_color); set_theme(window, cur_theme_color);
break; break;
case GLFW_KEY_ESCAPE: // Traverse colors
glfwSetWindowShouldClose(window, GLFW_TRUE);
break;
case GLFW_KEY_SPACE: case GLFW_KEY_SPACE:
cur_theme_color = (cur_theme_color + 1) % 6; cur_theme_color = (cur_theme_color + 1) % 6;
set_theme(window, cur_theme_color); set_theme(window, cur_theme_color);
break; break;
// Inspect
case GLFW_KEY_I:
print_theme(theme, "Constructed theme");
break;
// Print
case GLFW_KEY_P: case GLFW_KEY_P:
print_theme(glfwGetTheme(window), "Window theme"); print_theme(glfwGetTheme(window, GLFW_FALSE), "Specified window theme");
print_theme(glfwGetTheme(window, GLFW_TRUE), "Active window theme");
printf("\n");
break;
// High contrast
case GLFW_KEY_H:
flip_attribute(GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST);
set_theme(window, cur_theme_color);
break;
// Transparency
case GLFW_KEY_T:
flip_attribute(GLFW_THEME_ATTRIBUTE_REDUCE_TRANSPARENCY);
set_theme(window, cur_theme_color);
break;
// Motion
case GLFW_KEY_M:
flip_attribute(GLFW_THEME_ATTRIBUTE_REDUCE_MOTION);
set_theme(window, cur_theme_color);
break;
// Exit
case GLFW_KEY_ESCAPE:
glfwSetWindowShouldClose(window, GLFW_TRUE);
break; break;
} }
} }
@ -174,6 +210,10 @@ int main(int argc, char** argv)
fprintf(stderr, "Failed to open GLFW window\n"); fprintf(stderr, "Failed to open GLFW window\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
print_theme(glfwGetTheme(window, GLFW_FALSE), "Initial specified window theme");
print_theme(glfwGetTheme(window, GLFW_TRUE), "Initial active window theme");
printf("\n");
glfwMakeContextCurrent(window); glfwMakeContextCurrent(window);
gladLoadGL(glfwGetProcAddress); gladLoadGL(glfwGetProcAddress);