From 4fc07a8edbc6dbc11b9e47e49a98928b5ee5d4c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pa=CC=84vels=20Nadtoc=CC=8Cajevs?= <7645683+bruvzg@users.noreply.github.com> Date: Wed, 30 Jul 2025 20:07:36 +0300 Subject: [PATCH] [macOS] Add option for renaming system menus. --- doc/classes/NativeMenu.xml | 17 +++++++++ editor/project_manager/project_manager.cpp | 21 +++++++++++ platform/macos/native_menu_macos.h | 3 ++ platform/macos/native_menu_macos.mm | 41 ++++++++++++++++++++++ scene/gui/menu_bar.cpp | 19 +++++++--- scene/gui/menu_bar.h | 1 + servers/display/native_menu.cpp | 12 +++++++ servers/display/native_menu.h | 3 ++ 8 files changed, 113 insertions(+), 4 deletions(-) diff --git a/doc/classes/NativeMenu.xml b/doc/classes/NativeMenu.xml index fc8cb530be8..499be957ff0 100644 --- a/doc/classes/NativeMenu.xml +++ b/doc/classes/NativeMenu.xml @@ -401,6 +401,14 @@ [b]Note:[/b] This method is implemented only on macOS. + + + + + Returns the text of the system menu item. + [b]Note:[/b] This method is implemented on macOS. + + @@ -720,6 +728,15 @@ [b]Note:[/b] This method is implemented only on macOS. + + + + + + Sets the text of the system menu item. + [b]Note:[/b] This method is implemented on macOS. + + diff --git a/editor/project_manager/project_manager.cpp b/editor/project_manager/project_manager.cpp index 70c55514dfa..b93d35b2f68 100644 --- a/editor/project_manager/project_manager.cpp +++ b/editor/project_manager/project_manager.cpp @@ -57,6 +57,7 @@ #include "scene/gui/flow_container.h" #include "scene/gui/line_edit.h" #include "scene/gui/margin_container.h" +#include "scene/gui/menu_bar.h" #include "scene/gui/option_button.h" #include "scene/gui/panel_container.h" #include "scene/gui/rich_text_label.h" @@ -1438,6 +1439,26 @@ ProjectManager::ProjectManager() { left_hbox->add_child(title_bar_logo); title_bar_logo->connect(SceneStringName(pressed), callable_mp(this, &ProjectManager::_show_about)); + bool global_menu = !bool(EDITOR_GET("interface/editor/use_embedded_menu")) && NativeMenu::get_singleton()->has_feature(NativeMenu::FEATURE_GLOBAL_MENU); + if (global_menu) { + MenuBar *main_menu_bar = memnew(MenuBar); + main_menu_bar->set_start_index(0); // Main menu, add to the start of global menu. + main_menu_bar->set_prefer_global_menu(true); + left_hbox->add_child(main_menu_bar); + + if (NativeMenu::get_singleton()->has_system_menu(NativeMenu::WINDOW_MENU_ID)) { + PopupMenu *window_menu = memnew(PopupMenu); + window_menu->set_system_menu(NativeMenu::WINDOW_MENU_ID); + window_menu->set_name(TTRC("Window")); + main_menu_bar->add_child(window_menu); + } + if (NativeMenu::get_singleton()->has_system_menu(NativeMenu::HELP_MENU_ID)) { + PopupMenu *help_menu = memnew(PopupMenu); + help_menu->set_system_menu(NativeMenu::HELP_MENU_ID); + help_menu->set_name(TTRC("Help")); + main_menu_bar->add_child(help_menu); + } + } if (can_expand) { // Spacer to center main toggles. left_spacer = memnew(Control); diff --git a/platform/macos/native_menu_macos.h b/platform/macos/native_menu_macos.h index f307dba01c6..def625cf25c 100644 --- a/platform/macos/native_menu_macos.h +++ b/platform/macos/native_menu_macos.h @@ -89,6 +89,9 @@ public: NSMenu *get_native_menu_handle(const RID &p_rid); + virtual String get_system_menu_text(SystemMenus p_menu_id) const override; + virtual void set_system_menu_text(SystemMenus p_menu_id, const String &p_name) override; + virtual Size2 get_size(const RID &p_rid) const override; virtual void popup(const RID &p_rid, const Vector2i &p_position) override; diff --git a/platform/macos/native_menu_macos.mm b/platform/macos/native_menu_macos.mm index 33bfb210ffd..821969e247d 100644 --- a/platform/macos/native_menu_macos.mm +++ b/platform/macos/native_menu_macos.mm @@ -238,6 +238,47 @@ RID NativeMenuMacOS::get_system_menu(SystemMenus p_menu_id) const { } } +String NativeMenuMacOS::get_system_menu_text(SystemMenus p_menu_id) const { + NSMenu *menu = nullptr; + switch (p_menu_id) { + case WINDOW_MENU_ID: { + menu = window_menu_ns; + } break; + case HELP_MENU_ID: { + menu = help_menu_ns; + } break; + default: + return String(); + } + if (!menu) { + return String(); + } + return String::utf8([[menu title] UTF8String]); +} + +void NativeMenuMacOS::set_system_menu_text(SystemMenus p_menu_id, const String &p_name) { + NSMenu *menu = nullptr; + switch (p_menu_id) { + case WINDOW_MENU_ID: { + menu = window_menu_ns; + } break; + case HELP_MENU_ID: { + menu = help_menu_ns; + } break; + default: + return; + } + if (!menu || !main_menu_ns) { + return; + } + [menu setTitle:[NSString stringWithUTF8String:p_name.utf8().get_data()]]; + int idx = [main_menu_ns indexOfItemWithSubmenu:(NSMenu *)menu]; + NSMenuItem *menu_item = [main_menu_ns itemAtIndex:idx]; + if (menu_item) { + [menu_item setTitle:[NSString stringWithUTF8String:p_name.utf8().get_data()]]; + } +} + RID NativeMenuMacOS::create_menu() { MenuData *md = memnew(MenuData); md->menu = [[NSMenu alloc] initWithTitle:@""]; diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp index 6ce7a652946..325ac40ce99 100644 --- a/scene/gui/menu_bar.cpp +++ b/scene/gui/menu_bar.cpp @@ -249,11 +249,13 @@ void MenuBar::bind_global_menu() { if (!popups[i]->is_system_menu()) { int index = nmenu->add_submenu_item(main_menu, menu_cache[i].name, submenu_rid, global_menu_tag + "#" + itos(i), global_start_idx + i); menu_cache.write[i].submenu_rid = submenu_rid; + menu_cache.write[i].sysmenu_id = NativeMenu::INVALID_MENU_ID; nmenu->set_item_hidden(main_menu, index, menu_cache[i].hidden); nmenu->set_item_disabled(main_menu, index, menu_cache[i].disabled); nmenu->set_item_tooltip(main_menu, index, menu_cache[i].tooltip); } else { menu_cache.write[i].submenu_rid = RID(); + menu_cache.write[i].sysmenu_id = popups[i]->get_system_menu(); } } } @@ -278,6 +280,7 @@ void MenuBar::unbind_global_menu() { popups[i]->unbind_global_menu(); menu_cache.write[i].submenu_rid = RID(); } + menu_cache.write[i].sysmenu_id = NativeMenu::INVALID_MENU_ID; } global_menu_tag = String(); @@ -313,10 +316,14 @@ void MenuBar::_notification(int p_what) { RID main_menu = is_global ? nmenu->get_system_menu(NativeMenu::MAIN_MENU_ID) : RID(); for (int i = 0; i < menu_cache.size(); i++) { shape(menu_cache.write[i]); - if (is_global && menu_cache[i].submenu_rid.is_valid()) { - int item_idx = nmenu->find_item_index_with_submenu(main_menu, menu_cache[i].submenu_rid); - if (item_idx >= 0) { - nmenu->set_item_text(main_menu, item_idx, atr(menu_cache[i].name)); + if (is_global) { + if (menu_cache[i].submenu_rid.is_valid()) { + int item_idx = nmenu->find_item_index_with_submenu(main_menu, menu_cache[i].submenu_rid); + if (item_idx >= 0) { + nmenu->set_item_text(main_menu, item_idx, atr(menu_cache[i].name)); + } + } else if (menu_cache[i].sysmenu_id != NativeMenu::INVALID_MENU_ID) { + nmenu->set_system_menu_text(menu_cache[i].sysmenu_id, atr(menu_cache[i].name)); } } } @@ -619,6 +626,10 @@ void MenuBar::add_child_notify(Node *p_child) { if (!pm->is_system_menu()) { nmenu->add_submenu_item(main_menu, atr(menu.name), submenu_rid, global_menu_tag + "#" + itos(menu_cache.size() - 1), _find_global_start_index() + menu_cache.size() - 1); menu_cache.write[menu_cache.size() - 1].submenu_rid = submenu_rid; + menu_cache.write[menu_cache.size() - 1].sysmenu_id = NativeMenu::INVALID_MENU_ID; + } else { + menu_cache.write[menu_cache.size() - 1].submenu_rid = RID(); + menu_cache.write[menu_cache.size() - 1].sysmenu_id = pm->get_system_menu(); } } update_minimum_size(); diff --git a/scene/gui/menu_bar.h b/scene/gui/menu_bar.h index 77193d39472..a4ae1c34abc 100644 --- a/scene/gui/menu_bar.h +++ b/scene/gui/menu_bar.h @@ -54,6 +54,7 @@ class MenuBar : public Control { bool hidden = false; bool disabled = false; RID submenu_rid; + NativeMenu::SystemMenus sysmenu_id = NativeMenu::INVALID_MENU_ID; Menu(const String &p_name) { name = p_name; diff --git a/servers/display/native_menu.cpp b/servers/display/native_menu.cpp index c7346637d8c..d1a5bfd78c0 100644 --- a/servers/display/native_menu.cpp +++ b/servers/display/native_menu.cpp @@ -41,6 +41,9 @@ void NativeMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("get_system_menu", "menu_id"), &NativeMenu::get_system_menu); ClassDB::bind_method(D_METHOD("get_system_menu_name", "menu_id"), &NativeMenu::get_system_menu_name); + ClassDB::bind_method(D_METHOD("get_system_menu_text", "menu_id"), &NativeMenu::get_system_menu_text); + ClassDB::bind_method(D_METHOD("set_system_menu_text", "menu_id", "name"), &NativeMenu::set_system_menu_text); + ClassDB::bind_method(D_METHOD("create_menu"), &NativeMenu::create_menu); ClassDB::bind_method(D_METHOD("has_menu", "rid"), &NativeMenu::has_menu); ClassDB::bind_method(D_METHOD("free_menu", "rid"), &NativeMenu::free_menu); @@ -157,6 +160,15 @@ String NativeMenu::get_system_menu_name(SystemMenus p_menu_id) const { } } +String NativeMenu::get_system_menu_text(SystemMenus p_menu_id) const { + WARN_PRINT("Global menus are not supported on this platform."); + return String(); +} + +void NativeMenu::set_system_menu_text(SystemMenus p_menu_id, const String &p_name) { + WARN_PRINT("Global menus are not supported on this platform."); +} + RID NativeMenu::create_menu() { WARN_PRINT("Global menus are not supported on this platform."); return RID(); diff --git a/servers/display/native_menu.h b/servers/display/native_menu.h index d696b7e9364..a58dce6cdb3 100644 --- a/servers/display/native_menu.h +++ b/servers/display/native_menu.h @@ -71,6 +71,9 @@ public: virtual RID get_system_menu(SystemMenus p_menu_id) const; virtual String get_system_menu_name(SystemMenus p_menu_id) const; + virtual String get_system_menu_text(SystemMenus p_menu_id) const; + virtual void set_system_menu_text(SystemMenus p_menu_id, const String &p_name); + virtual RID create_menu(); virtual bool has_menu(const RID &p_rid) const; virtual void free_menu(const RID &p_rid);