Add support for system dark mode and accent color detection (macOS and Windows). Add support for dark mode title bar on Windows.
This commit is contained in:
parent
f6714581cc
commit
629ae58a80
|
@ -104,6 +104,13 @@
|
|||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_accent_color" qualifiers="const">
|
||||
<return type="Color" />
|
||||
<description>
|
||||
Returns OS theme accent color. Returns [code]Color(0, 0, 0, 0)[/code], if accent color is unknown.
|
||||
[b]Note:[/b] This method is implemented on macOS and Windows.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_display_cutouts" qualifiers="const">
|
||||
<return type="Rect2[]" />
|
||||
<description>
|
||||
|
@ -644,6 +651,20 @@
|
|||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_dark_mode" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if OS is using dark mode.
|
||||
[b]Note:[/b] This method is implemented on macOS and Windows.
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_dark_mode_supported" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if OS supports dark mode.
|
||||
[b]Note:[/b] This method is implemented on macOS and Windows.
|
||||
</description>
|
||||
</method>
|
||||
<method name="keyboard_get_current_layout" qualifiers="const">
|
||||
<return type="int" />
|
||||
<description>
|
||||
|
|
|
@ -284,6 +284,10 @@ public:
|
|||
virtual void tts_resume() override;
|
||||
virtual void tts_stop() override;
|
||||
|
||||
virtual bool is_dark_mode_supported() const override;
|
||||
virtual bool is_dark_mode() const override;
|
||||
virtual Color get_accent_color() const override;
|
||||
|
||||
virtual Error dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) override;
|
||||
virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) override;
|
||||
|
||||
|
|
|
@ -1682,6 +1682,41 @@ void DisplayServerMacOS::tts_stop() {
|
|||
[tts stopSpeaking];
|
||||
}
|
||||
|
||||
bool DisplayServerMacOS::is_dark_mode_supported() const {
|
||||
if (@available(macOS 10.14, *)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DisplayServerMacOS::is_dark_mode() const {
|
||||
if (@available(macOS 10.14, *)) {
|
||||
if (![[NSUserDefaults standardUserDefaults] objectForKey:@"AppleInterfaceStyle"]) {
|
||||
return false;
|
||||
} else {
|
||||
return ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"] isEqual:@"Dark"]);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Color DisplayServerMacOS::get_accent_color() const {
|
||||
if (@available(macOS 10.14, *)) {
|
||||
NSColor *color = [[NSColor controlAccentColor] colorUsingColorSpace:[NSColorSpace genericRGBColorSpace]];
|
||||
if (color) {
|
||||
CGFloat components[4];
|
||||
[color getRed:&components[0] green:&components[1] blue:&components[2] alpha:&components[3]];
|
||||
return Color(components[0], components[1], components[2], components[3]);
|
||||
} else {
|
||||
return Color(0, 0, 0, 0);
|
||||
}
|
||||
} else {
|
||||
return Color(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Error DisplayServerMacOS::dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
|
|
|
@ -37,6 +37,11 @@
|
|||
#include "scene/resources/texture.h"
|
||||
|
||||
#include <avrt.h>
|
||||
#include <dwmapi.h>
|
||||
|
||||
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
|
||||
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
|
||||
#endif
|
||||
|
||||
#if defined(GLES3_ENABLED)
|
||||
#include "drivers/gles3/rasterizer_gles3.h"
|
||||
|
@ -2391,6 +2396,20 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
|
|||
case WM_PAINT: {
|
||||
Main::force_redraw();
|
||||
} break;
|
||||
case WM_SETTINGCHANGE: {
|
||||
if (lParam && CompareStringOrdinal(reinterpret_cast<LPCWCH>(lParam), -1, L"ImmersiveColorSet", -1, true) == CSTR_EQUAL) {
|
||||
if (is_dark_mode_supported()) {
|
||||
BOOL value = is_dark_mode();
|
||||
::DwmSetWindowAttribute(windows[window_id].hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case WM_THEMECHANGED: {
|
||||
if (is_dark_mode_supported()) {
|
||||
BOOL value = is_dark_mode();
|
||||
::DwmSetWindowAttribute(windows[window_id].hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
|
||||
}
|
||||
} break;
|
||||
case WM_SYSCOMMAND: // Intercept system commands.
|
||||
{
|
||||
switch (wParam) // Check system calls.
|
||||
|
@ -3501,6 +3520,11 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
|
|||
wd.pre_fs_valid = true;
|
||||
}
|
||||
|
||||
if (is_dark_mode_supported()) {
|
||||
BOOL value = is_dark_mode();
|
||||
::DwmSetWindowAttribute(wd.hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
|
||||
}
|
||||
|
||||
#ifdef VULKAN_ENABLED
|
||||
if (context_vulkan) {
|
||||
if (context_vulkan->window_create(id, p_vsync_mode, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) == -1) {
|
||||
|
@ -3587,6 +3611,14 @@ WTInfoPtr DisplayServerWindows::wintab_WTInfo = nullptr;
|
|||
WTPacketPtr DisplayServerWindows::wintab_WTPacket = nullptr;
|
||||
WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr;
|
||||
|
||||
// UXTheme API.
|
||||
bool DisplayServerWindows::ux_theme_available = false;
|
||||
IsDarkModeAllowedForAppPtr DisplayServerWindows::IsDarkModeAllowedForApp = nullptr;
|
||||
ShouldAppsUseDarkModePtr DisplayServerWindows::ShouldAppsUseDarkMode = nullptr;
|
||||
GetImmersiveColorFromColorSetExPtr DisplayServerWindows::GetImmersiveColorFromColorSetEx = nullptr;
|
||||
GetImmersiveColorTypeFromNamePtr DisplayServerWindows::GetImmersiveColorTypeFromName = nullptr;
|
||||
GetImmersiveUserColorSetPreferencePtr DisplayServerWindows::GetImmersiveUserColorSetPreference = nullptr;
|
||||
|
||||
// Windows Ink API.
|
||||
bool DisplayServerWindows::winink_available = false;
|
||||
GetPointerTypePtr DisplayServerWindows::win8p_GetPointerType = nullptr;
|
||||
|
@ -3598,6 +3630,23 @@ typedef enum _SHC_PROCESS_DPI_AWARENESS {
|
|||
SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2
|
||||
} SHC_PROCESS_DPI_AWARENESS;
|
||||
|
||||
bool DisplayServerWindows::is_dark_mode_supported() const {
|
||||
return ux_theme_available && IsDarkModeAllowedForApp();
|
||||
}
|
||||
|
||||
bool DisplayServerWindows::is_dark_mode() const {
|
||||
return ux_theme_available && ShouldAppsUseDarkMode();
|
||||
}
|
||||
|
||||
Color DisplayServerWindows::get_accent_color() const {
|
||||
if (!ux_theme_available) {
|
||||
return Color(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
int argb = GetImmersiveColorFromColorSetEx((UINT)GetImmersiveUserColorSetPreference(false, false), GetImmersiveColorTypeFromName(L"ImmersiveSystemAccent"), false, 0);
|
||||
return Color((argb & 0xFF) / 255.f, ((argb & 0xFF00) >> 8) / 255.f, ((argb & 0xFF0000) >> 16) / 255.f, ((argb & 0xFF000000) >> 24) / 255.f);
|
||||
}
|
||||
|
||||
int DisplayServerWindows::tablet_get_driver_count() const {
|
||||
return tablet_drivers.size();
|
||||
}
|
||||
|
@ -3655,6 +3704,18 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
|
|||
// Enforce default keep screen on value.
|
||||
screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));
|
||||
|
||||
// Load UXTheme
|
||||
HMODULE ux_theme_lib = LoadLibraryW(L"uxtheme.dll");
|
||||
if (ux_theme_lib) {
|
||||
IsDarkModeAllowedForApp = (IsDarkModeAllowedForAppPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(136));
|
||||
ShouldAppsUseDarkMode = (ShouldAppsUseDarkModePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(132));
|
||||
GetImmersiveColorFromColorSetEx = (GetImmersiveColorFromColorSetExPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(95));
|
||||
GetImmersiveColorTypeFromName = (GetImmersiveColorTypeFromNamePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(96));
|
||||
GetImmersiveUserColorSetPreference = (GetImmersiveUserColorSetPreferencePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(98));
|
||||
|
||||
ux_theme_available = IsDarkModeAllowedForApp && ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference;
|
||||
}
|
||||
|
||||
// Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink.
|
||||
HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll");
|
||||
if (wintab_lib) {
|
||||
|
|
|
@ -152,6 +152,12 @@ typedef UINT(WINAPI *WTInfoPtr)(UINT p_category, UINT p_index, LPVOID p_output);
|
|||
typedef BOOL(WINAPI *WTPacketPtr)(HANDLE p_ctx, UINT p_param, LPVOID p_packets);
|
||||
typedef BOOL(WINAPI *WTEnablePtr)(HANDLE p_ctx, BOOL p_enable);
|
||||
|
||||
typedef bool(WINAPI *IsDarkModeAllowedForAppPtr)();
|
||||
typedef bool(WINAPI *ShouldAppsUseDarkModePtr)();
|
||||
typedef DWORD(WINAPI *GetImmersiveColorFromColorSetExPtr)(UINT dwImmersiveColorSet, UINT dwImmersiveColorType, bool bIgnoreHighContrast, UINT dwHighContrastCacheMode);
|
||||
typedef int(WINAPI *GetImmersiveColorTypeFromNamePtr)(const WCHAR *name);
|
||||
typedef int(WINAPI *GetImmersiveUserColorSetPreferencePtr)(bool bForceCheckRegistry, bool bSkipCheckOnFail);
|
||||
|
||||
// Windows Ink API
|
||||
#ifndef POINTER_STRUCTURES
|
||||
|
||||
|
@ -278,6 +284,14 @@ class DisplayServerWindows : public DisplayServer {
|
|||
|
||||
_THREAD_SAFE_CLASS_
|
||||
|
||||
// UXTheme API
|
||||
static bool ux_theme_available;
|
||||
static IsDarkModeAllowedForAppPtr IsDarkModeAllowedForApp;
|
||||
static ShouldAppsUseDarkModePtr ShouldAppsUseDarkMode;
|
||||
static GetImmersiveColorFromColorSetExPtr GetImmersiveColorFromColorSetEx;
|
||||
static GetImmersiveColorTypeFromNamePtr GetImmersiveColorTypeFromName;
|
||||
static GetImmersiveUserColorSetPreferencePtr GetImmersiveUserColorSetPreference;
|
||||
|
||||
// WinTab API
|
||||
static bool wintab_available;
|
||||
static WTOpenPtr wintab_WTOpen;
|
||||
|
@ -485,6 +499,10 @@ public:
|
|||
virtual void tts_resume() override;
|
||||
virtual void tts_stop() override;
|
||||
|
||||
virtual bool is_dark_mode_supported() const override;
|
||||
virtual bool is_dark_mode() const override;
|
||||
virtual Color get_accent_color() const override;
|
||||
|
||||
virtual void mouse_set_mode(MouseMode p_mode) override;
|
||||
virtual MouseMode mouse_get_mode() const override;
|
||||
|
||||
|
|
|
@ -586,6 +586,10 @@ void DisplayServer::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("tts_set_utterance_callback", "event", "callable"), &DisplayServer::tts_set_utterance_callback);
|
||||
ClassDB::bind_method(D_METHOD("_tts_post_utterance_event", "event", "id", "char_pos"), &DisplayServer::tts_post_utterance_event);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_dark_mode_supported"), &DisplayServer::is_dark_mode_supported);
|
||||
ClassDB::bind_method(D_METHOD("is_dark_mode"), &DisplayServer::is_dark_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_accent_color"), &DisplayServer::get_accent_color);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("mouse_set_mode", "mouse_mode"), &DisplayServer::mouse_set_mode);
|
||||
ClassDB::bind_method(D_METHOD("mouse_get_mode"), &DisplayServer::mouse_get_mode);
|
||||
|
||||
|
|
|
@ -210,6 +210,10 @@ public:
|
|||
virtual void tts_set_utterance_callback(TTSUtteranceEvent p_event, const Callable &p_callable);
|
||||
virtual void tts_post_utterance_event(TTSUtteranceEvent p_event, int p_id, int p_pos = 0);
|
||||
|
||||
virtual bool is_dark_mode_supported() const { return false; };
|
||||
virtual bool is_dark_mode() const { return false; };
|
||||
virtual Color get_accent_color() const { return Color(0, 0, 0, 0); };
|
||||
|
||||
enum MouseMode {
|
||||
MOUSE_MODE_VISIBLE,
|
||||
MOUSE_MODE_HIDDEN,
|
||||
|
|
Loading…
Reference in New Issue