Merge pull request #87384 from bruvzg/sys_base_color

Add method to get "base" system UI color and system theme change callback.
This commit is contained in:
Rémi Verschelde 2024-02-13 23:43:21 +01:00
commit b6dee8850b
No known key found for this signature in database
GPG key ID: C3336907360768E1
24 changed files with 282 additions and 22 deletions

View file

@ -136,7 +136,7 @@
<description>
Displays OS native dialog for selecting files or directories in the file system.
Callbacks have the following arguments: [code]status: bool, selected_paths: PackedStringArray, selected_filter_index: int[/code].
[b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG] feature. Supported platforms include Linux (X11 and Wayland), Windows, and macOS.
[b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG] feature. Supported platforms include Linux (X11/Wayland), Windows, and macOS.
[b]Note:[/b] [param current_directory] might be ignored.
[b]Note:[/b] On Linux, [param show_hidden] is ignored.
[b]Note:[/b] On macOS, native file dialogs have no title.
@ -161,7 +161,7 @@
- [code]"values"[/code] - [PackedStringArray] of values. If empty, boolean option (check box) is used.
- [code]"default"[/code] - default selected option index ([int]) or default boolean value ([bool]).
Callbacks have the following arguments: [code]status: bool, selected_paths: PackedStringArray, selected_filter_index: int, selected_option: Dictionary[/code].
[b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG] feature. Supported platforms include Linux (X11 and Wayland), Windows, and macOS.
[b]Note:[/b] This method is implemented if the display server has the [constant FEATURE_NATIVE_DIALOG] feature. Supported platforms include Linux (X11/Wayland), Windows, and macOS.
[b]Note:[/b] [param current_directory] might be ignored.
[b]Note:[/b] On Linux (X11), [param show_hidden] is ignored.
[b]Note:[/b] On macOS, native file dialogs have no title.
@ -182,6 +182,13 @@
[b]Note:[/b] This method is implemented on macOS and Windows.
</description>
</method>
<method name="get_base_color" qualifiers="const">
<return type="Color" />
<description>
Returns the OS theme base color (default control background). Returns [code]Color(0, 0, 0, 0)[/code] if the base 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>
@ -1147,6 +1154,14 @@
Sets the window icon (usually displayed in the top-left corner) in the operating system's [i]native[/i] format. The file at [param filename] must be in [code].ico[/code] format on Windows or [code].icns[/code] on macOS. By using specially crafted [code].ico[/code] or [code].icns[/code] icons, [method set_native_icon] allows specifying different icons depending on the size the icon is displayed at. This size is determined by the operating system and user preferences (including the display scale factor). To use icons in other formats, use [method set_icon] instead.
</description>
</method>
<method name="set_system_theme_change_callback">
<return type="void" />
<param index="0" name="callable" type="Callable" />
<description>
Sets the [param callable] that should be called when system theme settings are changed. Callback method should have zero arguments.
[b]Note:[/b] This method is implemented on Android, iOS, macOS, Windows, and Linux (X11/Wayland).
</description>
</method>
<method name="status_indicator_set_callback">
<return type="void" />
<param index="0" name="id" type="int" />

View file

