diff --git a/doc/classes/NativeMenu.xml b/doc/classes/NativeMenu.xml
index cc81c81a24a..cfe3f3f5c57 100644
--- a/doc/classes/NativeMenu.xml
+++ b/doc/classes/NativeMenu.xml
@@ -53,7 +53,7 @@
An [param accelerator] can optionally be defined, which is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The [param accelerator] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using bitwise OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]).
[b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag].
[b]Note:[/b] This method is implemented on macOS and Windows.
- [b]Note:[/b] [param accelerator] and [param key_callback] are ignored on Windows.
+ [b]Note:[/b] On Windows, [param accelerator] and [param key_callback] are ignored.
@@ -72,7 +72,7 @@
An [param accelerator] can optionally be defined, which is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The [param accelerator] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using bitwise OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]).
[b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag].
[b]Note:[/b] This method is implemented on macOS and Windows.
- [b]Note:[/b] [param accelerator] and [param key_callback] are ignored on Windows.
+ [b]Note:[/b] On Windows, [param accelerator] and [param key_callback] are ignored.
@@ -91,7 +91,7 @@
An [param accelerator] can optionally be defined, which is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The [param accelerator] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using bitwise OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]).
[b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag].
[b]Note:[/b] This method is implemented on macOS and Windows.
- [b]Note:[/b] [param accelerator] and [param key_callback] are ignored on Windows.
+ [b]Note:[/b] On Windows, [param accelerator] and [param key_callback] are ignored.
@@ -111,7 +111,7 @@
[b]Note:[/b] Radio-checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually. See [method set_item_checked] for more info on how to control it.
[b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag].
[b]Note:[/b] This method is implemented on macOS and Windows.
- [b]Note:[/b] [param accelerator] and [param key_callback] are ignored on Windows.
+ [b]Note:[/b] On Windows, [param accelerator] and [param key_callback] are ignored.
@@ -129,7 +129,7 @@
An [param accelerator] can optionally be defined, which is a keyboard shortcut that can be pressed to trigger the menu button even if it's not currently open. The [param accelerator] is generally a combination of [enum KeyModifierMask]s and [enum Key]s using bitwise OR such as [code]KEY_MASK_CTRL | KEY_A[/code] ([kbd]Ctrl + A[/kbd]).
[b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag].
[b]Note:[/b] This method is implemented on macOS and Windows.
- [b]Note:[/b] [param accelerator] and [param key_callback] are ignored on Windows.
+ [b]Note:[/b] On Windows, [param accelerator] and [param key_callback] are ignored.
@@ -151,7 +151,7 @@
[b]Note:[/b] By default, there's no indication of the current item state, it should be changed manually.
[b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag].
[b]Note:[/b] This method is implemented on macOS and Windows.
- [b]Note:[/b] [param accelerator] and [param key_callback] are ignored on Windows.
+ [b]Note:[/b] On Windows, [param accelerator] and [param key_callback] are ignored.
@@ -170,7 +170,7 @@
[b]Note:[/b] Radio-checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually. See [method set_item_checked] for more info on how to control it.
[b]Note:[/b] The [param callback] and [param key_callback] Callables need to accept exactly one Variant parameter, the parameter passed to the Callables will be the value passed to [param tag].
[b]Note:[/b] This method is implemented on macOS and Windows.
- [b]Note:[/b] [param accelerator] and [param key_callback] are ignored on Windows.
+ [b]Note:[/b] On Windows, [param accelerator] and [param key_callback] are ignored.
@@ -496,7 +496,7 @@
- Sets the menu text layout directtion.
+ Sets the menu text layout direction from right-to-left if [param is_rtl] is [code]true[/code].
[b]Note:[/b] This method is implemented on macOS and Windows.
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 3abf0582bca..c2490a43656 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -6754,7 +6754,7 @@ EditorNode::EditorNode() {
ED_SHORTCUT_AND_COMMAND("editor/editor_settings", TTR("Editor Settings..."));
ED_SHORTCUT_OVERRIDE("editor/editor_settings", "macos", KeyModifierMask::META + Key::COMMA);
#ifdef MACOS_ENABLED
- if (global_menu) {
+ if (global_menu && NativeMenu::get_singleton()->has_system_menu(NativeMenu::APPLICATION_MENU_ID)) {
apple_menu = memnew(PopupMenu);
apple_menu->set_system_menu(NativeMenu::APPLICATION_MENU_ID);
main_menu->add_child(apple_menu);
@@ -6879,7 +6879,9 @@ EditorNode::EditorNode() {
help_menu = memnew(PopupMenu);
help_menu->set_name(TTR("Help"));
- help_menu->set_system_menu(NativeMenu::HELP_MENU_ID);
+ if (global_menu && NativeMenu::get_singleton()->has_system_menu(NativeMenu::HELP_MENU_ID)) {
+ help_menu->set_system_menu(NativeMenu::HELP_MENU_ID);
+ }
main_menu->add_child(help_menu);
help_menu->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option));
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 2093f552cee..074d40bf4bd 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -5497,6 +5497,32 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
GetImmersiveColorFromColorSetEx = (GetImmersiveColorFromColorSetExPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(95));
GetImmersiveColorTypeFromName = (GetImmersiveColorTypeFromNamePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(96));
GetImmersiveUserColorSetPreference = (GetImmersiveUserColorSetPreferencePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(98));
+ if (os_ver.dwBuildNumber >= 17763) {
+ AllowDarkModeForAppPtr AllowDarkModeForApp = nullptr;
+ SetPreferredAppModePtr SetPreferredAppMode = nullptr;
+ FlushMenuThemesPtr FlushMenuThemes = nullptr;
+ if (os_ver.dwBuildNumber < 18362) {
+ AllowDarkModeForApp = (AllowDarkModeForAppPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(135));
+ } else {
+ SetPreferredAppMode = (SetPreferredAppModePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(135));
+ FlushMenuThemes = (FlushMenuThemesPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(136));
+ }
+ RefreshImmersiveColorPolicyStatePtr RefreshImmersiveColorPolicyState = (RefreshImmersiveColorPolicyStatePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(104));
+ if (ShouldAppsUseDarkMode) {
+ bool dark_mode = ShouldAppsUseDarkMode();
+ if (SetPreferredAppMode) {
+ SetPreferredAppMode(dark_mode ? APPMODE_ALLOWDARK : APPMODE_DEFAULT);
+ } else if (AllowDarkModeForApp) {
+ AllowDarkModeForApp(dark_mode);
+ }
+ if (RefreshImmersiveColorPolicyState) {
+ RefreshImmersiveColorPolicyState();
+ }
+ if (FlushMenuThemes) {
+ FlushMenuThemes();
+ }
+ }
+ }
ux_theme_available = ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference;
if (os_ver.dwBuildNumber >= 18363) {
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 910b9baa45b..1191f229687 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -154,11 +154,23 @@ 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);
+enum PreferredAppMode {
+ APPMODE_DEFAULT = 0,
+ APPMODE_ALLOWDARK = 1,
+ APPMODE_FORCEDARK = 2,
+ APPMODE_FORCELIGHT = 3,
+ APPMODE_MAX = 4
+};
+
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);
typedef HRESULT(WINAPI *RtlGetVersionPtr)(OSVERSIONINFOW *lpVersionInformation);
+typedef bool(WINAPI *AllowDarkModeForAppPtr)(bool darkMode);
+typedef PreferredAppMode(WINAPI *SetPreferredAppModePtr)(PreferredAppMode appMode);
+typedef void(WINAPI *RefreshImmersiveColorPolicyStatePtr)();
+typedef void(WINAPI *FlushMenuThemesPtr)();
// Windows Ink API
#ifndef POINTER_STRUCTURES
diff --git a/platform/windows/native_menu_windows.cpp b/platform/windows/native_menu_windows.cpp
index 13d1cc2a67a..40a08f87df4 100644
--- a/platform/windows/native_menu_windows.cpp
+++ b/platform/windows/native_menu_windows.cpp
@@ -141,7 +141,7 @@ RID NativeMenuWindows::create_menu() {
ZeroMemory(&menu_info, sizeof(menu_info));
menu_info.cbSize = sizeof(menu_info);
menu_info.fMask = MIM_STYLE;
- menu_info.dwStyle = MNS_NOTIFYBYPOS | MNS_MODELESS;
+ menu_info.dwStyle = MNS_NOTIFYBYPOS;
SetMenuInfo(md->menu, &menu_info);
RID rid = menus.make_rid(md);
@@ -189,7 +189,9 @@ void NativeMenuWindows::popup(const RID &p_rid, const Vector2i &p_position) {
if (md->is_rtl) {
flags |= TPM_LAYOUTRTL;
}
+ SetForegroundWindow(hwnd);
TrackPopupMenuEx(md->menu, flags, p_position.x, p_position.y, hwnd, nullptr);
+ PostMessage(hwnd, WM_NULL, 0, 0);
}
void NativeMenuWindows::set_interface_direction(const RID &p_rid, bool p_is_rtl) {
@@ -556,9 +558,16 @@ int NativeMenuWindows::find_item_index_with_text(const RID &p_rid, const String
ZeroMemory(&item, sizeof(item));
item.cbSize = sizeof(item);
item.fMask = MIIM_STRING;
+ item.dwTypeData = nullptr;
if (GetMenuItemInfoW(md->menu, i, true, &item)) {
- if (String::utf16((const char16_t *)item.dwTypeData) == p_text) {
- return i;
+ item.cch++;
+ Char16String str;
+ str.resize(item.cch);
+ item.dwTypeData = (LPWSTR)str.ptrw();
+ if (GetMenuItemInfoW(md->menu, i, true, &item)) {
+ if (String::utf16((const char16_t *)str.get_data()) == p_text) {
+ return i;
+ }
}
}
}
@@ -700,8 +709,15 @@ String NativeMenuWindows::get_item_text(const RID &p_rid, int p_idx) const {
ZeroMemory(&item, sizeof(item));
item.cbSize = sizeof(item);
item.fMask = MIIM_STRING;
+ item.dwTypeData = nullptr;
if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
- return String::utf16((const char16_t *)item.dwTypeData);
+ item.cch++;
+ Char16String str;
+ str.resize(item.cch);
+ item.dwTypeData = (LPWSTR)str.ptrw();
+ if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
+ return String::utf16((const char16_t *)str.get_data());
+ }
}
return String();
}