diff --git a/doc/classes/NativeMenu.xml b/doc/classes/NativeMenu.xml index cc81c81a24ab..cfe3f3f5c57c 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 3abf0582bca0..c2490a436567 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 2093f552cee6..074d40bf4bd2 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 910b9baa45b8..1191f2296876 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 13d1cc2a67a0..40a08f87df4a 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(); }