@ -127,6 +127,16 @@ bool DisplayServerAndroid::is_dark_mode() const {
return godot_java->is_dark_mode();
}
void DisplayServerAndroid::set_system_theme_change_callback(const Callable &p_callable) {
system_theme_changed = p_callable;
}
void DisplayServerAndroid::emit_system_theme_changed() {
if (system_theme_changed.is_valid()) {
system_theme_changed.call_deferred();
}
}
void DisplayServerAndroid::clipboard_set(const String &p_text) {
GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
ERR_FAIL_NULL(godot_java);

View file

@ -84,6 +84,8 @@ class DisplayServerAndroid : public DisplayServer {
Callable input_text_callback;
Callable rect_changed_callback;
Callable system_theme_changed;
void _window_callback(const Callable &p_callable, const Variant &p_arg, bool p_deferred = false) const;
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
@ -103,8 +105,11 @@ public:
virtual void tts_resume() override;
virtual void tts_stop() override;
void emit_system_theme_changed();
virtual bool is_dark_mode_supported() const override;
virtual bool is_dark_mode() const override;
virtual void set_system_theme_change_callback(const Callable &p_callable) override;
virtual void clipboard_set(const String &p_text) override;
virtual String clipboard_get() const override;

View file

@ -149,6 +149,7 @@ class Godot(private val context: Context) : SensorEventListener {
private var useApkExpansion = false
private var useImmersive = false
private var useDebugOpengl = false
private var darkMode = false;
private var containerLayout: FrameLayout? = null
var renderView: GodotRenderView? = null
@ -184,6 +185,8 @@ class Godot(private val context: Context) : SensorEventListener {
return
}
darkMode = context.resources?.configuration?.uiMode?.and(Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
beginBenchmarkMeasure("Startup", "Godot::onCreate")
try {
this.primaryHost = primaryHost
@ -559,6 +562,17 @@ class Godot(private val context: Context) : SensorEventListener {
}
}
/**
* Configuration change callback
*/
fun onConfigurationChanged(newConfig: Configuration) {
var newDarkMode = newConfig.uiMode?.and(Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
if (darkMode != newDarkMode) {
darkMode = newDarkMode
GodotLib.onNightModeChanged()
}
}
/**
* Activity result callback
*/
@ -731,7 +745,7 @@ class Godot(private val context: Context) : SensorEventListener {
*/
@Keep
private fun isDarkModeSupported(): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
return context.resources?.configuration?.uiMode?.and(Configuration.UI_MODE_NIGHT_MASK) != Configuration.UI_MODE_NIGHT_UNDEFINED
}
/**
@ -739,10 +753,7 @@ class Godot(private val context: Context) : SensorEventListener {
*/
@Keep
private fun isDarkMode(): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return context.resources?.configuration?.uiMode?.and(Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
}
return false
return darkMode
}
fun hasClipboard(): Boolean {

View file

@ -38,6 +38,7 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.os.Messenger;
@ -145,6 +146,13 @@ public class GodotFragment extends Fragment implements IDownloaderClient, GodotH
parentHost = null;
}
@CallSuper
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
godot.onConfigurationChanged(newConfig);
}
@CallSuper
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {

View file

@ -219,6 +219,11 @@ public class GodotLib {
*/
public static native void requestPermissionResult(String p_permission, boolean p_result);
/**
* Invoked on the theme light/dark mode change.
*/
public static native void onNightModeChanged();
/**
* Invoked on the GL thread to configure the height of the virtual keyboard.
*/

View file

@ -487,6 +487,13 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *
Callable(obj, str_method).call_deferredp(argptrs, count);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onNightModeChanged(JNIEnv *env, jclass clazz) {
DisplayServerAndroid *ds = (DisplayServerAndroid *)DisplayServer::get_singleton();
if (ds) {
ds->emit_system_theme_changed();
}
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result) {
String permission = jstring_to_string(p_permission, env);
if (permission == "android.permission.RECORD_AUDIO" && p_result) {

View file

@ -66,6 +66,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *en
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jclass clazz, jlong ID, jstring method, jobjectArray params);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jclass clazz, jint p_height);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onNightModeChanged(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz);
}

View file

@ -77,6 +77,8 @@ class DisplayServerIOS : public DisplayServer {
Callable input_event_callback;
Callable input_text_callback;
Callable system_theme_changed;
int virtual_keyboard_height = 0;
void perform_event(const Ref<InputEvent> &p_event);
@ -109,6 +111,8 @@ public:
void send_window_event(DisplayServer::WindowEvent p_event) const;
void _window_callback(const Callable &p_callable, const Variant &p_arg) const;
void emit_system_theme_changed();
// MARK: - Input
// MARK: Touches and Apple Pencil
@ -145,6 +149,7 @@ public:
virtual bool is_dark_mode_supported() const override;
virtual bool is_dark_mode() const override;
virtual void set_system_theme_change_callback(const Callable &p_callable) override;
virtual Rect2i get_display_safe_area() const override;
@ -159,8 +164,7 @@ public:
virtual Vector<DisplayServer::WindowID> get_window_list() const override;
virtual WindowID
get_window_at_screen_position(const Point2i &p_position) const override;
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const override;
virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override;

View file

@ -387,6 +387,16 @@ bool DisplayServerIOS::is_dark_mode() const {
}
}
void DisplayServerIOS::set_system_theme_change_callback(const Callable &p_callable) {
system_theme_changed = p_callable;
}
void DisplayServerIOS::emit_system_theme_changed() {
if (system_theme_changed.is_valid()) {
system_theme_changed.call();
}
}
Rect2i DisplayServerIOS::get_display_safe_area() const {
UIEdgeInsets insets = UIEdgeInsetsZero;
UIView *view = AppDelegate.viewController.godotView;

View file

@ -167,6 +167,23 @@ static const float earth_gravity = 9.80665;
}
}
- (void)system_theme_changed {
DisplayServerIOS *ds = (DisplayServerIOS *)DisplayServer::get_singleton();
if (ds) {
ds->emit_system_theme_changed();
}
}
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
if (@available(iOS 13.0, *)) {
[super traitCollectionDidChange:previousTraitCollection];
if ([UITraitCollection currentTraitCollection].userInterfaceStyle != previousTraitCollection.userInterfaceStyle) {
[self system_theme_changed];
}
}
}
- (void)stopRendering {
if (!self.isActive) {
return;

View file

@ -528,10 +528,10 @@ void FreeDesktopPortalDesktop::_file_dialog_callback(const Callable &p_callable,
}
}
void FreeDesktopPortalDesktop::_thread_file_dialog_monitor(void *p_ud) {
void FreeDesktopPortalDesktop::_thread_monitor(void *p_ud) {
FreeDesktopPortalDesktop *portal = (FreeDesktopPortalDesktop *)p_ud;
while (!portal->file_dialog_thread_abort.is_set()) {
while (!portal->monitor_thread_abort.is_set()) {
{
MutexLock lock(portal->file_dialog_mutex);
for (int i = portal->file_dialogs.size() - 1; i >= 0; i--) {
@ -579,10 +579,44 @@ void FreeDesktopPortalDesktop::_thread_file_dialog_monitor(void *p_ud) {
}
}
}
if (portal->theme_connection) {
while (true) {
DBusMessage *msg = dbus_connection_pop_message(portal->theme_connection);
if (!msg) {
break;
} else if (dbus_message_is_signal(msg, "org.freedesktop.portal.Settings", "SettingChanged")) {
DBusMessageIter iter;
if (dbus_message_iter_init(msg, &iter)) {
const char *value;
dbus_message_iter_get_basic(&iter, &value);
String name_space = String::utf8(value);
dbus_message_iter_next(&iter);
dbus_message_iter_get_basic(&iter, &value);
String key = String::utf8(value);
if (name_space == "org.freedesktop.appearance" && key == "color-scheme") {
callable_mp(portal, &FreeDesktopPortalDesktop::_system_theme_changed_callback).call_deferred();
}
}
dbus_message_unref(msg);
break;
}
dbus_message_unref(msg);
}
dbus_connection_read_write(portal->theme_connection, 0);
}
usleep(50000);
}
}
void FreeDesktopPortalDesktop::_system_theme_changed_callback() {
if (system_theme_changed.is_valid()) {
system_theme_changed.call();
}
}
FreeDesktopPortalDesktop::FreeDesktopPortalDesktop() {
#ifdef SOWRAP_ENABLED
#ifdef DEBUG_ENABLED
@ -611,17 +645,34 @@ FreeDesktopPortalDesktop::FreeDesktopPortalDesktop() {
unsupported = true;
}
DBusError err;
dbus_error_init(&err);
theme_connection = dbus_bus_get(DBUS_BUS_SESSION, &err);
if (dbus_error_is_set(&err)) {
dbus_error_free(&err);
} else {
theme_path = "type='signal',sender='org.freedesktop.portal.Desktop',interface='org.freedesktop.portal.Settings',member='SettingChanged'";
dbus_bus_add_match(theme_connection, theme_path.utf8().get_data(), &err);
if (dbus_error_is_set(&err)) {
dbus_error_free(&err);
dbus_connection_unref(theme_connection);
theme_connection = nullptr;
}
dbus_connection_read_write(theme_connection, 0);
}
if (!unsupported) {
file_dialog_thread_abort.clear();
file_dialog_thread.start(FreeDesktopPortalDesktop::_thread_file_dialog_monitor, this);
monitor_thread_abort.clear();
monitor_thread.start(FreeDesktopPortalDesktop::_thread_monitor, this);
}
}
FreeDesktopPortalDesktop::~FreeDesktopPortalDesktop() {
file_dialog_thread_abort.set();
if (file_dialog_thread.is_started()) {
file_dialog_thread.wait_to_finish();
monitor_thread_abort.set();
if (monitor_thread.is_started()) {
monitor_thread.wait_to_finish();
}
for (FreeDesktopPortalDesktop::FileDialogData &fd : file_dialogs) {
if (fd.connection) {
DBusError err;
@ -631,6 +682,13 @@ FreeDesktopPortalDesktop::~FreeDesktopPortalDesktop() {
dbus_connection_unref(fd.connection);
}
}
if (theme_connection) {
DBusError err;
dbus_error_init(&err);
dbus_bus_remove_match(theme_connection, theme_path.utf8().get_data(), &err);
dbus_error_free(&err);
dbus_connection_unref(theme_connection);
}
}
#endif // DBUS_ENABLED

View file

@ -34,6 +34,7 @@
#ifdef DBUS_ENABLED
#include "core/os/thread.h"
#include "core/os/thread_safe.h"
#include "servers/display_server.h"
struct DBusMessage;
@ -68,10 +69,15 @@ private:
Mutex file_dialog_mutex;
Vector<FileDialogData> file_dialogs;
Thread file_dialog_thread;
SafeFlag file_dialog_thread_abort;
Thread monitor_thread;
SafeFlag monitor_thread_abort;
static void _thread_file_dialog_monitor(void *p_ud);
DBusConnection *theme_connection = nullptr;
String theme_path;
Callable system_theme_changed;
void _system_theme_changed_callback();
static void _thread_monitor(void *p_ud);
public:
FreeDesktopPortalDesktop();
@ -86,6 +92,9 @@ public:
// 1: Prefer dark appearance.
// 2: Prefer light appearance.
uint32_t get_appearance_color_scheme();
void set_system_theme_change_callback(const Callable &p_system_theme_changed) {
system_theme_changed = p_system_theme_changed;
}
};
#endif // DBUS_ENABLED

View file

@ -275,6 +275,10 @@ bool DisplayServerWayland::is_dark_mode() const {
}
}
void DisplayServerWayland::set_system_theme_change_callback(const Callable &p_callable) {
portal_desktop->set_system_theme_change_callback(p_callable);
}
Error DisplayServerWayland::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
WindowID window_id = MAIN_WINDOW_ID;
// TODO: Use window IDs for multiwindow support.

View file

@ -171,6 +171,7 @@ public:
#ifdef DBUS_ENABLED
virtual bool is_dark_mode_supported() const override;
virtual bool is_dark_mode() const override;
virtual void set_system_theme_change_callback(const Callable &p_callable) override;
virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) override;
virtual Error file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback) override;

View file

@ -364,6 +364,10 @@ bool DisplayServerX11::is_dark_mode() const {
}
}
void DisplayServerX11::set_system_theme_change_callback(const Callable &p_callable) {
portal_desktop->set_system_theme_change_callback(p_callable);
}
Error DisplayServerX11::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
WindowID window_id = last_focused_window;

View file

@ -400,6 +400,7 @@ public:
#if defined(DBUS_ENABLED)
virtual bool is_dark_mode_supported() const override;
virtual bool is_dark_mode() const override;
virtual void set_system_theme_change_callback(const Callable &p_callable) override;
virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) override;
virtual Error file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback) override;

View file

@ -220,6 +220,8 @@ private:
};
List<MenuCall> deferred_menu_calls;
Callable system_theme_changed;
const NSMenu *_get_menu_root(const String &p_menu_root) const;
NSMenu *_get_menu_root(const String &p_menu_root);
bool _is_menu_opened(NSMenu *p_menu) const;
@ -254,6 +256,8 @@ public:
void menu_open(NSMenu *p_menu);
void menu_close(NSMenu *p_menu);
void emit_system_theme_changed();
bool has_window(WindowID p_window) const;
WindowData &get_window(WindowID p_window);
@ -358,6 +362,8 @@ public:
virtual bool is_dark_mode_supported() const override;
virtual bool is_dark_mode() const override;
virtual Color get_accent_color() const override;
virtual Color get_base_color() const override;
virtual void set_system_theme_change_callback(const Callable &p_callable) 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;

View file

@ -2051,7 +2051,17 @@ bool DisplayServerMacOS::is_dark_mode() const {
Color DisplayServerMacOS::get_accent_color() const {
if (@available(macOS 10.14, *)) {
NSColor *color = [[NSColor controlAccentColor] colorUsingColorSpace:[NSColorSpace genericRGBColorSpace]];
__block NSColor *color = nullptr;
if (@available(macOS 11.0, *)) {
[NSApp.effectiveAppearance performAsCurrentDrawingAppearance:^{
color = [[NSColor controlAccentColor] colorUsingColorSpace:[NSColorSpace genericRGBColorSpace]];
}];
} else {
NSAppearance *saved_appearance = [NSAppearance currentAppearance];
[NSAppearance setCurrentAppearance:[NSApp effectiveAppearance]];
color = [[NSColor controlAccentColor] colorUsingColorSpace:[NSColorSpace genericRGBColorSpace]];
[NSAppearance setCurrentAppearance:saved_appearance];
}
if (color) {
CGFloat components[4];
[color getRed:&components[0] green:&components[1] blue:&components[2] alpha:&components[3]];
@ -2064,6 +2074,41 @@ Color DisplayServerMacOS::get_accent_color() const {
}
}
Color DisplayServerMacOS::get_base_color() const {
if (@available(macOS 10.14, *)) {
__block NSColor *color = nullptr;
if (@available(macOS 11.0, *)) {
[NSApp.effectiveAppearance performAsCurrentDrawingAppearance:^{
color = [[NSColor controlColor] colorUsingColorSpace:[NSColorSpace genericRGBColorSpace]];
}];
} else {
NSAppearance *saved_appearance = [NSAppearance currentAppearance];
[NSAppearance setCurrentAppearance:[NSApp effectiveAppearance]];
color = [[NSColor controlColor] colorUsingColorSpace:[NSColorSpace genericRGBColorSpace]];
[NSAppearance setCurrentAppearance:saved_appearance];
}
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);
}
}
void DisplayServerMacOS::set_system_theme_change_callback(const Callable &p_callable) {
system_theme_changed = p_callable;
}
void DisplayServerMacOS::emit_system_theme_changed() {
if (system_theme_changed.is_valid()) {
system_theme_changed.call();
}
}
Error DisplayServerMacOS::dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) {
_THREAD_SAFE_METHOD_

View file

@ -116,6 +116,13 @@
[[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps];
}
- (void)system_theme_changed:(NSNotification *)notification {
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
if (ds) {
ds->emit_system_theme_changed();
}
}
- (void)applicationDidFinishLaunching:(NSNotification *)notice {
NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
NSString *nsbundleid_env = [NSString stringWithUTF8String:getenv("__CFBundleIdentifier")];
@ -124,6 +131,8 @@
// If the executable is started from terminal or is not bundled, macOS WindowServer won't register and activate app window correctly (menu and title bar are grayed out and input ignored).
[self performSelector:@selector(forceUnbundledWindowActivationHackStep1) withObject:nil afterDelay:0.02];
}
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(system_theme_changed:) name:@"AppleInterfaceThemeChangedNotification" object:nil];
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(system_theme_changed:) name:@"AppleColorPreferencesChangedNotification" object:nil];
}
- (id)init {
@ -136,6 +145,11 @@
return self;
}
- (void)dealloc {
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:@"AppleInterfaceThemeChangedNotification" object:nil];
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:@"AppleColorPreferencesChangedNotification" object:nil];
}
- (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent {
OS_MacOS *os = (OS_MacOS *)OS::get_singleton();
if (!event || !os) {

View file

@ -3494,13 +3494,17 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
case WM_PAINT: {
Main::force_redraw();
} break;
case WM_SETTINGCHANGE: {
case WM_SETTINGCHANGE:
case WM_SYSCOLORCHANGE: {
if (lParam && CompareStringOrdinal(reinterpret_cast<LPCWCH>(lParam), -1, L"ImmersiveColorSet", -1, true) == CSTR_EQUAL) {
if (is_dark_mode_supported() && dark_title_available) {
BOOL value = is_dark_mode();
::DwmSetWindowAttribute(windows[window_id].hWnd, use_legacy_dark_mode_before_20H1 ? DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 : DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
}
}
if (system_theme_changed.is_valid()) {
system_theme_changed.call();
}
} break;
case WM_THEMECHANGED: {
if (is_dark_mode_supported() && dark_title_available) {
@ -5058,6 +5062,19 @@ Color DisplayServerWindows::get_accent_color() const {
return Color((argb & 0xFF) / 255.f, ((argb & 0xFF00) >> 8) / 255.f, ((argb & 0xFF0000) >> 16) / 255.f, ((argb & 0xFF000000) >> 24) / 255.f);
}
Color DisplayServerWindows::get_base_color() const {
if (!ux_theme_available) {
return Color(0, 0, 0, 0);
}
int argb = GetImmersiveColorFromColorSetEx((UINT)GetImmersiveUserColorSetPreference(false, false), GetImmersiveColorTypeFromName(ShouldAppsUseDarkMode() ? L"ImmersiveDarkChromeMediumLow" : L"ImmersiveLightChromeMediumLow"), false, 0);
return Color((argb & 0xFF) / 255.f, ((argb & 0xFF00) >> 8) / 255.f, ((argb & 0xFF0000) >> 16) / 255.f, ((argb & 0xFF000000) >> 24) / 255.f);
}
void DisplayServerWindows::set_system_theme_change_callback(const Callable &p_callable) {
system_theme_changed = p_callable;
}
int DisplayServerWindows::tablet_get_driver_count() const {
return tablet_drivers.size();
}

View file

@ -477,6 +477,8 @@ class DisplayServerWindows : public DisplayServer {
CursorShape cursor_shape = CursorShape::CURSOR_ARROW;
RBMap<CursorShape, Vector<Variant>> cursors_cache;
Callable system_theme_changed;
void _drag_event(WindowID p_window, float p_x, float p_y, int idx);
void _touch_event(WindowID p_window, bool p_pressed, float p_x, float p_y, int idx);
@ -522,6 +524,8 @@ public:
virtual bool is_dark_mode_supported() const override;
virtual bool is_dark_mode() const override;
virtual Color get_accent_color() const override;
virtual Color get_base_color() const override;
virtual void set_system_theme_change_callback(const Callable &p_callable) override;
virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) override;
virtual Error file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback) override;

View file

@ -711,6 +711,8 @@ void DisplayServer::_bind_methods() {
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("get_base_color"), &DisplayServer::get_base_color);
ClassDB::bind_method(D_METHOD("set_system_theme_change_callback", "callable"), &DisplayServer::set_system_theme_change_callback);
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);

View file

@ -227,7 +227,9 @@ public:
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); };
virtual Color get_accent_color() const { return Color(0, 0, 0, 0); }
virtual Color get_base_color() const { return Color(0, 0, 0, 0); }
virtual void set_system_theme_change_callback(const Callable &p_callable) {}
private:
static bool window_early_clear_override_enabled;