diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 9d36e548a26..7b61fd1ecfe 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -266,7 +266,7 @@ [b]Note:[/b] Native dialogs are not included in this list. - + @@ -291,7 +291,7 @@ [/codeblock] - + @@ -317,7 +317,7 @@ [/codeblock] - + @@ -343,7 +343,7 @@ [/codeblock] - + @@ -370,7 +370,7 @@ [/codeblock] - + @@ -395,7 +395,7 @@ [/codeblock] - + @@ -424,7 +424,7 @@ [/codeblock] - + @@ -450,7 +450,7 @@ [/codeblock] - + @@ -468,7 +468,7 @@ [/codeblock] - + @@ -488,7 +488,7 @@ [/codeblock] - + @@ -504,7 +504,7 @@ [/codeblock] - + @@ -513,7 +513,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -522,7 +522,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -530,7 +530,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -539,7 +539,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -548,7 +548,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -557,7 +557,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -566,7 +566,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -575,7 +575,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -584,7 +584,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -593,7 +593,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -602,7 +602,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -611,7 +611,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -620,7 +620,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -629,14 +629,14 @@ [b]Note:[/b] This method is implemented only on macOS. - + Returns Dictionary of supported system menu IDs and names. [b]Note:[/b] This method is implemented only on macOS. - + @@ -645,7 +645,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -654,7 +654,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -664,7 +664,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -674,7 +674,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -684,7 +684,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -694,7 +694,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -704,7 +704,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -715,7 +715,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -725,7 +725,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -735,7 +735,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -745,7 +745,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -755,7 +755,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -766,7 +766,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -777,7 +777,7 @@ [b]Note:[/b] This method is not supported by macOS "_dock" menu items. - + @@ -787,7 +787,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -798,7 +798,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -808,7 +808,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -819,7 +819,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -829,7 +829,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -839,7 +839,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -849,7 +849,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -859,7 +859,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -869,7 +869,7 @@ [b]Note:[/b] This method is implemented only on macOS. - + @@ -1754,7 +1754,7 @@ - + Display server supports global menu. This allows the application to display its menu items in the operating system's top bar. [b]macOS[/b] diff --git a/doc/classes/NativeMenu.xml b/doc/classes/NativeMenu.xml new file mode 100644 index 00000000000..159666ded8c --- /dev/null +++ b/doc/classes/NativeMenu.xml @@ -0,0 +1,685 @@ + + + + A server interface for OS native menus. + + + [NativeMenu] handles low-level access to the OS native global menu bar and popup menus. + + + + + + + + + + + + + + + Adds a new checkable item with text [param label] to the global menu [param rid]. + Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. + 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 only on macOS. + + + + + + + + + + + + + + Adds a new checkable item with text [param label] and icon [param icon] to the global menu [param rid]. + Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. + 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 only on macOS. + + + + + + + + + + + + + + Adds a new item with text [param label] and icon [param icon] to the global menu [param rid]. + Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. + 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 only on macOS. + + + + + + + + + + + + + + Adds a new radio-checkable item with text [param label] and icon [param icon] to the global menu [param rid]. + Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. + 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] 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 only on macOS. + + + + + + + + + + + + + Adds a new item with text [param label] to the global menu [param rid]. + Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. + 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 only on macOS. + + + + + + + + + + + + + + + Adds a new item with text [param label] to the global menu [param rid]. + Contrarily to normal binary items, multistate items can have more than two states, as defined by [param max_states]. Each press or activate of the item will increase the state by one. The default value is defined by [param default_state]. + Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. + 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] 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 only on macOS. + + + + + + + + + + + + + Adds a new radio-checkable item with text [param label] to the global menu [param rid]. + Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. + 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] 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 only on macOS. + + + + + + + + Adds a separator between items to the global menu [param rid]. Separators also occupy an index. + Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + + + + Adds an item that will act as a submenu of the global menu [param rid]. The [param submenu_rid] argument is the RID of the global menu that will be shown when the item is clicked. + Returns index of the inserted item, it's not guaranteed to be the same as [param index] value. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + Removes all items from the global menu [param rid]. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + Creates a new global menu object. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Returns the index of the item with the specified [param tag]. Index is automatically assigned to each item by the engine. Index can not be set manually. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Returns the index of the item with the specified [param text]. Index is automatically assigned to each item by the engine. Index can not be set manually. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + Frees a global menu object created by this [NativeMenu]. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Returns the accelerator of the item at index [param idx]. Accelerators are special combinations of keys that activate the item, no matter which control is focused. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Returns the callback of the item at index [param idx]. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + Returns number of items in the global menu [param rid]. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Returns the icon of the item at index [param idx]. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Returns the horizontal offset of the item at the given [param idx]. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Returns the callback of the item accelerator at index [param idx]. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Returns number of states of a multistate item. See [method add_multistate_item] for details. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Returns the state of a multistate item. See [method add_multistate_item] for details. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Returns the submenu ID of the item at index [param idx]. See [method add_submenu_item] for more info on how to add a submenu. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Returns the metadata of the specified item, which might be of any type. You can set it with [method set_item_tag], which provides a simple way of assigning context data to items. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Returns the text of the item at index [param idx]. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Returns the tooltip associated with the specified index [param idx]. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + Returns global menu minimum width. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + Returns global menu close callback. + b]Note:[/b] This method is implemented only on macOS. + + + + + + + Returns global menu open callback. + b]Note:[/b] This method is implemented only on macOS. + + + + + + + Returns global menu size. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + Returns RID of a special system menu. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + Returns readable name of a special system menu. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + Returns [code]true[/code] if the specified [param feature] is supported by the current [NativeMenu], [code]false[/code] otherwise. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + Returns [code]true[/code] if [param rid] is valid global menu. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + Returns [code]true[/code] if a special system menu is supported. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Returns [code]true[/code] if the item at index [param idx] is checkable in some way, i.e. if it has a checkbox or radio button. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Returns [code]true[/code] if the item at index [param idx] is checked. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Returns [code]true[/code] if the item at index [param idx] is disabled. When it is disabled it can't be selected, or its action invoked. + See [method set_item_disabled] for more info on how to disable an item. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Returns [code]true[/code] if the item at index [param idx] is hidden. + See [method set_item_hidden] for more info on how to hide an item. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Returns [code]true[/code] if the item at index [param idx] has radio button-style checkability. + [b]Note:[/b] This is purely cosmetic; you must add the logic for checking/unchecking items in radio groups. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + Return [code]true[/code] is global menu is a special system menu. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Shows the global menu at [param position] in the screen coordinates. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Removes the item at index [param idx] from the global menu [param rid]. + [b]Note:[/b] The indices of items after the removed item will be shifted by one. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + + Sets the accelerator of the item at index [param idx]. [param keycode] can be a single [enum Key], or 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] This method is implemented only on macOS. + + + + + + + + + Sets the callback of the item at index [param idx]. Callback is emitted when an item is pressed. + [b]Note:[/b] The [param callback] Callable needs to accept exactly one Variant parameter, the parameter passed to the Callable will be the value passed to the [code]tag[/code] parameter when the menu item was created. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + + Sets whether the item at index [param idx] has a checkbox. If [code]false[/code], sets the type of the item to plain text. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + + Sets the checkstate status of the item at index [param idx]. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + + Enables/disables the item at index [param idx]. When it is disabled, it can't be selected and its action can't be invoked. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + + Hides/shows the item at index [param idx]. When it is hidden, an item does not appear in a menu and its action cannot be invoked. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + + Sets the callback of the item at index [param idx]. The callback is emitted when an item is hovered. + [b]Note:[/b] The [param callback] Callable needs to accept exactly one Variant parameter, the parameter passed to the Callable will be the value passed to the [code]tag[/code] parameter when the menu item was created. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + + Replaces the [Texture2D] icon of the specified [param idx]. + [b]Note:[/b] This method is implemented only on macOS. + [b]Note:[/b] This method is not supported by macOS "_dock" menu items. + + + + + + + + + Sets the horizontal offset of the item at the given [param idx]. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + + Sets the callback of the item at index [param idx]. Callback is emitted when its accelerator is activated. + [b]Note:[/b] The [param key_callback] Callable needs to accept exactly one Variant parameter, the parameter passed to the Callable will be the value passed to the [code]tag[/code] parameter when the menu item was created. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + + Sets number of state of a multistate item. See [method add_multistate_item] for details. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + + Sets the type of the item at the specified index [param idx] to radio button. If [code]false[/code], sets the type of the item to plain text. + [b]Note:[/b] This is purely cosmetic; you must add the logic for checking/unchecking items in radio groups. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + + Sets the state of a multistate item. See [method add_multistate_item] for details. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + + Sets the submenu RID of the item at index [param idx]. The submenu is a global menu that would be shown when the item is clicked. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + + Sets the metadata of an item, which may be of any type. You can later get it with [method get_item_tag], which provides a simple way of assigning context data to items. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + + Sets the text of the item at index [param idx]. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + + Sets the [String] tooltip of the item at the specified index [param idx]. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Sets the minimum width of the global menu. + [b]Note:[/b] This method is implemented only on macOS. + + + + + + + + Registers callable to emit when the menu is about to show. + + + + + + + + Registers callable to emit when the menu is about to closed. + + + + + + [NativeMenu] supports native global main menu. + + + [NativeMenu] supports native popup menus. + + + Invalid special system menu ID. + + + Global main menu ID. + + + Application (first menu after "Apple" menu on macOS) menu ID. + + + "Window" menu ID (on macOS this menu includes standard window control items and a list of open windows). + + + "Help" menu ID (on macOS this menu includes help search bar). + + + Dock icon right-click menu ID (on macOS this menu include standard application control items and a list of open windows). + + + diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml index 266a0940eb4..1e0b4d12e04 100644 --- a/doc/classes/PopupMenu.xml +++ b/doc/classes/PopupMenu.xml @@ -637,8 +637,8 @@ Sets the delay time in seconds for the submenu item to popup on mouse hovering. If the popup menu is added as a child of another (acting as a submenu), it will inherit the delay time of the parent menu item. - - If set to one of the values returned by [method DisplayServer.global_menu_get_system_menu_roots], this [PopupMenu] is bound to the special system menu. Only one [PopupMenu] can be bound to each special menu at a time. + + If set to one of the values of [enum NativeMenu.SystemMenus], this [PopupMenu] is bound to the special system menu. Only one [PopupMenu] can be bound to each special menu at a time. diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 44c9afebb68..d99e1ced576 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -6584,7 +6584,7 @@ EditorNode::EditorNode() { main_screen_vbox->add_theme_constant_override("separation", 0); scene_root_parent->add_child(main_screen_vbox); - bool global_menu = !bool(EDITOR_GET("interface/editor/use_embedded_menu")) && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU); + bool global_menu = !bool(EDITOR_GET("interface/editor/use_embedded_menu")) && NativeMenu::get_singleton()->has_feature(NativeMenu::FEATURE_GLOBAL_MENU); bool can_expand = bool(EDITOR_GET("interface/editor/expand_to_title")) && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_EXTEND_TO_TITLE); if (can_expand) { @@ -6715,7 +6715,7 @@ EditorNode::EditorNode() { #ifdef MACOS_ENABLED if (global_menu) { apple_menu = memnew(PopupMenu); - apple_menu->set_system_menu_root("_apple"); + apple_menu->set_system_menu(NativeMenu::APPLICATION_MENU_ID); main_menu->add_child(apple_menu); apple_menu->add_shortcut(ED_GET_SHORTCUT("editor/editor_settings"), SETTINGS_PREFERENCES); @@ -6838,7 +6838,7 @@ EditorNode::EditorNode() { help_menu = memnew(PopupMenu); help_menu->set_name(TTR("Help")); - help_menu->set_system_menu_root("_help"); + 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/editor/gui/editor_scene_tabs.cpp b/editor/gui/editor_scene_tabs.cpp index fdc0c589cc6..b6cb3d73719 100644 --- a/editor/gui/editor_scene_tabs.cpp +++ b/editor/gui/editor_scene_tabs.cpp @@ -202,22 +202,23 @@ void EditorSceneTabs::update_scene_tabs() { } menu_initialized = true; - if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { - DisplayServer::get_singleton()->global_menu_clear("_dock"); + if (NativeMenu::get_singleton()->has_feature(NativeMenu::FEATURE_GLOBAL_MENU)) { + RID dock_rid = NativeMenu::get_singleton()->get_system_menu(NativeMenu::DOCK_MENU_ID); + NativeMenu::get_singleton()->clear(dock_rid); } scene_tabs->set_block_signals(true); scene_tabs->set_tab_count(EditorNode::get_editor_data().get_edited_scene_count()); scene_tabs->set_block_signals(false); - if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { + if (NativeMenu::get_singleton()->has_feature(NativeMenu::FEATURE_GLOBAL_MENU)) { + RID dock_rid = NativeMenu::get_singleton()->get_system_menu(NativeMenu::DOCK_MENU_ID); for (int i = 0; i < EditorNode::get_editor_data().get_edited_scene_count(); i++) { - int global_menu_index = DisplayServer::get_singleton()->global_menu_add_item("_dock", EditorNode::get_editor_data().get_scene_title(i), callable_mp(this, &EditorSceneTabs::_global_menu_scene), Callable(), i); + int global_menu_index = NativeMenu::get_singleton()->add_item(dock_rid, EditorNode::get_editor_data().get_scene_title(i), callable_mp(this, &EditorSceneTabs::_global_menu_scene), Callable(), i); scene_tabs->set_tab_metadata(i, global_menu_index); } - - DisplayServer::get_singleton()->global_menu_add_separator("_dock"); - DisplayServer::get_singleton()->global_menu_add_item("_dock", TTR("New Window"), callable_mp(this, &EditorSceneTabs::_global_menu_new_window)); + NativeMenu::get_singleton()->add_separator(dock_rid); + NativeMenu::get_singleton()->add_item(dock_rid, TTR("New Window"), callable_mp(this, &EditorSceneTabs::_global_menu_new_window)); } _update_tab_titles(); @@ -247,10 +248,11 @@ void EditorSceneTabs::_update_tab_titles() { bool unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(EditorNode::get_editor_data().get_scene_history_id(i)); scene_tabs->set_tab_title(i, disambiguated_scene_names[i] + (unsaved ? "(*)" : "")); - if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { + if (NativeMenu::get_singleton()->has_feature(NativeMenu::FEATURE_GLOBAL_MENU)) { + RID dock_rid = NativeMenu::get_singleton()->get_system_menu(NativeMenu::DOCK_MENU_ID); int global_menu_index = scene_tabs->get_tab_metadata(i); - DisplayServer::get_singleton()->global_menu_set_item_text("_dock", global_menu_index, EditorNode::get_editor_data().get_scene_title(i) + (unsaved ? "(*)" : "")); - DisplayServer::get_singleton()->global_menu_set_item_tag("_dock", global_menu_index, i); + NativeMenu::get_singleton()->set_item_text(dock_rid, global_menu_index, EditorNode::get_editor_data().get_scene_title(i) + (unsaved ? "(*)" : "")); + NativeMenu::get_singleton()->set_item_tag(dock_rid, global_menu_index, i); } if (show_rb && EditorNode::get_editor_data().get_scene_root_script(i).is_valid()) { diff --git a/editor/project_manager/project_list.cpp b/editor/project_manager/project_list.cpp index 234390c136f..aa93d9414bd 100644 --- a/editor/project_manager/project_list.cpp +++ b/editor/project_manager/project_list.cpp @@ -1012,10 +1012,11 @@ void ProjectList::set_order_option(int p_option) { // Global menu integration. void ProjectList::update_dock_menu() { - if (!DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { + if (!NativeMenu::get_singleton()->has_feature(NativeMenu::FEATURE_GLOBAL_MENU)) { return; } - DisplayServer::get_singleton()->global_menu_clear("_dock"); + RID dock_rid = NativeMenu::get_singleton()->get_system_menu(NativeMenu::DOCK_MENU_ID); + NativeMenu::get_singleton()->clear(dock_rid); int favs_added = 0; int total_added = 0; @@ -1025,18 +1026,18 @@ void ProjectList::update_dock_menu() { favs_added++; } else { if (favs_added != 0) { - DisplayServer::get_singleton()->global_menu_add_separator("_dock"); + NativeMenu::get_singleton()->add_separator(dock_rid); } favs_added = 0; } - DisplayServer::get_singleton()->global_menu_add_item("_dock", _projects[i].project_name + " ( " + _projects[i].path + " )", callable_mp(this, &ProjectList::_global_menu_open_project), Callable(), i); + NativeMenu::get_singleton()->add_item(dock_rid, _projects[i].project_name + " ( " + _projects[i].path + " )", callable_mp(this, &ProjectList::_global_menu_open_project), Callable(), i); total_added++; } } if (total_added != 0) { - DisplayServer::get_singleton()->global_menu_add_separator("_dock"); + NativeMenu::get_singleton()->add_separator(dock_rid); } - DisplayServer::get_singleton()->global_menu_add_item("_dock", TTR("New Window"), callable_mp(this, &ProjectList::_global_menu_new_window)); + NativeMenu::get_singleton()->add_item(dock_rid, TTR("New Window"), callable_mp(this, &ProjectList::_global_menu_new_window)); } void ProjectList::_global_menu_new_window(const Variant &p_tag) { diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index 01ecbc7164e..90759810b1c 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -58,9 +58,13 @@ DisplayServerAndroid *DisplayServerAndroid::get_singleton() { bool DisplayServerAndroid::has_feature(Feature p_feature) const { switch (p_feature) { +#ifndef DISABLE_DEPRECATED + case FEATURE_GLOBAL_MENU: { + return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU)); + } break; +#endif case FEATURE_CURSOR_SHAPE: //case FEATURE_CUSTOM_CURSOR_SHAPE: - //case FEATURE_GLOBAL_MENU: //case FEATURE_HIDPI: //case FEATURE_ICON: //case FEATURE_IME: @@ -578,6 +582,8 @@ DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, Dis keep_screen_on = GLOBAL_GET("display/window/energy_saving/keep_screen_on"); + native_menu = memnew(NativeMenu); + #if defined(GLES3_ENABLED) if (rendering_driver == "opengl3") { RasterizerGLES3::make_current(false); @@ -641,6 +647,11 @@ DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, Dis } DisplayServerAndroid::~DisplayServerAndroid() { + if (native_menu) { + memdelete(native_menu); + native_menu = nullptr; + } + #if defined(RD_ENABLED) if (rendering_device) { memdelete(rendering_device); diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h index c95eaddf93c..e1914f4d18d 100644 --- a/platform/android/display_server_android.h +++ b/platform/android/display_server_android.h @@ -76,6 +76,7 @@ class DisplayServerAndroid : public DisplayServer { RenderingContextDriver *rendering_context = nullptr; RenderingDevice *rendering_device = nullptr; #endif + NativeMenu *native_menu = nullptr; ObjectID window_attached_instance_id; diff --git a/platform/ios/display_server_ios.h b/platform/ios/display_server_ios.h index 6f66783a475..3f9211c572b 100644 --- a/platform/ios/display_server_ios.h +++ b/platform/ios/display_server_ios.h @@ -65,6 +65,7 @@ class DisplayServerIOS : public DisplayServer { RenderingContextDriver *rendering_context = nullptr; RenderingDevice *rendering_device = nullptr; #endif + NativeMenu *native_menu = nullptr; id tts = nullptr; diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm index ed69b91fdd0..e1c3dcd372f 100644 --- a/platform/ios/display_server_ios.mm +++ b/platform/ios/display_server_ios.mm @@ -61,6 +61,7 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode if (tts_enabled) { tts = [[TTS_IOS alloc] init]; } + native_menu = memnew(NativeMenu); #if defined(RD_ENABLED) rendering_context = nullptr; @@ -134,6 +135,11 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode } DisplayServerIOS::~DisplayServerIOS() { + if (native_menu) { + memdelete(native_menu); + native_menu = nullptr; + } + #if defined(RD_ENABLED) if (rendering_device) { rendering_device->screen_free(MAIN_WINDOW_ID); @@ -309,9 +315,13 @@ void DisplayServerIOS::update_gyroscope(float p_x, float p_y, float p_z) { bool DisplayServerIOS::has_feature(Feature p_feature) const { switch (p_feature) { +#ifndef DISABLE_DEPRECATED + case FEATURE_GLOBAL_MENU: { + return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU)); + } break; +#endif // case FEATURE_CURSOR_SHAPE: // case FEATURE_CUSTOM_CURSOR_SHAPE: - // case FEATURE_GLOBAL_MENU: // case FEATURE_HIDPI: // case FEATURE_ICON: // case FEATURE_IME: diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp index 528c688a9ce..c61037d69ac 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.cpp +++ b/platform/linuxbsd/wayland/display_server_wayland.cpp @@ -192,6 +192,11 @@ void DisplayServerWayland::_show_window() { bool DisplayServerWayland::has_feature(Feature p_feature) const { switch (p_feature) { +#ifndef DISABLE_DEPRECATED + case FEATURE_GLOBAL_MENU: { + return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU)); + } break; +#endif case FEATURE_MOUSE: case FEATURE_CLIPBOARD: case FEATURE_CURSOR_SHAPE: @@ -1231,6 +1236,8 @@ DisplayServerWayland::DisplayServerWayland(const String &p_rendering_driver, Win // Input. Input::get_singleton()->set_event_dispatch_function(dispatch_input_events); + native_menu = memnew(NativeMenu); + #ifdef SPEECHD_ENABLED // Init TTS tts = memnew(TTS_Linux); @@ -1355,6 +1362,12 @@ DisplayServerWayland::DisplayServerWayland(const String &p_rendering_driver, Win DisplayServerWayland::~DisplayServerWayland() { // TODO: Multiwindow support. + + if (native_menu) { + memdelete(native_menu); + native_menu = nullptr; + } + if (main_window.visible) { #ifdef VULKAN_ENABLED if (rendering_device) { diff --git a/platform/linuxbsd/wayland/display_server_wayland.h b/platform/linuxbsd/wayland/display_server_wayland.h index 5b8db1be47a..b7d7bee0052 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.h +++ b/platform/linuxbsd/wayland/display_server_wayland.h @@ -132,6 +132,7 @@ class DisplayServerWayland : public DisplayServer { #ifdef SPEECHD_ENABLED TTS_Linux *tts = nullptr; #endif + NativeMenu *native_menu = nullptr; #if DBUS_ENABLED FreeDesktopPortalDesktop *portal_desktop = nullptr; diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index d58b5b93d78..7e7c791e7fe 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -109,6 +109,11 @@ static String get_atom_name(Display *p_disp, Atom p_atom) { bool DisplayServerX11::has_feature(Feature p_feature) const { switch (p_feature) { +#ifndef DISABLE_DEPRECATED + case FEATURE_GLOBAL_MENU: { + return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU)); + } break; +#endif case FEATURE_SUBWINDOWS: #ifdef TOUCH_ENABLED case FEATURE_TOUCHSCREEN: @@ -5765,6 +5770,8 @@ static ::XIMStyle _get_best_xim_style(const ::XIMStyle &p_style_a, const ::XIMSt DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) { KeyMappingX11::initialize(); + native_menu = memnew(NativeMenu); + #ifdef SOWRAP_ENABLED #ifdef DEBUG_ENABLED int dylibloader_verbose = 1; @@ -6357,6 +6364,11 @@ DisplayServerX11::~DisplayServerX11() { events_thread_done.set(); events_thread.wait_to_finish(); + if (native_menu) { + memdelete(native_menu); + native_menu = nullptr; + } + //destroy all windows for (KeyValue &E : windows) { #if defined(RD_ENABLED) diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h index a5cbe34d267..715a8e48e66 100644 --- a/platform/linuxbsd/x11/display_server_x11.h +++ b/platform/linuxbsd/x11/display_server_x11.h @@ -156,6 +156,7 @@ class DisplayServerX11 : public DisplayServer { #ifdef SPEECHD_ENABLED TTS_Linux *tts = nullptr; #endif + NativeMenu *native_menu = nullptr; #if defined(DBUS_ENABLED) FreeDesktopPortalDesktop *portal_desktop = nullptr; diff --git a/platform/macos/SCsub b/platform/macos/SCsub index 355772fcd2a..9083c2a2887 100644 --- a/platform/macos/SCsub +++ b/platform/macos/SCsub @@ -113,6 +113,7 @@ files = [ "godot_menu_delegate.mm", "godot_menu_item.mm", "godot_open_save_delegate.mm", + "native_menu_macos.mm", "dir_access_macos.mm", "tts_macos.mm", "joypad_macos.mm", diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index 7373a402379..09073a80309 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -39,6 +39,8 @@ #include "gl_manager_macos_legacy.h" #endif // GLES3_ENABLED +#include "native_menu_macos.h" + #if defined(RD_ENABLED) #include "servers/rendering/rendering_device.h" @@ -142,19 +144,6 @@ private: #endif String rendering_driver; - NSMenu *apple_menu = nullptr; - NSMenu *window_menu = nullptr; - NSMenu *help_menu = nullptr; - NSMenu *dock_menu = nullptr; - struct MenuData { - Callable open; - Callable close; - NSMenu *menu = nullptr; - bool is_open = false; - }; - HashMap submenu; - HashMap submenu_inv; - struct WarpEvent { NSTimeInterval timestamp; NSPoint delta; @@ -168,6 +157,7 @@ private: id tts = nullptr; id menu_delegate = nullptr; + NativeMenuMacOS *native_menu = nullptr; Point2i im_selection; String im_text; @@ -222,15 +212,10 @@ private: 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; - WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect); void _update_window_style(WindowData p_wd); void _update_displays_arrangement(); - Point2i _get_screens_origin() const; Point2i _get_native_screen_position(int p_screen) const; static void _displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info); @@ -240,27 +225,22 @@ private: void _process_key_events(); void _update_keyboard_layouts(); static void _keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info); - NSImage *_convert_to_nsimg(Ref &p_image) const; static NSCursor *_cursor_from_selector(SEL p_selector, SEL p_fallback = nil); - int _get_system_menu_start(const NSMenu *p_menu) const; - int _get_system_menu_count(const NSMenu *p_menu) const; - NSMenuItem *_menu_add_item(const String &p_menu_root, const String &p_label, Key p_accel, int p_index, int *r_out); - 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 &p_filters, const TypedArray &p_options, const Callable &p_callback, bool p_options_in_cb); public: - NSMenu *get_dock_menu() const; void menu_callback(id p_sender); - 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); + NSImage *_convert_to_nsimg(Ref &p_image) const; + Point2i _get_screens_origin() const; + void send_event(NSEvent *p_event); void send_window_event(const WindowData &p_wd, WindowEvent p_event); void release_pressed_events(); @@ -293,63 +273,6 @@ public: Callable _help_get_search_callback() const; Callable _help_get_action_callback() const; - virtual void global_menu_set_popup_callbacks(const String &p_menu_root, const Callable &p_open_callback = Callable(), const Callable &p_close_callback = Callable()) override; - - virtual int global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index = -1) override; - virtual int global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual int global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual int global_menu_add_icon_item(const String &p_menu_root, const Ref &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual int global_menu_add_icon_check_item(const String &p_menu_root, const Ref &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual int global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual int global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual int global_menu_add_multistate_item(const String &p_menu_root, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual int global_menu_add_separator(const String &p_menu_root, int p_index = -1) override; - - virtual int global_menu_get_item_index_from_text(const String &p_menu_root, const String &p_text) const override; - virtual int global_menu_get_item_index_from_tag(const String &p_menu_root, const Variant &p_tag) const override; - - virtual bool global_menu_is_item_checked(const String &p_menu_root, int p_idx) const override; - virtual bool global_menu_is_item_checkable(const String &p_menu_root, int p_idx) const override; - virtual bool global_menu_is_item_radio_checkable(const String &p_menu_root, int p_idx) const override; - virtual Callable global_menu_get_item_callback(const String &p_menu_root, int p_idx) const override; - virtual Callable global_menu_get_item_key_callback(const String &p_menu_root, int p_idx) const override; - virtual Variant global_menu_get_item_tag(const String &p_menu_root, int p_idx) const override; - virtual String global_menu_get_item_text(const String &p_menu_root, int p_idx) const override; - virtual String global_menu_get_item_submenu(const String &p_menu_root, int p_idx) const override; - virtual Key global_menu_get_item_accelerator(const String &p_menu_root, int p_idx) const override; - virtual bool global_menu_is_item_disabled(const String &p_menu_root, int p_idx) const override; - virtual bool global_menu_is_item_hidden(const String &p_menu_root, int p_idx) const override; - virtual String global_menu_get_item_tooltip(const String &p_menu_root, int p_idx) const override; - virtual int global_menu_get_item_state(const String &p_menu_root, int p_idx) const override; - virtual int global_menu_get_item_max_states(const String &p_menu_root, int p_idx) const override; - virtual Ref global_menu_get_item_icon(const String &p_menu_root, int p_idx) const override; - virtual int global_menu_get_item_indentation_level(const String &p_menu_root, int p_idx) const override; - - virtual void global_menu_set_item_checked(const String &p_menu_root, int p_idx, bool p_checked) override; - virtual void global_menu_set_item_checkable(const String &p_menu_root, int p_idx, bool p_checkable) override; - virtual void global_menu_set_item_radio_checkable(const String &p_menu_root, int p_idx, bool p_checkable) override; - virtual void global_menu_set_item_callback(const String &p_menu_root, int p_idx, const Callable &p_callback) override; - virtual void global_menu_set_item_key_callback(const String &p_menu_root, int p_idx, const Callable &p_key_callback) override; - virtual void global_menu_set_item_hover_callbacks(const String &p_menu_root, int p_idx, const Callable &p_callback) override; - virtual void global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag) override; - virtual void global_menu_set_item_text(const String &p_menu_root, int p_idx, const String &p_text) override; - virtual void global_menu_set_item_submenu(const String &p_menu_root, int p_idx, const String &p_submenu) override; - virtual void global_menu_set_item_accelerator(const String &p_menu_root, int p_idx, Key p_keycode) override; - virtual void global_menu_set_item_disabled(const String &p_menu_root, int p_idx, bool p_disabled) override; - virtual void global_menu_set_item_hidden(const String &p_menu_root, int p_idx, bool p_hidden) override; - virtual void global_menu_set_item_tooltip(const String &p_menu_root, int p_idx, const String &p_tooltip) override; - virtual void global_menu_set_item_state(const String &p_menu_root, int p_idx, int p_state) override; - virtual void global_menu_set_item_max_states(const String &p_menu_root, int p_idx, int p_max_states) override; - virtual void global_menu_set_item_icon(const String &p_menu_root, int p_idx, const Ref &p_icon) override; - virtual void global_menu_set_item_indentation_level(const String &p_menu_root, int p_idx, int p_level) override; - - virtual int global_menu_get_item_count(const String &p_menu_root) const override; - - virtual void global_menu_remove_item(const String &p_menu_root, int p_idx) override; - virtual void global_menu_clear(const String &p_menu_root) override; - - virtual Dictionary global_menu_get_system_menu_roots() const override; - virtual bool tts_is_speaking() const override; virtual bool tts_is_paused() const override; virtual TypedArray tts_get_voices() const override; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index 2d6e614a050..29bc60bd2a1 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -65,66 +65,6 @@ #import #import -#define MENU_TAG_START 0x00004447 -#define MENU_TAG_END 0xFFFF4447 - -const NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) const { - const NSMenu *menu = nullptr; - if (p_menu_root == "" || p_menu_root.to_lower() == "_main") { - // Main menu. - menu = [NSApp mainMenu]; - } else if (p_menu_root.to_lower() == "_dock") { - // macOS dock menu. - menu = dock_menu; - } else if (p_menu_root.to_lower() == "_apple") { - // macOS Apple menu. - menu = apple_menu; - } else if (p_menu_root.to_lower() == "_window") { - // macOS Window menu. - menu = window_menu; - } else if (p_menu_root.to_lower() == "_help") { - // macOS Help menu. - menu = help_menu; - } else { - // Submenu. - if (submenu.has(p_menu_root)) { - menu = submenu[p_menu_root].menu; - } - } - return menu; -} - -NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) { - NSMenu *menu = nullptr; - if (p_menu_root == "" || p_menu_root.to_lower() == "_main") { - // Main menu. - menu = [NSApp mainMenu]; - } else if (p_menu_root.to_lower() == "_dock") { - // macOS dock menu. - menu = dock_menu; - } else if (p_menu_root.to_lower() == "_apple") { - // macOS Apple menu. - menu = apple_menu; - } else if (p_menu_root.to_lower() == "_window") { - // macOS Window menu. - menu = window_menu; - } else if (p_menu_root.to_lower() == "_help") { - // macOS Help menu. - menu = help_menu; - } else { - // Submenu. - if (!submenu.has(p_menu_root)) { - NSMenu *n_menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:p_menu_root.utf8().get_data()]]; - [n_menu setAutoenablesItems:NO]; - [n_menu setDelegate:menu_delegate]; - submenu[p_menu_root].menu = n_menu; - submenu_inv[n_menu] = p_menu_root; - } - menu = submenu[p_menu_root].menu; - } - return menu; -} - DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect) { WindowID id; const float scale = screen_get_max_scale(); @@ -620,42 +560,6 @@ NSCursor *DisplayServerMacOS::_cursor_from_selector(SEL p_selector, SEL p_fallba return [NSCursor arrowCursor]; } -NSMenu *DisplayServerMacOS::get_dock_menu() const { - return dock_menu; -} - -void DisplayServerMacOS::menu_open(NSMenu *p_menu) { - if (submenu_inv.has(p_menu)) { - MenuData &md = submenu[submenu_inv[p_menu]]; - md.is_open = true; - if (md.open.is_valid()) { - Variant ret; - Callable::CallError ce; - - md.open.callp(nullptr, 0, ret, ce); - if (ce.error != Callable::CallError::CALL_OK) { - ERR_PRINT(vformat("Failed to execute menu open callback: %s.", Variant::get_callable_error_text(md.open, nullptr, 0, ce))); - } - } - } -} - -void DisplayServerMacOS::menu_close(NSMenu *p_menu) { - if (submenu_inv.has(p_menu)) { - MenuData &md = submenu[submenu_inv[p_menu]]; - md.is_open = false; - if (md.close.is_valid()) { - Variant ret; - Callable::CallError ce; - - md.close.callp(nullptr, 0, ret, ce); - if (ce.error != Callable::CallError::CALL_OK) { - ERR_PRINT(vformat("Failed to execute menu close callback: %s.", Variant::get_callable_error_text(md.close, nullptr, 0, ce))); - } - } - } -} - void DisplayServerMacOS::menu_callback(id p_sender) { if (![p_sender representedObject]) { return; @@ -839,7 +743,11 @@ void DisplayServerMacOS::window_resize(WindowID p_window, int p_width, int p_hei bool DisplayServerMacOS::has_feature(Feature p_feature) const { switch (p_feature) { - case FEATURE_GLOBAL_MENU: +#ifndef DISABLE_DEPRECATED + case FEATURE_GLOBAL_MENU: { + return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU)); + } break; +#endif case FEATURE_SUBWINDOWS: //case FEATURE_TOUCHSCREEN: case FEATURE_MOUSE: @@ -884,1135 +792,6 @@ Callable DisplayServerMacOS::_help_get_action_callback() const { return help_action_callback; } -bool DisplayServerMacOS::_is_menu_opened(NSMenu *p_menu) const { - if (submenu_inv.has(p_menu)) { - const MenuData &md = submenu[submenu_inv[p_menu]]; - if (md.is_open) { - return true; - } - } - for (NSInteger i = (p_menu == [NSApp mainMenu]) ? 1 : 0; i < [p_menu numberOfItems]; i++) { - const NSMenuItem *menu_item = [p_menu itemAtIndex:i]; - if ([menu_item submenu]) { - if (_is_menu_opened([menu_item submenu])) { - return true; - } - } - } - return false; -} - -int DisplayServerMacOS::_get_system_menu_start(const NSMenu *p_menu) const { - if (p_menu == [NSApp mainMenu]) { // Skip Apple menu. - return 1; - } - if (p_menu == apple_menu || p_menu == window_menu || p_menu == help_menu) { - int count = [p_menu numberOfItems]; - for (int i = 0; i < count; i++) { - NSMenuItem *menu_item = [p_menu itemAtIndex:i]; - if (menu_item.tag == MENU_TAG_START) { - return i + 1; - } - } - } - return 0; -} - -int DisplayServerMacOS::_get_system_menu_count(const NSMenu *p_menu) const { - if (p_menu == [NSApp mainMenu]) { // Skip Apple, Window and Help menu. - return [p_menu numberOfItems] - 3; - } - if (p_menu == apple_menu || p_menu == window_menu || p_menu == help_menu) { - int start = 0; - int count = [p_menu numberOfItems]; - for (int i = 0; i < count; i++) { - NSMenuItem *menu_item = [p_menu itemAtIndex:i]; - if (menu_item.tag == MENU_TAG_START) { - start = i + 1; - } - if (menu_item.tag == MENU_TAG_END) { - return i - start; - } - } - } - return [p_menu numberOfItems]; -} - -NSMenuItem *DisplayServerMacOS::_menu_add_item(const String &p_menu_root, const String &p_label, Key p_accel, int p_index, int *r_out) { - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - String keycode = KeyMappingMacOS::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); - NSMenuItem *menu_item; - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - if (p_index < 0) { - p_index = item_start + item_count; - } else { - p_index += item_start; - p_index = CLAMP(p_index, item_start, item_start + item_count); - } - menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; - *r_out = p_index - item_start; - return menu_item; - } - return nullptr; -} - -int DisplayServerMacOS::global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { - _THREAD_SAFE_METHOD_ - - int out = -1; - NSMenuItem *menu_item = _menu_add_item(p_menu_root, p_label, p_accel, p_index, &out); - if (menu_item) { - GodotMenuItem *obj = [[GodotMenuItem alloc] init]; - obj->callback = p_callback; - obj->key_callback = p_key_callback; - obj->meta = p_tag; - obj->checkable_type = CHECKABLE_TYPE_NONE; - obj->max_states = 0; - obj->state = 0; - [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)]; - [menu_item setRepresentedObject:obj]; - } - return out; -} - -int DisplayServerMacOS::global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { - _THREAD_SAFE_METHOD_ - - int out = -1; - NSMenuItem *menu_item = _menu_add_item(p_menu_root, p_label, p_accel, p_index, &out); - if (menu_item) { - GodotMenuItem *obj = [[GodotMenuItem alloc] init]; - obj->callback = p_callback; - obj->key_callback = p_key_callback; - obj->meta = p_tag; - obj->checkable_type = CHECKABLE_TYPE_CHECK_BOX; - obj->max_states = 0; - obj->state = 0; - [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)]; - [menu_item setRepresentedObject:obj]; - } - return out; -} - -int DisplayServerMacOS::global_menu_add_icon_item(const String &p_menu_root, const Ref &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { - _THREAD_SAFE_METHOD_ - - int out = -1; - NSMenuItem *menu_item = _menu_add_item(p_menu_root, p_label, p_accel, p_index, &out); - if (menu_item) { - GodotMenuItem *obj = [[GodotMenuItem alloc] init]; - obj->callback = p_callback; - obj->key_callback = p_key_callback; - obj->meta = p_tag; - obj->checkable_type = CHECKABLE_TYPE_NONE; - obj->max_states = 0; - obj->state = 0; - if (p_icon.is_valid()) { - obj->img = p_icon->get_image(); - obj->img = obj->img->duplicate(); - if (obj->img->is_compressed()) { - obj->img->decompress(); - } - obj->img->resize(16, 16, Image::INTERPOLATE_LANCZOS); - [menu_item setImage:_convert_to_nsimg(obj->img)]; - } - [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)]; - [menu_item setRepresentedObject:obj]; - } - return out; -} - -int DisplayServerMacOS::global_menu_add_icon_check_item(const String &p_menu_root, const Ref &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { - _THREAD_SAFE_METHOD_ - - int out = -1; - NSMenuItem *menu_item = _menu_add_item(p_menu_root, p_label, p_accel, p_index, &out); - if (menu_item) { - GodotMenuItem *obj = [[GodotMenuItem alloc] init]; - obj->callback = p_callback; - obj->key_callback = p_key_callback; - obj->meta = p_tag; - obj->checkable_type = CHECKABLE_TYPE_CHECK_BOX; - obj->max_states = 0; - obj->state = 0; - if (p_icon.is_valid()) { - obj->img = p_icon->get_image(); - obj->img = obj->img->duplicate(); - if (obj->img->is_compressed()) { - obj->img->decompress(); - } - obj->img->resize(16, 16, Image::INTERPOLATE_LANCZOS); - [menu_item setImage:_convert_to_nsimg(obj->img)]; - } - [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)]; - [menu_item setRepresentedObject:obj]; - } - return out; -} - -int DisplayServerMacOS::global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { - _THREAD_SAFE_METHOD_ - - int out = -1; - NSMenuItem *menu_item = _menu_add_item(p_menu_root, p_label, p_accel, p_index, &out); - if (menu_item) { - GodotMenuItem *obj = [[GodotMenuItem alloc] init]; - obj->callback = p_callback; - obj->key_callback = p_key_callback; - obj->meta = p_tag; - obj->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON; - obj->max_states = 0; - obj->state = 0; - [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)]; - [menu_item setRepresentedObject:obj]; - } - return out; -} - -int DisplayServerMacOS::global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { - _THREAD_SAFE_METHOD_ - - int out = -1; - NSMenuItem *menu_item = _menu_add_item(p_menu_root, p_label, p_accel, p_index, &out); - if (menu_item) { - GodotMenuItem *obj = [[GodotMenuItem alloc] init]; - obj->callback = p_callback; - obj->key_callback = p_key_callback; - obj->meta = p_tag; - obj->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON; - obj->max_states = 0; - obj->state = 0; - if (p_icon.is_valid()) { - obj->img = p_icon->get_image(); - obj->img = obj->img->duplicate(); - if (obj->img->is_compressed()) { - obj->img->decompress(); - } - obj->img->resize(16, 16, Image::INTERPOLATE_LANCZOS); - [menu_item setImage:_convert_to_nsimg(obj->img)]; - } - [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)]; - [menu_item setRepresentedObject:obj]; - } - return out; -} - -int DisplayServerMacOS::global_menu_add_multistate_item(const String &p_menu_root, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { - _THREAD_SAFE_METHOD_ - - int out = -1; - NSMenuItem *menu_item = _menu_add_item(p_menu_root, p_label, p_accel, p_index, &out); - if (menu_item) { - GodotMenuItem *obj = [[GodotMenuItem alloc] init]; - obj->callback = p_callback; - obj->key_callback = p_key_callback; - obj->meta = p_tag; - obj->checkable_type = CHECKABLE_TYPE_NONE; - obj->max_states = p_max_states; - obj->state = p_default_state; - [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)]; - [menu_item setRepresentedObject:obj]; - } - return out; -} - -void DisplayServerMacOS::global_menu_set_popup_callbacks(const String &p_menu_root, const Callable &p_open_callback, const Callable &p_close_callback) { - _THREAD_SAFE_METHOD_ - - if (p_menu_root != "" && p_menu_root.to_lower() != "_main" && p_menu_root.to_lower() != "_dock") { - // Submenu. - if (!submenu.has(p_menu_root)) { - NSMenu *n_menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:p_menu_root.utf8().get_data()]]; - [n_menu setAutoenablesItems:NO]; - [n_menu setDelegate:menu_delegate]; - submenu[p_menu_root].menu = n_menu; - submenu_inv[n_menu] = p_menu_root; - } - submenu[p_menu_root].open = p_open_callback; - submenu[p_menu_root].close = p_close_callback; - } -} - -int DisplayServerMacOS::global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - NSMenu *sub_menu = _get_menu_root(p_submenu); - int out = -1; - if (menu && sub_menu) { - if (sub_menu == menu) { - ERR_PRINT("Can't set submenu to self!"); - return -1; - } - if ([sub_menu supermenu]) { - ERR_PRINT("Can't set submenu to menu that is already a submenu of some other menu!"); - return -1; - } - NSMenuItem *menu_item; - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - if (p_index < 0) { - p_index = item_start + item_count; - } else { - p_index += item_start; - p_index = CLAMP(p_index, item_start, item_start + item_count); - } - menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@"" atIndex:p_index]; - out = p_index - item_start; - - GodotMenuItem *obj = [[GodotMenuItem alloc] init]; - obj->callback = Callable(); - obj->checkable_type = CHECKABLE_TYPE_NONE; - obj->max_states = 0; - obj->state = 0; - [menu_item setRepresentedObject:obj]; - - [sub_menu setTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()]]; - [menu setSubmenu:sub_menu forItem:menu_item]; - } - return out; -} - -int DisplayServerMacOS::global_menu_add_separator(const String &p_menu_root, int p_index) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - if (menu == [NSApp mainMenu]) { // Do not add separators into main menu. - return -1; - } - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - if (p_index < 0) { - p_index = item_start + item_count; - } else { - p_index += item_start; - p_index = CLAMP(p_index, item_start, item_start + item_count); - } - [menu insertItem:[NSMenuItem separatorItem] atIndex:p_index]; - return p_index - item_start; - } - return -1; -} - -int DisplayServerMacOS::global_menu_get_item_index_from_text(const String &p_menu_root, const String &p_text) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - int item_start = _get_system_menu_start(menu); - return [menu indexOfItemWithTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]] - item_start; - } - - return -1; -} - -int DisplayServerMacOS::global_menu_get_item_index_from_tag(const String &p_menu_root, const Variant &p_tag) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - for (NSInteger i = item_start; i < item_start + item_count; i++) { - const NSMenuItem *menu_item = [menu itemAtIndex:i]; - if (menu_item) { - const GodotMenuItem *obj = [menu_item representedObject]; - if (obj && obj->meta == p_tag) { - return i - item_start; - } - } - } - } - - return -1; -} - -bool DisplayServerMacOS::global_menu_is_item_checked(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND_V(p_idx < 0, false); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - return ([menu_item state] == NSControlStateValueOn); - } - } - return false; -} - -bool DisplayServerMacOS::global_menu_is_item_checkable(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND_V(p_idx < 0, false); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - if (obj) { - return obj->checkable_type == CHECKABLE_TYPE_CHECK_BOX; - } - } - } - return false; -} - -bool DisplayServerMacOS::global_menu_is_item_radio_checkable(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND_V(p_idx < 0, false); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - if (obj) { - return obj->checkable_type == CHECKABLE_TYPE_RADIO_BUTTON; - } - } - } - return false; -} - -Callable DisplayServerMacOS::global_menu_get_item_callback(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND_V(p_idx < 0, Callable()); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND_V(p_idx >= item_start + item_count, Callable()); - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - if (obj) { - return obj->callback; - } - } - } - return Callable(); -} - -Callable DisplayServerMacOS::global_menu_get_item_key_callback(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND_V(p_idx < 0, Callable()); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND_V(p_idx >= item_start + item_count, Callable()); - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - if (obj) { - return obj->key_callback; - } - } - } - return Callable(); -} - -Variant DisplayServerMacOS::global_menu_get_item_tag(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND_V(p_idx < 0, Variant()); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND_V(p_idx >= item_start + item_count, Variant()); - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - if (obj) { - return obj->meta; - } - } - } - return Variant(); -} - -String DisplayServerMacOS::global_menu_get_item_text(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND_V(p_idx < 0, String()); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND_V(p_idx >= item_start + item_count, String()); - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - return String::utf8([[menu_item title] UTF8String]); - } - } - return String(); -} - -String DisplayServerMacOS::global_menu_get_item_submenu(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND_V(p_idx < 0, String()); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND_V(p_idx >= item_start + item_count, String()); - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - NSMenu *sub_menu = [menu_item submenu]; - if (sub_menu && submenu_inv.has(sub_menu)) { - return submenu_inv[sub_menu]; - } - } - } - return String(); -} - -Key DisplayServerMacOS::global_menu_get_item_accelerator(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND_V(p_idx < 0, Key::NONE); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND_V(p_idx >= item_start + item_count, Key::NONE); - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - String ret = String::utf8([[menu_item keyEquivalent] UTF8String]); - Key keycode = find_keycode(ret); - NSUInteger mask = [menu_item keyEquivalentModifierMask]; - if (mask & NSEventModifierFlagControl) { - keycode |= KeyModifierMask::CTRL; - } - if (mask & NSEventModifierFlagOption) { - keycode |= KeyModifierMask::ALT; - } - if (mask & NSEventModifierFlagShift) { - keycode |= KeyModifierMask::SHIFT; - } - if (mask & NSEventModifierFlagCommand) { - keycode |= KeyModifierMask::META; - } - if (mask & NSEventModifierFlagNumericPad) { - keycode |= KeyModifierMask::KPAD; - } - return keycode; - } - } - return Key::NONE; -} - -bool DisplayServerMacOS::global_menu_is_item_disabled(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND_V(p_idx < 0, false); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - return ![menu_item isEnabled]; - } - } - return false; -} - -bool DisplayServerMacOS::global_menu_is_item_hidden(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND_V(p_idx < 0, false); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - return [menu_item isHidden]; - } - } - return false; -} - -String DisplayServerMacOS::global_menu_get_item_tooltip(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND_V(p_idx < 0, String()); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND_V(p_idx >= item_start + item_count, String()); - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - return String::utf8([[menu_item toolTip] UTF8String]); - } - } - return String(); -} - -int DisplayServerMacOS::global_menu_get_item_state(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND_V(p_idx < 0, 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND_V(p_idx >= item_start + item_count, 0); - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - if (obj) { - return obj->state; - } - } - } - return 0; -} - -int DisplayServerMacOS::global_menu_get_item_max_states(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND_V(p_idx < 0, 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND_V(p_idx >= item_start + item_count, 0); - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - if (obj) { - return obj->max_states; - } - } - } - return 0; -} - -Ref DisplayServerMacOS::global_menu_get_item_icon(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND_V(p_idx < 0, Ref()); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND_V(p_idx >= item_start + item_count, Ref()); - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - if (obj) { - if (obj->img.is_valid()) { - return ImageTexture::create_from_image(obj->img); - } - } - } - } - return Ref(); -} - -int DisplayServerMacOS::global_menu_get_item_indentation_level(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND_V(p_idx < 0, 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND_V(p_idx >= item_start + item_count, 0); - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - return [menu_item indentationLevel]; - } - } - return 0; -} - -void DisplayServerMacOS::global_menu_set_item_checked(const String &p_menu_root, int p_idx, bool p_checked) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND(p_idx < 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND(p_idx >= item_start + item_count); - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - if (p_checked) { - [menu_item setState:NSControlStateValueOn]; - } else { - [menu_item setState:NSControlStateValueOff]; - } - } - } -} - -void DisplayServerMacOS::global_menu_set_item_checkable(const String &p_menu_root, int p_idx, bool p_checkable) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND(p_idx < 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND(p_idx >= item_start + item_count); - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_NULL(obj); - obj->checkable_type = (p_checkable) ? CHECKABLE_TYPE_CHECK_BOX : CHECKABLE_TYPE_NONE; - } - } -} - -void DisplayServerMacOS::global_menu_set_item_radio_checkable(const String &p_menu_root, int p_idx, bool p_checkable) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND(p_idx < 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND(p_idx >= item_start + item_count); - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_NULL(obj); - obj->checkable_type = (p_checkable) ? CHECKABLE_TYPE_RADIO_BUTTON : CHECKABLE_TYPE_NONE; - } - } -} - -void DisplayServerMacOS::global_menu_set_item_callback(const String &p_menu_root, int p_idx, const Callable &p_callback) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND(p_idx < 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND(p_idx >= item_start + item_count); - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_NULL(obj); - obj->callback = p_callback; - } - } -} - -void DisplayServerMacOS::global_menu_set_item_hover_callbacks(const String &p_menu_root, int p_idx, const Callable &p_callback) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND(p_idx < 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND(p_idx >= item_start + item_count); - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_NULL(obj); - obj->hover_callback = p_callback; - } - } -} - -void DisplayServerMacOS::global_menu_set_item_key_callback(const String &p_menu_root, int p_idx, const Callable &p_key_callback) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND(p_idx < 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND(p_idx >= item_start + item_count); - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_NULL(obj); - obj->key_callback = p_key_callback; - } - } -} - -void DisplayServerMacOS::global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND(p_idx < 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND(p_idx >= item_start + item_count); - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_NULL(obj); - obj->meta = p_tag; - } - } -} - -void DisplayServerMacOS::global_menu_set_item_text(const String &p_menu_root, int p_idx, const String &p_text) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND(p_idx < 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND(p_idx >= item_start + item_count); - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - [menu_item setTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]]; - NSMenu *sub_menu = [menu_item submenu]; - if (sub_menu) { - [sub_menu setTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]]; - } - } - } -} - -void DisplayServerMacOS::global_menu_set_item_submenu(const String &p_menu_root, int p_idx, const String &p_submenu) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu && p_submenu.is_empty()) { - ERR_FAIL_COND(p_idx < 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND(p_idx >= item_start + item_count); - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - if ([menu_item submenu] && _is_menu_opened([menu_item submenu])) { - ERR_PRINT("Can't remove open menu!"); - return; - } - [menu setSubmenu:nil forItem:menu_item]; - } - return; - } - - NSMenu *sub_menu = _get_menu_root(p_submenu); - if (menu && sub_menu) { - if (sub_menu == menu) { - ERR_PRINT("Can't set submenu to self!"); - return; - } - if ([sub_menu supermenu]) { - ERR_PRINT("Can't set submenu to menu that is already a submenu of some other menu!"); - return; - } - ERR_FAIL_COND(p_idx < 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND(p_idx >= item_start + item_count); - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - [menu setSubmenu:sub_menu forItem:menu_item]; - } - } -} - -void DisplayServerMacOS::global_menu_set_item_accelerator(const String &p_menu_root, int p_idx, Key p_keycode) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND(p_idx < 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND(p_idx >= item_start + item_count); - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - if (p_keycode == Key::NONE) { - [menu_item setKeyEquivalent:@""]; - } else { - [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_keycode)]; - String keycode = KeyMappingMacOS::keycode_get_native_string(p_keycode & KeyModifierMask::CODE_MASK); - [menu_item setKeyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; - } - } - } -} - -void DisplayServerMacOS::global_menu_set_item_disabled(const String &p_menu_root, int p_idx, bool p_disabled) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND(p_idx < 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND(p_idx >= item_start + item_count); - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - [menu_item setEnabled:(!p_disabled)]; - } - } -} - -void DisplayServerMacOS::global_menu_set_item_hidden(const String &p_menu_root, int p_idx, bool p_hidden) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND(p_idx < 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND(p_idx >= item_start + item_count); - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - [menu_item setHidden:p_hidden]; - } - } -} - -void DisplayServerMacOS::global_menu_set_item_tooltip(const String &p_menu_root, int p_idx, const String &p_tooltip) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND(p_idx < 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND(p_idx >= item_start + item_count); - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - [menu_item setToolTip:[NSString stringWithUTF8String:p_tooltip.utf8().get_data()]]; - } - } -} - -void DisplayServerMacOS::global_menu_set_item_state(const String &p_menu_root, int p_idx, int p_state) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND(p_idx < 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND(p_idx >= item_start + item_count); - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_NULL(obj); - obj->state = p_state; - } - } -} - -void DisplayServerMacOS::global_menu_set_item_max_states(const String &p_menu_root, int p_idx, int p_max_states) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND(p_idx < 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND(p_idx >= item_start + item_count); - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_NULL(obj); - obj->max_states = p_max_states; - } - } -} - -void DisplayServerMacOS::global_menu_set_item_icon(const String &p_menu_root, int p_idx, const Ref &p_icon) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND(p_idx < 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND(p_idx >= item_start + item_count); - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - ERR_FAIL_NULL(obj); - if (p_icon.is_valid()) { - obj->img = p_icon->get_image(); - obj->img = obj->img->duplicate(); - if (obj->img->is_compressed()) { - obj->img->decompress(); - } - obj->img->resize(16, 16, Image::INTERPOLATE_LANCZOS); - [menu_item setImage:_convert_to_nsimg(obj->img)]; - } else { - obj->img = Ref(); - [menu_item setImage:nil]; - } - } - } -} - -void DisplayServerMacOS::global_menu_set_item_indentation_level(const String &p_menu_root, int p_idx, int p_level) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND(p_idx < 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND(p_idx >= item_start + item_count); - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - [menu_item setIndentationLevel:p_level]; - } - } -} - -int DisplayServerMacOS::global_menu_get_item_count(const String &p_menu_root) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - return _get_system_menu_count(menu); - } else { - return 0; - } -} - -void DisplayServerMacOS::global_menu_remove_item(const String &p_menu_root, int p_idx) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - ERR_FAIL_COND(p_idx < 0); - int item_start = _get_system_menu_start(menu); - int item_count = _get_system_menu_count(menu); - p_idx += item_start; - ERR_FAIL_COND(p_idx >= item_start + item_count); - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if ([menu_item submenu] && _is_menu_opened([menu_item submenu])) { - ERR_PRINT("Can't remove open menu!"); - return; - } - [menu removeItemAtIndex:p_idx]; - } -} - -void DisplayServerMacOS::global_menu_clear(const String &p_menu_root) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - if (_is_menu_opened(menu)) { - ERR_PRINT("Can't remove open menu!"); - return; - } - - if (menu == apple_menu) { - int start = _get_system_menu_start(apple_menu); - int count = _get_system_menu_count(apple_menu); - for (int i = start + count - 1; i >= start; i--) { - [apple_menu removeItemAtIndex:i]; - } - } else if (menu == window_menu) { - int start = _get_system_menu_start(window_menu); - int count = _get_system_menu_count(window_menu); - for (int i = start + count - 1; i >= start; i--) { - [window_menu removeItemAtIndex:i]; - } - } else if (menu == help_menu) { - int start = _get_system_menu_start(help_menu); - int count = _get_system_menu_count(help_menu); - for (int i = start + count - 1; i >= start; i--) { - [help_menu removeItemAtIndex:i]; - } - } else { - [menu removeAllItems]; - } - - // Restore Apple, Window and Help menu. - if (menu == [NSApp mainMenu]) { - NSMenuItem *menu_item = [menu addItemWithTitle:@"" action:nil keyEquivalent:@""]; - [menu setSubmenu:apple_menu forItem:menu_item]; - - menu_item = [menu addItemWithTitle:@"Window" action:nil keyEquivalent:@""]; - [menu setSubmenu:window_menu forItem:menu_item]; - - menu_item = [menu addItemWithTitle:@"Help" action:nil keyEquivalent:@""]; - [menu setSubmenu:help_menu forItem:menu_item]; - } - - if (submenu.has(p_menu_root)) { - submenu_inv.erase(submenu[p_menu_root].menu); - submenu.erase(p_menu_root); - } - } -} - -Dictionary DisplayServerMacOS::global_menu_get_system_menu_roots() const { - Dictionary out; - out["_dock"] = "@Dock"; - out["_apple"] = "@Apple"; - out["_window"] = "Window"; - out["_help"] = "Help"; - return out; -} - bool DisplayServerMacOS::tts_is_speaking() const { ERR_FAIL_NULL_V_MSG(tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return [tts isSpeaking]; @@ -4690,6 +3469,8 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM tts = [[TTS_MacOS alloc] init]; } + native_menu = memnew(NativeMenuMacOS); + NSMenuItem *menu_item; NSString *title; @@ -4701,47 +3482,47 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM menu_delegate = [[GodotMenuDelegate alloc] init]; // Setup Dock menu. - dock_menu = [[NSMenu alloc] initWithTitle:@"_dock"]; + NSMenu *dock_menu = [[NSMenu alloc] initWithTitle:@"_dock"]; [dock_menu setAutoenablesItems:NO]; [dock_menu setDelegate:menu_delegate]; // Setup Apple menu. - apple_menu = [[NSMenu alloc] initWithTitle:@""]; + NSMenu *application_menu = [[NSMenu alloc] initWithTitle:@""]; title = [NSString stringWithFormat:NSLocalizedString(@"About %@", nil), nsappname]; - [apple_menu addItemWithTitle:title action:@selector(showAbout:) keyEquivalent:@""]; - [apple_menu setAutoenablesItems:NO]; - [apple_menu setDelegate:menu_delegate]; + [application_menu addItemWithTitle:title action:@selector(showAbout:) keyEquivalent:@""]; + [application_menu setAutoenablesItems:NO]; + [application_menu setDelegate:menu_delegate]; - [apple_menu addItem:[NSMenuItem separatorItem]]; + [application_menu addItem:[NSMenuItem separatorItem]]; - menu_item = [apple_menu addItemWithTitle:@"_start_" action:nil keyEquivalent:@""]; + menu_item = [application_menu addItemWithTitle:@"_start_" action:nil keyEquivalent:@""]; menu_item.hidden = YES; menu_item.tag = MENU_TAG_START; - menu_item = [apple_menu addItemWithTitle:@"_end_" action:nil keyEquivalent:@""]; + menu_item = [application_menu addItemWithTitle:@"_end_" action:nil keyEquivalent:@""]; menu_item.hidden = YES; menu_item.tag = MENU_TAG_END; NSMenu *services = [[NSMenu alloc] initWithTitle:@""]; - menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Services", nil) action:nil keyEquivalent:@""]; - [apple_menu setSubmenu:services forItem:menu_item]; + menu_item = [application_menu addItemWithTitle:NSLocalizedString(@"Services", nil) action:nil keyEquivalent:@""]; + [application_menu setSubmenu:services forItem:menu_item]; [NSApp setServicesMenu:services]; - [apple_menu addItem:[NSMenuItem separatorItem]]; + [application_menu addItem:[NSMenuItem separatorItem]]; title = [NSString stringWithFormat:NSLocalizedString(@"Hide %@", nil), nsappname]; - [apple_menu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; + [application_menu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; - menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Hide Others", nil) action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; + menu_item = [application_menu addItemWithTitle:NSLocalizedString(@"Hide Others", nil) action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; [menu_item setKeyEquivalentModifierMask:(NSEventModifierFlagOption | NSEventModifierFlagCommand)]; - [apple_menu addItemWithTitle:NSLocalizedString(@"Show all", nil) action:@selector(unhideAllApplications:) keyEquivalent:@""]; + [application_menu addItemWithTitle:NSLocalizedString(@"Show all", nil) action:@selector(unhideAllApplications:) keyEquivalent:@""]; - [apple_menu addItem:[NSMenuItem separatorItem]]; + [application_menu addItem:[NSMenuItem separatorItem]]; title = [NSString stringWithFormat:NSLocalizedString(@"Quit %@", nil), nsappname]; - [apple_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; + [application_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; - window_menu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Window", nil)]; + NSMenu *window_menu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Window", nil)]; [window_menu addItemWithTitle:NSLocalizedString(@"Minimize", nil) action:@selector(performMiniaturize:) keyEquivalent:@"m"]; [window_menu addItemWithTitle:NSLocalizedString(@"Zoom", nil) action:@selector(performZoom:) keyEquivalent:@""]; [window_menu addItem:[NSMenuItem separatorItem]]; @@ -4754,7 +3535,7 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM menu_item.hidden = YES; menu_item.tag = MENU_TAG_END; - help_menu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Help", nil)]; + NSMenu *help_menu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Help", nil)]; menu_item = [help_menu addItemWithTitle:@"_start_" action:nil keyEquivalent:@""]; menu_item.hidden = YES; menu_item.tag = MENU_TAG_START; @@ -4768,7 +3549,7 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM // Add items to the menu bar. NSMenu *main_menu = [NSApp mainMenu]; menu_item = [main_menu addItemWithTitle:@"" action:nil keyEquivalent:@""]; - [main_menu setSubmenu:apple_menu forItem:menu_item]; + [main_menu setSubmenu:application_menu forItem:menu_item]; menu_item = [main_menu addItemWithTitle:NSLocalizedString(@"Window", nil) action:nil keyEquivalent:@""]; [main_menu setSubmenu:window_menu forItem:menu_item]; @@ -4778,6 +3559,8 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM [main_menu setAutoenablesItems:NO]; + native_menu->_register_system_menus(main_menu, application_menu, window_menu, help_menu, dock_menu); + //!!!!!!!!!!!!!!!!!!!!!!!!!! //TODO - do Vulkan and OpenGL support checks, driver selection and fallback rendering_driver = p_rendering_driver; @@ -4879,6 +3662,12 @@ DisplayServerMacOS::~DisplayServerMacOS() { [[NSStatusBar systemStatusBar] removeStatusItem:E->value.item]; } + // Destroy native menu. + if (native_menu) { + memdelete(native_menu); + native_menu = nullptr; + } + // Destroy all windows. for (HashMap::Iterator E = windows.begin(); E;) { HashMap::Iterator F = E; diff --git a/platform/macos/godot_application_delegate.mm b/platform/macos/godot_application_delegate.mm index b3759e66f54..02466bab974 100644 --- a/platform/macos/godot_application_delegate.mm +++ b/platform/macos/godot_application_delegate.mm @@ -31,6 +31,7 @@ #include "godot_application_delegate.h" #include "display_server_macos.h" +#include "native_menu_macos.h" #include "os_macos.h" @implementation GodotApplicationDelegate @@ -211,9 +212,9 @@ } - (NSMenu *)applicationDockMenu:(NSApplication *)sender { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (ds) { - return ds->get_dock_menu(); + if (NativeMenu::get_singleton()) { + NativeMenuMacOS *nmenu = (NativeMenuMacOS *)NativeMenu::get_singleton(); + return nmenu->_get_dock_menu(); } else { return nullptr; } diff --git a/platform/macos/godot_menu_delegate.mm b/platform/macos/godot_menu_delegate.mm index dd57d9f2517..5c1e849715d 100644 --- a/platform/macos/godot_menu_delegate.mm +++ b/platform/macos/godot_menu_delegate.mm @@ -33,6 +33,7 @@ #include "display_server_macos.h" #include "godot_menu_item.h" #include "key_mapping_macos.h" +#include "native_menu_macos.h" @implementation GodotMenuDelegate @@ -40,16 +41,16 @@ } - (void)menuNeedsUpdate:(NSMenu *)menu { - if (DisplayServer::get_singleton()) { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - ds->menu_open(menu); + if (NativeMenu::get_singleton()) { + NativeMenuMacOS *nmenu = (NativeMenuMacOS *)NativeMenu::get_singleton(); + nmenu->_menu_open(menu); } } - (void)menuDidClose:(NSMenu *)menu { - if (DisplayServer::get_singleton()) { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - ds->menu_close(menu); + if (NativeMenu::get_singleton()) { + NativeMenuMacOS *nmenu = (NativeMenuMacOS *)NativeMenu::get_singleton(); + nmenu->_menu_close(menu); } } diff --git a/platform/macos/godot_menu_item.h b/platform/macos/godot_menu_item.h index b816ea1b3a0..b6e2d41c08d 100644 --- a/platform/macos/godot_menu_item.h +++ b/platform/macos/godot_menu_item.h @@ -36,6 +36,9 @@ #import #import +#define MENU_TAG_START 0x00004447 +#define MENU_TAG_END 0xFFFF4447 + enum GlobalMenuCheckType { CHECKABLE_TYPE_NONE, CHECKABLE_TYPE_CHECK_BOX, diff --git a/platform/macos/native_menu_macos.h b/platform/macos/native_menu_macos.h new file mode 100644 index 00000000000..e0e15df8327 --- /dev/null +++ b/platform/macos/native_menu_macos.h @@ -0,0 +1,156 @@ +/**************************************************************************/ +/* native_menu_macos.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef NATIVE_MENU_MACOS_H +#define NATIVE_MENU_MACOS_H + +#include "core/templates/hash_map.h" +#include "core/templates/rid_owner.h" +#include "servers/native_menu.h" + +#import +#import + +class NativeMenuMacOS : public NativeMenu { + GDCLASS(NativeMenuMacOS, NativeMenu) + + struct MenuData { + NSMenu *menu = nullptr; + + Callable open_cb; + Callable close_cb; + bool is_open = false; + bool is_system = false; + }; + + mutable RID_PtrOwner menus; + HashMap menu_lookup; + + NSMenu *main_menu_ns = nullptr; + NSMenu *application_menu_ns = nullptr; + NSMenu *window_menu_ns = nullptr; + NSMenu *help_menu_ns = nullptr; + NSMenu *dock_menu_ns = nullptr; + + RID main_menu; + RID application_menu; + RID window_menu; + RID help_menu; + RID dock_menu; + + int _get_system_menu_start(const NSMenu *p_menu) const; + int _get_system_menu_count(const NSMenu *p_menu) const; + bool _is_menu_opened(NSMenu *p_menu) const; + NSMenuItem *_menu_add_item(NSMenu *p_menu, const String &p_label, Key p_accel, int p_index, int *r_out); + +public: + void _register_system_menus(NSMenu *p_main_menu, NSMenu *p_application_menu, NSMenu *p_window_menu, NSMenu *p_help_menu, NSMenu *p_dock_menu); + NSMenu *_get_dock_menu(); + void _menu_open(NSMenu *p_menu); + void _menu_close(NSMenu *p_menu); + + virtual bool has_feature(Feature p_feature) const override; + + virtual bool has_system_menu(SystemMenus p_menu_id) const override; + virtual RID get_system_menu(SystemMenus p_menu_id) const override; + + virtual RID create_menu() override; + virtual bool has_menu(const RID &p_rid) const override; + virtual void free_menu(const RID &p_rid) override; + + virtual Size2 get_size(const RID &p_rid) const override; + virtual void popup(const RID &p_rid, const Vector2i &p_position) override; + + virtual void set_popup_open_callback(const RID &p_rid, const Callable &p_callback) override; + virtual Callable get_popup_open_callback(const RID &p_rid) const override; + virtual void set_popup_close_callback(const RID &p_rid, const Callable &p_callback) override; + virtual Callable get_popup_close_callback(const RID &p_rid) const override; + virtual void set_minimum_width(const RID &p_rid, float p_width) override; + virtual float get_minimum_width(const RID &p_rid) const override; + + virtual int add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag = Variant(), int p_index = -1) override; + virtual int add_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual int add_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual int add_icon_item(const RID &p_rid, const Ref &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual int add_icon_check_item(const RID &p_rid, const Ref &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual int add_radio_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual int add_icon_radio_check_item(const RID &p_rid, const Ref &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual int add_multistate_item(const RID &p_rid, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; + virtual int add_separator(const RID &p_rid, int p_index = -1) override; + + virtual int find_item_index_with_text(const RID &p_rid, const String &p_text) const override; + virtual int find_item_index_with_tag(const RID &p_rid, const Variant &p_tag) const override; + + virtual bool is_item_checked(const RID &p_rid, int p_idx) const override; + virtual bool is_item_checkable(const RID &p_rid, int p_idx) const override; + virtual bool is_item_radio_checkable(const RID &p_rid, int p_idx) const override; + virtual Callable get_item_callback(const RID &p_rid, int p_idx) const override; + virtual Callable get_item_key_callback(const RID &p_rid, int p_idx) const override; + virtual Variant get_item_tag(const RID &p_rid, int p_idx) const override; + virtual String get_item_text(const RID &p_rid, int p_idx) const override; + virtual RID get_item_submenu(const RID &p_rid, int p_idx) const override; + virtual Key get_item_accelerator(const RID &p_rid, int p_idx) const override; + virtual bool is_item_disabled(const RID &p_rid, int p_idx) const override; + virtual bool is_item_hidden(const RID &p_rid, int p_idx) const override; + virtual String get_item_tooltip(const RID &p_rid, int p_idx) const override; + virtual int get_item_state(const RID &p_rid, int p_idx) const override; + virtual int get_item_max_states(const RID &p_rid, int p_idx) const override; + virtual Ref get_item_icon(const RID &p_rid, int p_idx) const override; + virtual int get_item_indentation_level(const RID &p_rid, int p_idx) const override; + + virtual void set_item_checked(const RID &p_rid, int p_idx, bool p_checked) override; + virtual void set_item_checkable(const RID &p_rid, int p_idx, bool p_checkable) override; + virtual void set_item_radio_checkable(const RID &p_rid, int p_idx, bool p_checkable) override; + virtual void set_item_callback(const RID &p_rid, int p_idx, const Callable &p_callback) override; + virtual void set_item_key_callback(const RID &p_rid, int p_idx, const Callable &p_key_callback) override; + virtual void set_item_hover_callbacks(const RID &p_rid, int p_idx, const Callable &p_callback) override; + virtual void set_item_tag(const RID &p_rid, int p_idx, const Variant &p_tag) override; + virtual void set_item_text(const RID &p_rid, int p_idx, const String &p_text) override; + virtual void set_item_submenu(const RID &p_rid, int p_idx, const RID &p_submenu_rid) override; + virtual void set_item_accelerator(const RID &p_rid, int p_idx, Key p_keycode) override; + virtual void set_item_disabled(const RID &p_rid, int p_idx, bool p_disabled) override; + virtual void set_item_hidden(const RID &p_rid, int p_idx, bool p_hidden) override; + virtual void set_item_tooltip(const RID &p_rid, int p_idx, const String &p_tooltip) override; + virtual void set_item_state(const RID &p_rid, int p_idx, int p_state) override; + virtual void set_item_max_states(const RID &p_rid, int p_idx, int p_max_states) override; + virtual void set_item_icon(const RID &p_rid, int p_idx, const Ref &p_icon) override; + virtual void set_item_indentation_level(const RID &p_rid, int p_idx, int p_level) override; + + virtual int get_item_count(const RID &p_rid) const override; + virtual bool is_system_menu(const RID &p_rid) const override; + + virtual void remove_item(const RID &p_rid, int p_idx) override; + virtual void clear(const RID &p_rid) override; + + NativeMenuMacOS(); + ~NativeMenuMacOS(); +}; + +#endif // NATIVE_MENU_MACOS_H diff --git a/platform/macos/native_menu_macos.mm b/platform/macos/native_menu_macos.mm new file mode 100644 index 00000000000..cb88f94a28d --- /dev/null +++ b/platform/macos/native_menu_macos.mm @@ -0,0 +1,1348 @@ +/**************************************************************************/ +/* native_menu_macos.mm */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "native_menu_macos.h" + +#include "display_server_macos.h" +#include "godot_menu_item.h" +#include "key_mapping_macos.h" + +#include "scene/resources/image_texture.h" + +void NativeMenuMacOS::_register_system_menus(NSMenu *p_main_menu, NSMenu *p_application_menu, NSMenu *p_window_menu, NSMenu *p_help_menu, NSMenu *p_dock_menu) { + { + MenuData *md = memnew(MenuData); + md->menu = p_main_menu; + md->is_system = true; + main_menu = menus.make_rid(md); + main_menu_ns = p_main_menu; + menu_lookup[md->menu] = main_menu; + } + { + MenuData *md = memnew(MenuData); + md->menu = p_application_menu; + md->is_system = true; + application_menu = menus.make_rid(md); + application_menu_ns = p_application_menu; + menu_lookup[md->menu] = application_menu; + } + { + MenuData *md = memnew(MenuData); + md->menu = p_window_menu; + md->is_system = true; + window_menu = menus.make_rid(md); + window_menu_ns = p_window_menu; + menu_lookup[md->menu] = window_menu; + } + { + MenuData *md = memnew(MenuData); + md->menu = p_help_menu; + md->is_system = true; + help_menu = menus.make_rid(md); + help_menu_ns = p_help_menu; + menu_lookup[md->menu] = help_menu; + } + { + MenuData *md = memnew(MenuData); + md->menu = p_dock_menu; + md->is_system = true; + dock_menu = menus.make_rid(md); + dock_menu_ns = p_dock_menu; + menu_lookup[md->menu] = dock_menu; + } +} + +NSMenu *NativeMenuMacOS::_get_dock_menu() { + MenuData *md = menus.get_or_null(dock_menu); + if (md) { + return md->menu; + } + return nullptr; +} + +void NativeMenuMacOS::_menu_open(NSMenu *p_menu) { + if (menu_lookup.has(p_menu)) { + MenuData *md = menus.get_or_null(menu_lookup[p_menu]); + if (md) { + md->is_open = true; + if (md->open_cb.is_valid()) { + Variant ret; + Callable::CallError ce; + + md->open_cb.callp(nullptr, 0, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT(vformat("Failed to execute menu open callback: %s.", Variant::get_callable_error_text(md->open_cb, nullptr, 0, ce))); + } + } + } + } +} + +void NativeMenuMacOS::_menu_close(NSMenu *p_menu) { + if (menu_lookup.has(p_menu)) { + MenuData *md = menus.get_or_null(menu_lookup[p_menu]); + if (md) { + md->is_open = false; + if (md->close_cb.is_valid()) { + Variant ret; + Callable::CallError ce; + + md->close_cb.callp(nullptr, 0, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT(vformat("Failed to execute menu close callback: %s.", Variant::get_callable_error_text(md->close_cb, nullptr, 0, ce))); + } + } + } + } +} + +bool NativeMenuMacOS::_is_menu_opened(NSMenu *p_menu) const { + if (menu_lookup.has(p_menu)) { + const MenuData *md = menus.get_or_null(menu_lookup[p_menu]); + if (md && md->is_open) { + return true; + } + } + for (NSInteger i = (p_menu == [NSApp mainMenu]) ? 1 : 0; i < [p_menu numberOfItems]; i++) { + const NSMenuItem *menu_item = [p_menu itemAtIndex:i]; + if ([menu_item submenu]) { + if (_is_menu_opened([menu_item submenu])) { + return true; + } + } + } + return false; +} + +int NativeMenuMacOS::_get_system_menu_start(const NSMenu *p_menu) const { + if (p_menu == [NSApp mainMenu]) { // Skip Apple menu. + return 1; + } + if (p_menu == application_menu_ns || p_menu == window_menu_ns || p_menu == help_menu_ns) { + int count = [p_menu numberOfItems]; + for (int i = 0; i < count; i++) { + NSMenuItem *menu_item = [p_menu itemAtIndex:i]; + if (menu_item.tag == MENU_TAG_START) { + return i + 1; + } + } + } + return 0; +} + +int NativeMenuMacOS::_get_system_menu_count(const NSMenu *p_menu) const { + if (p_menu == [NSApp mainMenu]) { // Skip Apple, Window and Help menu. + return [p_menu numberOfItems] - 3; + } + if (p_menu == application_menu_ns || p_menu == window_menu_ns || p_menu == help_menu_ns) { + int start = 0; + int count = [p_menu numberOfItems]; + for (int i = 0; i < count; i++) { + NSMenuItem *menu_item = [p_menu itemAtIndex:i]; + if (menu_item.tag == MENU_TAG_START) { + start = i + 1; + } + if (menu_item.tag == MENU_TAG_END) { + return i - start; + } + } + } + return [p_menu numberOfItems]; +} + +bool NativeMenuMacOS::has_feature(Feature p_feature) const { + switch (p_feature) { + case FEATURE_GLOBAL_MENU: + case FEATURE_POPUP_MENU: + return true; + default: + return false; + } +} + +bool NativeMenuMacOS::has_system_menu(SystemMenus p_menu_id) const { + switch (p_menu_id) { + case MAIN_MENU_ID: + case APPLICATION_MENU_ID: + case WINDOW_MENU_ID: + case HELP_MENU_ID: + case DOCK_MENU_ID: + return true; + default: + return false; + } +} + +RID NativeMenuMacOS::get_system_menu(SystemMenus p_menu_id) const { + switch (p_menu_id) { + case MAIN_MENU_ID: + return main_menu; + case APPLICATION_MENU_ID: + return application_menu; + case WINDOW_MENU_ID: + return window_menu; + case HELP_MENU_ID: + return help_menu; + case DOCK_MENU_ID: + return dock_menu; + default: + return RID(); + } +} + +RID NativeMenuMacOS::create_menu() { + MenuData *md = memnew(MenuData); + md->menu = [[NSMenu alloc] initWithTitle:@""]; + RID rid = menus.make_rid(md); + menu_lookup[md->menu] = rid; + return rid; +} + +bool NativeMenuMacOS::has_menu(const RID &p_rid) const { + return menus.owns(p_rid); +} + +void NativeMenuMacOS::free_menu(const RID &p_rid) { + MenuData *md = menus.get_or_null(p_rid); + if (md && !md->is_system) { + clear(p_rid); + menus.free(p_rid); + menu_lookup.erase(md->menu); + md->menu = nullptr; + memdelete(md); + } +} + +Size2 NativeMenuMacOS::get_size(const RID &p_rid) const { + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, Size2()); + + return Size2(md->menu.size.width, md->menu.size.height) * DisplayServer::get_singleton()->screen_get_max_scale(); +} + +void NativeMenuMacOS::popup(const RID &p_rid, const Vector2i &p_position) { + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + + DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); + if (ds) { + Point2i position = p_position; + // macOS native y-coordinate relative to _get_screens_origin() is negative, + // Godot passes a positive value. + position.y *= -1; + position += ds->_get_screens_origin(); + position /= ds->screen_get_max_scale(); + + [md->menu popUpMenuPositioningItem:nil atLocation:NSMakePoint(position.x, position.y) inView:nil]; + } +} + +void NativeMenuMacOS::set_popup_open_callback(const RID &p_rid, const Callable &p_callback) { + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + + md->open_cb = p_callback; +} + +Callable NativeMenuMacOS::get_popup_open_callback(const RID &p_rid) const { + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, Callable()); + + return md->open_cb; +} + +void NativeMenuMacOS::set_popup_close_callback(const RID &p_rid, const Callable &p_callback) { + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + + md->close_cb = p_callback; +} + +Callable NativeMenuMacOS::get_popup_close_callback(const RID &p_rid) const { + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, Callable()); + + return md->close_cb; +} + +void NativeMenuMacOS::set_minimum_width(const RID &p_rid, float p_width) { + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + + md->menu.minimumWidth = p_width / DisplayServer::get_singleton()->screen_get_max_scale(); +} + +float NativeMenuMacOS::get_minimum_width(const RID &p_rid) const { + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, 0.0); + + return md->menu.minimumWidth * DisplayServer::get_singleton()->screen_get_max_scale(); +} + +int NativeMenuMacOS::add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag, int p_index) { + MenuData *md = menus.get_or_null(p_rid); + MenuData *md_sub = menus.get_or_null(p_submenu_rid); + ERR_FAIL_NULL_V(md, -1); + ERR_FAIL_NULL_V(md_sub, -1); + ERR_FAIL_COND_V_MSG(md->menu == md_sub->menu, -1, "Can't set submenu to self!"); + ERR_FAIL_COND_V_MSG([md_sub->menu supermenu], -1, "Can't set submenu to menu that is already a submenu of some other menu!"); + + NSMenuItem *menu_item; + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + if (p_index < 0) { + p_index = item_start + item_count; + } else { + p_index += item_start; + p_index = CLAMP(p_index, item_start, item_start + item_count); + } + menu_item = [md->menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@"" atIndex:p_index]; + + GodotMenuItem *obj = [[GodotMenuItem alloc] init]; + obj->callback = Callable(); + obj->key_callback = Callable(); + obj->meta = p_tag; + obj->checkable_type = CHECKABLE_TYPE_NONE; + obj->max_states = 0; + obj->state = 0; + [menu_item setRepresentedObject:obj]; + + [md_sub->menu setTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()]]; + [md->menu setSubmenu:md_sub->menu forItem:menu_item]; + + return p_index - item_start; +} + +NSMenuItem *NativeMenuMacOS::_menu_add_item(NSMenu *p_menu, const String &p_label, Key p_accel, int p_index, int *r_out) { + if (p_menu) { + String keycode = KeyMappingMacOS::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); + NSMenuItem *menu_item; + int item_start = _get_system_menu_start(p_menu); + int item_count = _get_system_menu_count(p_menu); + if (p_index < 0) { + p_index = item_start + item_count; + } else { + p_index += item_start; + p_index = CLAMP(p_index, item_start, item_start + item_count); + } + menu_item = [p_menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; + *r_out = p_index - item_start; + return menu_item; + } + return nullptr; +} + +int NativeMenuMacOS::add_item(const RID &p_rid, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, -1); + + int out = -1; + NSMenuItem *menu_item = _menu_add_item(md->menu, p_label, p_accel, p_index, &out); + if (menu_item) { + GodotMenuItem *obj = [[GodotMenuItem alloc] init]; + obj->callback = p_callback; + obj->key_callback = p_key_callback; + obj->meta = p_tag; + obj->checkable_type = CHECKABLE_TYPE_NONE; + obj->max_states = 0; + obj->state = 0; + [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)]; + [menu_item setRepresentedObject:obj]; + } + return out; +} + +int NativeMenuMacOS::add_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, -1); + + int out = -1; + NSMenuItem *menu_item = _menu_add_item(md->menu, p_label, p_accel, p_index, &out); + if (menu_item) { + GodotMenuItem *obj = [[GodotMenuItem alloc] init]; + obj->callback = p_callback; + obj->key_callback = p_key_callback; + obj->meta = p_tag; + obj->checkable_type = CHECKABLE_TYPE_CHECK_BOX; + obj->max_states = 0; + obj->state = 0; + [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)]; + [menu_item setRepresentedObject:obj]; + } + return out; +} + +int NativeMenuMacOS::add_icon_item(const RID &p_rid, const Ref &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, -1); + + int out = -1; + NSMenuItem *menu_item = _menu_add_item(md->menu, p_label, p_accel, p_index, &out); + if (menu_item) { + GodotMenuItem *obj = [[GodotMenuItem alloc] init]; + obj->callback = p_callback; + obj->key_callback = p_key_callback; + obj->meta = p_tag; + obj->checkable_type = CHECKABLE_TYPE_NONE; + obj->max_states = 0; + obj->state = 0; + DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); + if (ds && p_icon.is_valid()) { + obj->img = p_icon->get_image(); + obj->img = obj->img->duplicate(); + if (obj->img->is_compressed()) { + obj->img->decompress(); + } + NSImage *image = ds->_convert_to_nsimg(obj->img); + [image setSize:NSMakeSize(16, 16)]; + [menu_item setImage:image]; + } + [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)]; + [menu_item setRepresentedObject:obj]; + } + return out; +} + +int NativeMenuMacOS::add_icon_check_item(const RID &p_rid, const Ref &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, -1); + + int out = -1; + NSMenuItem *menu_item = _menu_add_item(md->menu, p_label, p_accel, p_index, &out); + if (menu_item) { + GodotMenuItem *obj = [[GodotMenuItem alloc] init]; + obj->callback = p_callback; + obj->key_callback = p_key_callback; + obj->meta = p_tag; + obj->checkable_type = CHECKABLE_TYPE_CHECK_BOX; + obj->max_states = 0; + obj->state = 0; + DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); + if (ds && p_icon.is_valid()) { + obj->img = p_icon->get_image(); + obj->img = obj->img->duplicate(); + if (obj->img->is_compressed()) { + obj->img->decompress(); + } + NSImage *image = ds->_convert_to_nsimg(obj->img); + [image setSize:NSMakeSize(16, 16)]; + [menu_item setImage:image]; + } + [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)]; + [menu_item setRepresentedObject:obj]; + } + return out; +} + +int NativeMenuMacOS::add_radio_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, -1); + + int out = -1; + NSMenuItem *menu_item = _menu_add_item(md->menu, p_label, p_accel, p_index, &out); + if (menu_item) { + GodotMenuItem *obj = [[GodotMenuItem alloc] init]; + obj->callback = p_callback; + obj->key_callback = p_key_callback; + obj->meta = p_tag; + obj->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON; + obj->max_states = 0; + obj->state = 0; + [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)]; + [menu_item setRepresentedObject:obj]; + } + return out; +} + +int NativeMenuMacOS::add_icon_radio_check_item(const RID &p_rid, const Ref &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, -1); + + int out = -1; + NSMenuItem *menu_item = _menu_add_item(md->menu, p_label, p_accel, p_index, &out); + if (menu_item) { + GodotMenuItem *obj = [[GodotMenuItem alloc] init]; + obj->callback = p_callback; + obj->key_callback = p_key_callback; + obj->meta = p_tag; + obj->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON; + obj->max_states = 0; + obj->state = 0; + DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); + if (ds && p_icon.is_valid()) { + obj->img = p_icon->get_image(); + obj->img = obj->img->duplicate(); + if (obj->img->is_compressed()) { + obj->img->decompress(); + } + NSImage *image = ds->_convert_to_nsimg(obj->img); + [image setSize:NSMakeSize(16, 16)]; + [menu_item setImage:image]; + } + [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)]; + [menu_item setRepresentedObject:obj]; + } + return out; +} + +int NativeMenuMacOS::add_multistate_item(const RID &p_rid, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, -1); + + int out = -1; + NSMenuItem *menu_item = _menu_add_item(md->menu, p_label, p_accel, p_index, &out); + if (menu_item) { + GodotMenuItem *obj = [[GodotMenuItem alloc] init]; + obj->callback = p_callback; + obj->key_callback = p_key_callback; + obj->meta = p_tag; + obj->checkable_type = CHECKABLE_TYPE_NONE; + obj->max_states = p_max_states; + obj->state = p_default_state; + [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_accel)]; + [menu_item setRepresentedObject:obj]; + } + return out; +} + +int NativeMenuMacOS::add_separator(const RID &p_rid, int p_index) { + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, -1); + + if (md->menu == [NSApp mainMenu]) { // Do not add separators into main menu. + return -1; + } + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + if (p_index < 0) { + p_index = item_start + item_count; + } else { + p_index += item_start; + p_index = CLAMP(p_index, item_start, item_start + item_count); + } + [md->menu insertItem:[NSMenuItem separatorItem] atIndex:p_index]; + return p_index - item_start; +} + +int NativeMenuMacOS::find_item_index_with_text(const RID &p_rid, const String &p_text) const { + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, -1); + + int item_start = _get_system_menu_start(md->menu); + int index = [md->menu indexOfItemWithTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]]; + if (index >= 0) { + return index - item_start; + } + return -1; +} + +int NativeMenuMacOS::find_item_index_with_tag(const RID &p_rid, const Variant &p_tag) const { + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, -1); + + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + for (NSInteger i = item_start; i < item_start + item_count; i++) { + const NSMenuItem *menu_item = [md->menu itemAtIndex:i]; + if (menu_item) { + const GodotMenuItem *obj = [menu_item representedObject]; + if (obj && obj->meta == p_tag) { + return i - item_start; + } + } + } + return -1; +} + +bool NativeMenuMacOS::is_item_checked(const RID &p_rid, int p_idx) const { + ERR_FAIL_COND_V(p_idx < 0, false); + + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, false); + + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); + const NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + return ([menu_item state] == NSControlStateValueOn); + } + return false; +} + +bool NativeMenuMacOS::is_item_checkable(const RID &p_rid, int p_idx) const { + ERR_FAIL_COND_V(p_idx < 0, false); + + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, false); + + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); + const NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + if (obj) { + return obj->checkable_type == CHECKABLE_TYPE_CHECK_BOX; + } + } + return false; +} + +bool NativeMenuMacOS::is_item_radio_checkable(const RID &p_rid, int p_idx) const { + ERR_FAIL_COND_V(p_idx < 0, false); + + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, false); + + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); + const NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + if (obj) { + return obj->checkable_type == CHECKABLE_TYPE_RADIO_BUTTON; + } + } + return false; +} + +Callable NativeMenuMacOS::get_item_callback(const RID &p_rid, int p_idx) const { + ERR_FAIL_COND_V(p_idx < 0, Callable()); + + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, Callable()); + + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, Callable()); + const NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + if (obj) { + return obj->callback; + } + } + return Callable(); +} + +Callable NativeMenuMacOS::get_item_key_callback(const RID &p_rid, int p_idx) const { + ERR_FAIL_COND_V(p_idx < 0, Callable()); + + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, Callable()); + + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, Callable()); + const NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + if (obj) { + return obj->key_callback; + } + } + return Callable(); +} + +Variant NativeMenuMacOS::get_item_tag(const RID &p_rid, int p_idx) const { + ERR_FAIL_COND_V(p_idx < 0, Variant()); + + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, Variant()); + + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, Variant()); + const NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + if (obj) { + return obj->meta; + } + } + return Variant(); +} + +String NativeMenuMacOS::get_item_text(const RID &p_rid, int p_idx) const { + ERR_FAIL_COND_V(p_idx < 0, String()); + + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, String()); + + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, String()); + const NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + return String::utf8([[menu_item title] UTF8String]); + } + return String(); +} + +RID NativeMenuMacOS::get_item_submenu(const RID &p_rid, int p_idx) const { + ERR_FAIL_COND_V(p_idx < 0, RID()); + + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, RID()); + + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, RID()); + const NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + NSMenu *sub_menu = [menu_item submenu]; + if (sub_menu && menu_lookup.has(sub_menu)) { + return menu_lookup[sub_menu]; + } + } + return RID(); +} + +Key NativeMenuMacOS::get_item_accelerator(const RID &p_rid, int p_idx) const { + ERR_FAIL_COND_V(p_idx < 0, Key::NONE); + + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, Key::NONE); + + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, Key::NONE); + const NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + String ret = String::utf8([[menu_item keyEquivalent] UTF8String]); + Key keycode = find_keycode(ret); + NSUInteger mask = [menu_item keyEquivalentModifierMask]; + if (mask & NSEventModifierFlagControl) { + keycode |= KeyModifierMask::CTRL; + } + if (mask & NSEventModifierFlagOption) { + keycode |= KeyModifierMask::ALT; + } + if (mask & NSEventModifierFlagShift) { + keycode |= KeyModifierMask::SHIFT; + } + if (mask & NSEventModifierFlagCommand) { + keycode |= KeyModifierMask::META; + } + if (mask & NSEventModifierFlagNumericPad) { + keycode |= KeyModifierMask::KPAD; + } + return keycode; + } + return Key::NONE; +} + +bool NativeMenuMacOS::is_item_disabled(const RID &p_rid, int p_idx) const { + ERR_FAIL_COND_V(p_idx < 0, false); + + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, false); + + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); + const NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + return ![menu_item isEnabled]; + } + return false; +} + +bool NativeMenuMacOS::is_item_hidden(const RID &p_rid, int p_idx) const { + ERR_FAIL_COND_V(p_idx < 0, false); + + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, false); + + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); + const NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + return [menu_item isHidden]; + } + return false; +} + +String NativeMenuMacOS::get_item_tooltip(const RID &p_rid, int p_idx) const { + ERR_FAIL_COND_V(p_idx < 0, String()); + + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, String()); + + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, String()); + const NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + return String::utf8([[menu_item toolTip] UTF8String]); + } + return String(); +} + +int NativeMenuMacOS::get_item_state(const RID &p_rid, int p_idx) const { + ERR_FAIL_COND_V(p_idx < 0, 0); + + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, 0); + + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, 0); + const NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + if (obj) { + return obj->state; + } + } + return 0; +} + +int NativeMenuMacOS::get_item_max_states(const RID &p_rid, int p_idx) const { + ERR_FAIL_COND_V(p_idx < 0, 0); + + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, 0); + + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, 0); + const NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + if (obj) { + return obj->max_states; + } + } + return 0; +} + +Ref NativeMenuMacOS::get_item_icon(const RID &p_rid, int p_idx) const { + ERR_FAIL_COND_V(p_idx < 0, Ref()); + + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, Ref()); + + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, Ref()); + const NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + if (obj) { + if (obj->img.is_valid()) { + return ImageTexture::create_from_image(obj->img); + } + } + } + return Ref(); +} + +int NativeMenuMacOS::get_item_indentation_level(const RID &p_rid, int p_idx) const { + ERR_FAIL_COND_V(p_idx < 0, 0); + + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, 0); + + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, 0); + const NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + return [menu_item indentationLevel]; + } + return 0; +} + +void NativeMenuMacOS::set_item_checked(const RID &p_rid, int p_idx, bool p_checked) { + ERR_FAIL_COND(p_idx < 0); + + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); + NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + if (p_checked) { + [menu_item setState:NSControlStateValueOn]; + } else { + [menu_item setState:NSControlStateValueOff]; + } + } +} + +void NativeMenuMacOS::set_item_checkable(const RID &p_rid, int p_idx, bool p_checkable) { + ERR_FAIL_COND(p_idx < 0); + + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); + NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + ERR_FAIL_NULL(obj); + obj->checkable_type = (p_checkable) ? CHECKABLE_TYPE_CHECK_BOX : CHECKABLE_TYPE_NONE; + } +} + +void NativeMenuMacOS::set_item_radio_checkable(const RID &p_rid, int p_idx, bool p_checkable) { + ERR_FAIL_COND(p_idx < 0); + + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); + NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + ERR_FAIL_NULL(obj); + obj->checkable_type = (p_checkable) ? CHECKABLE_TYPE_RADIO_BUTTON : CHECKABLE_TYPE_NONE; + } +} + +void NativeMenuMacOS::set_item_callback(const RID &p_rid, int p_idx, const Callable &p_callback) { + ERR_FAIL_COND(p_idx < 0); + + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); + NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + ERR_FAIL_NULL(obj); + obj->callback = p_callback; + } +} + +void NativeMenuMacOS::set_item_key_callback(const RID &p_rid, int p_idx, const Callable &p_key_callback) { + ERR_FAIL_COND(p_idx < 0); + + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); + NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + ERR_FAIL_NULL(obj); + obj->key_callback = p_key_callback; + } +} + +void NativeMenuMacOS::set_item_hover_callbacks(const RID &p_rid, int p_idx, const Callable &p_callback) { + ERR_FAIL_COND(p_idx < 0); + + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); + NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + ERR_FAIL_NULL(obj); + obj->hover_callback = p_callback; + } +} + +void NativeMenuMacOS::set_item_tag(const RID &p_rid, int p_idx, const Variant &p_tag) { + ERR_FAIL_COND(p_idx < 0); + + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); + NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + ERR_FAIL_NULL(obj); + obj->meta = p_tag; + } +} + +void NativeMenuMacOS::set_item_text(const RID &p_rid, int p_idx, const String &p_text) { + ERR_FAIL_COND(p_idx < 0); + + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); + NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + [menu_item setTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]]; + NSMenu *sub_menu = [menu_item submenu]; + if (sub_menu) { + [sub_menu setTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]]; + } + } +} + +void NativeMenuMacOS::set_item_submenu(const RID &p_rid, int p_idx, const RID &p_submenu_rid) { + ERR_FAIL_COND(p_idx < 0); + + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + + if (p_submenu_rid.is_valid()) { + MenuData *md_sub = menus.get_or_null(p_submenu_rid); + ERR_FAIL_NULL(md_sub); + ERR_FAIL_COND_MSG(md->menu == md_sub->menu, "Can't set submenu to self!"); + ERR_FAIL_COND_MSG([md_sub->menu supermenu], "Can't set submenu to menu that is already a submenu of some other menu!"); + + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); + NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + [md->menu setSubmenu:md_sub->menu forItem:menu_item]; + } + } else { + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); + NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + if ([menu_item submenu] && _is_menu_opened([menu_item submenu])) { + ERR_PRINT("Can't remove open menu!"); + return; + } + [md->menu setSubmenu:nil forItem:menu_item]; + } + } +} + +void NativeMenuMacOS::set_item_accelerator(const RID &p_rid, int p_idx, Key p_keycode) { + ERR_FAIL_COND(p_idx < 0); + + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); + NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + if (p_keycode == Key::NONE) { + [menu_item setKeyEquivalent:@""]; + } else { + [menu_item setKeyEquivalentModifierMask:KeyMappingMacOS::keycode_get_native_mask(p_keycode)]; + String keycode = KeyMappingMacOS::keycode_get_native_string(p_keycode & KeyModifierMask::CODE_MASK); + [menu_item setKeyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; + } + } +} + +void NativeMenuMacOS::set_item_disabled(const RID &p_rid, int p_idx, bool p_disabled) { + ERR_FAIL_COND(p_idx < 0); + + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); + NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + [menu_item setEnabled:(!p_disabled)]; + } +} + +void NativeMenuMacOS::set_item_hidden(const RID &p_rid, int p_idx, bool p_hidden) { + ERR_FAIL_COND(p_idx < 0); + + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); + NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + [menu_item setHidden:p_hidden]; + } +} + +void NativeMenuMacOS::set_item_tooltip(const RID &p_rid, int p_idx, const String &p_tooltip) { + ERR_FAIL_COND(p_idx < 0); + + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); + NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + [menu_item setToolTip:[NSString stringWithUTF8String:p_tooltip.utf8().get_data()]]; + } +} + +void NativeMenuMacOS::set_item_state(const RID &p_rid, int p_idx, int p_state) { + ERR_FAIL_COND(p_idx < 0); + + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); + NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + ERR_FAIL_NULL(obj); + obj->state = p_state; + } +} + +void NativeMenuMacOS::set_item_max_states(const RID &p_rid, int p_idx, int p_max_states) { + ERR_FAIL_COND(p_idx < 0); + + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); + NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + ERR_FAIL_NULL(obj); + obj->max_states = p_max_states; + } +} + +void NativeMenuMacOS::set_item_icon(const RID &p_rid, int p_idx, const Ref &p_icon) { + ERR_FAIL_COND(p_idx < 0); + + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); + NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + GodotMenuItem *obj = [menu_item representedObject]; + ERR_FAIL_NULL(obj); + DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); + if (ds && p_icon.is_valid()) { + obj->img = p_icon->get_image(); + obj->img = obj->img->duplicate(); + if (obj->img->is_compressed()) { + obj->img->decompress(); + } + NSImage *image = ds->_convert_to_nsimg(obj->img); + [image setSize:NSMakeSize(16, 16)]; + [menu_item setImage:image]; + } else { + obj->img = Ref(); + [menu_item setImage:nil]; + } + } +} + +void NativeMenuMacOS::set_item_indentation_level(const RID &p_rid, int p_idx, int p_level) { + ERR_FAIL_COND(p_idx < 0); + + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); + NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if (menu_item) { + [menu_item setIndentationLevel:p_level]; + } +} + +int NativeMenuMacOS::get_item_count(const RID &p_rid) const { + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, 0); + + return _get_system_menu_count(md->menu); +} + +bool NativeMenuMacOS::is_system_menu(const RID &p_rid) const { + const MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL_V(md, false); + + return md->is_system; +} + +void NativeMenuMacOS::remove_item(const RID &p_rid, int p_idx) { + ERR_FAIL_COND(p_idx < 0); + + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + int item_start = _get_system_menu_start(md->menu); + int item_count = _get_system_menu_count(md->menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); + NSMenuItem *menu_item = [md->menu itemAtIndex:p_idx]; + if ([menu_item submenu] && _is_menu_opened([menu_item submenu])) { + ERR_PRINT("Can't remove open menu!"); + return; + } + [md->menu removeItemAtIndex:p_idx]; +} + +void NativeMenuMacOS::clear(const RID &p_rid) { + MenuData *md = menus.get_or_null(p_rid); + ERR_FAIL_NULL(md); + ERR_FAIL_COND_MSG(_is_menu_opened(md->menu), "Can't remove open menu!"); + + if (p_rid == application_menu || p_rid == window_menu || p_rid == help_menu) { + int start = _get_system_menu_start(md->menu); + int count = _get_system_menu_count(md->menu); + for (int i = start + count - 1; i >= start; i--) { + [md->menu removeItemAtIndex:i]; + } + } else { + [md->menu removeAllItems]; + } + + if (p_rid == main_menu) { + // Restore Apple, Window and Help menu. + MenuData *md_app = menus.get_or_null(application_menu); + if (md_app) { + NSMenuItem *menu_item = [md->menu addItemWithTitle:@"" action:nil keyEquivalent:@""]; + [md->menu setSubmenu:md_app->menu forItem:menu_item]; + } + MenuData *md_win = menus.get_or_null(window_menu); + if (md_win) { + NSMenuItem *menu_item = [md->menu addItemWithTitle:@"Window" action:nil keyEquivalent:@""]; + [md->menu setSubmenu:md_win->menu forItem:menu_item]; + } + MenuData *md_hlp = menus.get_or_null(help_menu); + if (md_hlp) { + NSMenuItem *menu_item = [md->menu addItemWithTitle:@"Help" action:nil keyEquivalent:@""]; + [md->menu setSubmenu:md_hlp->menu forItem:menu_item]; + } + } +} + +NativeMenuMacOS::NativeMenuMacOS() {} + +NativeMenuMacOS::~NativeMenuMacOS() { + if (main_menu.is_valid()) { + MenuData *md = menus.get_or_null(main_menu); + if (md) { + clear(main_menu); + menus.free(main_menu); + menu_lookup.erase(md->menu); + md->menu = nullptr; + main_menu_ns = nullptr; + memdelete(md); + } + } + if (application_menu.is_valid()) { + MenuData *md = menus.get_or_null(application_menu); + if (md) { + clear(application_menu); + menus.free(application_menu); + menu_lookup.erase(md->menu); + md->menu = nullptr; + application_menu_ns = nullptr; + memdelete(md); + } + } + if (window_menu.is_valid()) { + MenuData *md = menus.get_or_null(window_menu); + if (md) { + clear(window_menu); + menus.free(window_menu); + menu_lookup.erase(md->menu); + md->menu = nullptr; + window_menu_ns = nullptr; + memdelete(md); + } + } + if (help_menu.is_valid()) { + MenuData *md = menus.get_or_null(help_menu); + if (md) { + clear(help_menu); + menus.free(help_menu); + menu_lookup.erase(md->menu); + md->menu = nullptr; + help_menu_ns = nullptr; + memdelete(md); + } + } + if (dock_menu.is_valid()) { + MenuData *md = menus.get_or_null(dock_menu); + if (md) { + clear(dock_menu); + menus.free(dock_menu); + menu_lookup.erase(md->menu); + md->menu = nullptr; + dock_menu_ns = nullptr; + memdelete(md); + } + } +} diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp index 2e3afc49ca7..281f312000d 100644 --- a/platform/web/display_server_web.cpp +++ b/platform/web/display_server_web.cpp @@ -1030,6 +1030,7 @@ DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode r_error = OK; // Always succeeds for now. tts = GLOBAL_GET("audio/general/text_to_speech"); + native_menu = memnew(NativeMenu); // Dummy native menu. // Ensure the canvas ID. godot_js_config_canvas_id_get(canvas_id, 256); @@ -1098,6 +1099,10 @@ DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode } DisplayServerWeb::~DisplayServerWeb() { + if (native_menu) { + memdelete(native_menu); + native_menu = nullptr; + } #ifdef GLES3_ENABLED if (webgl_ctx) { emscripten_webgl_commit_frame(); @@ -1108,7 +1113,11 @@ DisplayServerWeb::~DisplayServerWeb() { bool DisplayServerWeb::has_feature(Feature p_feature) const { switch (p_feature) { - //case FEATURE_GLOBAL_MENU: +#ifndef DISABLE_DEPRECATED + case FEATURE_GLOBAL_MENU: { + return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU)); + } break; +#endif //case FEATURE_HIDPI: case FEATURE_ICON: case FEATURE_CLIPBOARD: diff --git a/platform/web/display_server_web.h b/platform/web/display_server_web.h index 682d10704f5..49a017015a6 100644 --- a/platform/web/display_server_web.h +++ b/platform/web/display_server_web.h @@ -104,6 +104,7 @@ private: bool swap_cancel_ok = false; bool tts = false; + NativeMenu *native_menu = nullptr; // utilities static void dom2godot_mod(Ref ev, int p_mod, Key p_keycode); diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index ced8dce65a7..7e8d6adcc20 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -96,6 +96,11 @@ static void track_mouse_leave_event(HWND hWnd) { bool DisplayServerWindows::has_feature(Feature p_feature) const { switch (p_feature) { +#ifndef DISABLE_DEPRECATED + case FEATURE_GLOBAL_MENU: { + return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU)); + } break; +#endif case FEATURE_SUBWINDOWS: case FEATURE_TOUCHSCREEN: case FEATURE_MOUSE: @@ -5173,6 +5178,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win if (tts_enabled) { tts = memnew(TTS_Windows); } + native_menu = memnew(NativeMenu); // Enforce default keep screen on value. screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on")); @@ -5532,6 +5538,11 @@ DisplayServerWindows::~DisplayServerWindows() { // Close power request handle. screen_set_keep_on(false); + if (native_menu) { + memdelete(native_menu); + native_menu = nullptr; + } + #ifdef GLES3_ENABLED // destroy windows .. NYI? // FIXME wglDeleteContext is never called diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 81cddec49f5..db1e949acfb 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -356,6 +356,7 @@ class DisplayServerWindows : public DisplayServer { HANDLE power_request; TTS_Windows *tts = nullptr; + NativeMenu *native_menu = nullptr; struct WindowData { HWND hWnd; diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp index 301de291a42..b8563f22251 100644 --- a/scene/gui/menu_bar.cpp +++ b/scene/gui/menu_bar.cpp @@ -192,31 +192,33 @@ bool MenuBar::is_native_menu() const { } #endif - return (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU) && is_native); + return (NativeMenu::get_singleton()->has_feature(NativeMenu::FEATURE_GLOBAL_MENU) && is_native); } -String MenuBar::bind_global_menu() { +void MenuBar::bind_global_menu() { #ifdef TOOLS_ENABLED if (is_part_of_edited_scene()) { - return String(); + return; } #endif - if (!DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { - return String(); + if (!NativeMenu::get_singleton()->has_feature(NativeMenu::FEATURE_GLOBAL_MENU)) { + return; } - if (!global_menu_name.is_empty()) { - return global_menu_name; // Already bound. + if (!global_menu_tag.is_empty()) { + return; // Already bound. } - DisplayServer *ds = DisplayServer::get_singleton(); - global_menu_name = "__MenuBar#" + itos(get_instance_id()); + NativeMenu *nmenu = NativeMenu::get_singleton(); + RID main_menu = nmenu->get_system_menu(NativeMenu::MAIN_MENU_ID); + + global_menu_tag = "__MenuBar#" + itos(get_instance_id()); int global_start_idx = -1; - int count = ds->global_menu_get_item_count("_main"); + int count = nmenu->get_item_count(main_menu); String prev_tag; for (int i = 0; i < count; i++) { - String tag = ds->global_menu_get_item_tag("_main", i).operator String().get_slice("#", 1); + String tag = nmenu->get_item_tag(main_menu, i).operator String().get_slice("#", 1); if (!tag.is_empty() && tag != prev_tag) { if (i >= start_index) { global_start_idx = i; @@ -231,40 +233,39 @@ String MenuBar::bind_global_menu() { Vector popups = _get_popups(); for (int i = 0; i < menu_cache.size(); i++) { - String submenu_name = popups[i]->bind_global_menu(); + RID submenu_rid = popups[i]->bind_global_menu(); if (!popups[i]->is_system_menu()) { - int index = ds->global_menu_add_submenu_item("_main", menu_cache[i].name, submenu_name, global_start_idx + i); + 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].global_index = index; - ds->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(i)); - ds->global_menu_set_item_hidden("_main", index, menu_cache[i].hidden); - ds->global_menu_set_item_disabled("_main", index, menu_cache[i].disabled); - ds->global_menu_set_item_tooltip("_main", index, menu_cache[i].tooltip); + 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].global_index = -1; } } - - return global_menu_name; } void MenuBar::unbind_global_menu() { - if (global_menu_name.is_empty()) { + if (global_menu_tag.is_empty()) { return; } - DisplayServer *ds = DisplayServer::get_singleton(); + NativeMenu *nmenu = NativeMenu::get_singleton(); + RID main_menu = nmenu->get_system_menu(NativeMenu::MAIN_MENU_ID); + Vector popups = _get_popups(); for (int i = menu_cache.size() - 1; i >= 0; i--) { if (!popups[i]->is_system_menu()) { popups[i]->unbind_global_menu(); if (menu_cache[i].global_index >= 0) { - ds->global_menu_remove_item("_main", menu_cache[i].global_index); + nmenu->remove_item(main_menu, menu_cache[i].global_index); } menu_cache.write[i].global_index = -1; } } - global_menu_name = String(); + global_menu_tag = String(); } void MenuBar::_notification(int p_what) { @@ -286,12 +287,13 @@ void MenuBar::_notification(int p_what) { queue_redraw(); } break; case NOTIFICATION_TRANSLATION_CHANGED: { - DisplayServer *ds = DisplayServer::get_singleton(); - bool is_global = !global_menu_name.is_empty(); + NativeMenu *nmenu = NativeMenu::get_singleton(); + RID main_menu = nmenu->get_system_menu(NativeMenu::MAIN_MENU_ID); + bool is_global = !global_menu_tag.is_empty(); for (int i = 0; i < menu_cache.size(); i++) { shape(menu_cache.write[i]); if (is_global && menu_cache[i].global_index >= 0) { - ds->global_menu_set_item_text("_main", menu_cache[i].global_index, atr(menu_cache[i].name)); + nmenu->set_item_text(main_menu, menu_cache[i].global_index, atr(menu_cache[i].name)); } } } break; @@ -489,8 +491,9 @@ void MenuBar::shape(Menu &p_menu) { } void MenuBar::_refresh_menu_names() { - DisplayServer *ds = DisplayServer::get_singleton(); - bool is_global = !global_menu_name.is_empty(); + NativeMenu *nmenu = NativeMenu::get_singleton(); + RID main_menu = nmenu->get_system_menu(NativeMenu::MAIN_MENU_ID); + bool is_global = !global_menu_tag.is_empty(); Vector popups = _get_popups(); for (int i = 0; i < popups.size(); i++) { @@ -498,7 +501,7 @@ void MenuBar::_refresh_menu_names() { menu_cache.write[i].name = popups[i]->get_name(); shape(menu_cache.write[i]); if (is_global && menu_cache[i].global_index >= 0) { - ds->global_menu_set_item_text("_main", menu_cache[i].global_index, atr(menu_cache[i].name)); + nmenu->set_item_text(main_menu, menu_cache[i].global_index, atr(menu_cache[i].name)); } } } @@ -545,12 +548,14 @@ void MenuBar::add_child_notify(Node *p_child) { p_child->connect("about_to_popup", callable_mp(this, &MenuBar::_popup_visibility_changed).bind(true)); p_child->connect("popup_hide", callable_mp(this, &MenuBar::_popup_visibility_changed).bind(false)); - if (!global_menu_name.is_empty()) { - String submenu_name = pm->bind_global_menu(); + if (!global_menu_tag.is_empty()) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + RID main_menu = nmenu->get_system_menu(NativeMenu::MAIN_MENU_ID); + + RID submenu_rid = pm->bind_global_menu(); if (!pm->is_system_menu()) { - int index = DisplayServer::get_singleton()->global_menu_add_submenu_item("_main", atr(menu.name), submenu_name, _find_global_start_index() + menu_cache.size() - 1); + int index = 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].global_index = index; - DisplayServer::get_singleton()->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(menu_cache.size() - 1)); } } update_minimum_size(); @@ -578,17 +583,19 @@ void MenuBar::move_child_notify(Node *p_child) { int new_idx = get_menu_idx_from_control(pm); menu_cache.insert(new_idx, menu); - if (!global_menu_name.is_empty()) { + if (!global_menu_tag.is_empty()) { if (!pm->is_system_menu()) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + RID main_menu = nmenu->get_system_menu(NativeMenu::MAIN_MENU_ID); + int global_start = _find_global_start_index(); if (menu.global_index >= 0) { - DisplayServer::get_singleton()->global_menu_remove_item("_main", menu.global_index); + nmenu->remove_item(main_menu, menu.global_index); } if (new_idx != -1) { - String submenu_name = pm->bind_global_menu(); - int index = DisplayServer::get_singleton()->global_menu_add_submenu_item("_main", atr(menu.name), submenu_name, global_start + new_idx); + RID submenu_rid = pm->bind_global_menu(); + int index = nmenu->add_submenu_item(main_menu, atr(menu.name), submenu_rid, global_menu_tag + "#" + itos(new_idx), global_start + new_idx); menu_cache.write[new_idx].global_index = index; - DisplayServer::get_singleton()->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(new_idx)); } } } @@ -606,11 +613,13 @@ void MenuBar::remove_child_notify(Node *p_child) { menu_cache.remove_at(idx); - if (!global_menu_name.is_empty()) { + if (!global_menu_tag.is_empty()) { if (!pm->is_system_menu()) { pm->unbind_global_menu(); if (menu_cache[idx].global_index >= 0) { - DisplayServer::get_singleton()->global_menu_remove_item("_main", menu_cache[idx].global_index); + NativeMenu *nmenu = NativeMenu::get_singleton(); + RID main_menu = nmenu->get_system_menu(NativeMenu::MAIN_MENU_ID); + nmenu->remove_item(main_menu, menu_cache[idx].global_index); menu_cache.write[idx].global_index = -1; } } @@ -746,7 +755,7 @@ bool MenuBar::is_flat() const { void MenuBar::set_start_index(int p_index) { if (start_index != p_index) { start_index = p_index; - if (!global_menu_name.is_empty()) { + if (!global_menu_tag.is_empty()) { unbind_global_menu(); bind_global_menu(); } @@ -808,8 +817,10 @@ void MenuBar::set_menu_title(int p_menu, const String &p_title) { } menu_cache.write[p_menu].name = p_title; shape(menu_cache.write[p_menu]); - if (!global_menu_name.is_empty() && menu_cache[p_menu].global_index >= 0) { - DisplayServer::get_singleton()->global_menu_set_item_text("_main", menu_cache[p_menu].global_index, atr(menu_cache[p_menu].name)); + if (!global_menu_tag.is_empty() && menu_cache[p_menu].global_index >= 0) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + RID main_menu = nmenu->get_system_menu(NativeMenu::MAIN_MENU_ID); + nmenu->set_item_text(main_menu, menu_cache[p_menu].global_index, atr(menu_cache[p_menu].name)); } update_minimum_size(); } @@ -824,8 +835,10 @@ void MenuBar::set_menu_tooltip(int p_menu, const String &p_tooltip) { PopupMenu *pm = get_menu_popup(p_menu); pm->set_meta("_menu_tooltip", p_tooltip); menu_cache.write[p_menu].tooltip = p_tooltip; - if (!global_menu_name.is_empty() && menu_cache[p_menu].global_index >= 0) { - DisplayServer::get_singleton()->global_menu_set_item_tooltip("_main", menu_cache[p_menu].global_index, p_tooltip); + if (!global_menu_tag.is_empty() && menu_cache[p_menu].global_index >= 0) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + RID main_menu = nmenu->get_system_menu(NativeMenu::MAIN_MENU_ID); + nmenu->set_item_tooltip(main_menu, menu_cache[p_menu].global_index, p_tooltip); } } @@ -837,8 +850,10 @@ String MenuBar::get_menu_tooltip(int p_menu) const { void MenuBar::set_menu_disabled(int p_menu, bool p_disabled) { ERR_FAIL_INDEX(p_menu, menu_cache.size()); menu_cache.write[p_menu].disabled = p_disabled; - if (!global_menu_name.is_empty() && menu_cache[p_menu].global_index >= 0) { - DisplayServer::get_singleton()->global_menu_set_item_disabled("_main", menu_cache[p_menu].global_index, p_disabled); + if (!global_menu_tag.is_empty() && menu_cache[p_menu].global_index >= 0) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + RID main_menu = nmenu->get_system_menu(NativeMenu::MAIN_MENU_ID); + nmenu->set_item_disabled(main_menu, menu_cache[p_menu].global_index, p_disabled); } } @@ -850,8 +865,10 @@ bool MenuBar::is_menu_disabled(int p_menu) const { void MenuBar::set_menu_hidden(int p_menu, bool p_hidden) { ERR_FAIL_INDEX(p_menu, menu_cache.size()); menu_cache.write[p_menu].hidden = p_hidden; - if (!global_menu_name.is_empty() && menu_cache[p_menu].global_index >= 0) { - DisplayServer::get_singleton()->global_menu_set_item_hidden("_main", menu_cache[p_menu].global_index, p_hidden); + if (!global_menu_tag.is_empty() && menu_cache[p_menu].global_index >= 0) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + RID main_menu = nmenu->get_system_menu(NativeMenu::MAIN_MENU_ID); + nmenu->set_item_hidden(main_menu, menu_cache[p_menu].global_index, p_hidden); } update_minimum_size(); } diff --git a/scene/gui/menu_bar.h b/scene/gui/menu_bar.h index c7d44aa927e..962eea593ff 100644 --- a/scene/gui/menu_bar.h +++ b/scene/gui/menu_bar.h @@ -114,23 +114,30 @@ class MenuBar : public Control { void _open_popup(int p_index, bool p_focus_item = false); void _popup_visibility_changed(bool p_visible); - String global_menu_name; + String global_menu_tag; int _find_global_start_index() { - if (global_menu_name.is_empty()) { + if (global_menu_tag.is_empty()) { return -1; } - DisplayServer *ds = DisplayServer::get_singleton(); - int count = ds->global_menu_get_item_count("_main"); + NativeMenu *nmenu = NativeMenu::get_singleton(); + if (!nmenu) { + return -1; + } + RID main_menu = nmenu->get_system_menu(NativeMenu::MAIN_MENU_ID); + int count = nmenu->get_item_count(main_menu); for (int i = 0; i < count; i++) { - if (ds->global_menu_get_item_tag("_main", i).operator String().begins_with(global_menu_name)) { + if (nmenu->get_item_tag(main_menu, i).operator String().begins_with(global_menu_tag)) { return i; } } return -1; } + void bind_global_menu(); + void unbind_global_menu(); + protected: virtual void shortcut_input(const Ref &p_event) override; @@ -143,9 +150,6 @@ protected: public: virtual void gui_input(const Ref &p_event) override; - String bind_global_menu(); - void unbind_global_menu(); - void set_switch_on_hover(bool p_enabled); bool is_switch_on_hover(); void set_disable_shortcuts(bool p_disabled); diff --git a/scene/gui/popup_menu.compat.inc b/scene/gui/popup_menu.compat.inc index 012d6a13346..3d41c626344 100644 --- a/scene/gui/popup_menu.compat.inc +++ b/scene/gui/popup_menu.compat.inc @@ -42,10 +42,40 @@ void PopupMenu::_clear_bind_compat_79965() { clear(false); } +void PopupMenu::_set_system_menu_root_compat_87452(const String &p_special) { + if (p_special == "_dock") { + set_system_menu(NativeMenu::DOCK_MENU_ID); + } else if (p_special == "_apple") { + set_system_menu(NativeMenu::APPLICATION_MENU_ID); + } else if (p_special == "_window") { + set_system_menu(NativeMenu::WINDOW_MENU_ID); + } else if (p_special == "_help") { + set_system_menu(NativeMenu::HELP_MENU_ID); + } +} + +String PopupMenu::_get_system_menu_root_compat_87452() const { + switch (get_system_menu()) { + case NativeMenu::APPLICATION_MENU_ID: + return "_apple"; + case NativeMenu::WINDOW_MENU_ID: + return "_window"; + case NativeMenu::HELP_MENU_ID: + return "_help"; + case NativeMenu::DOCK_MENU_ID: + return "_dock"; + default: + return ""; + } +} + void PopupMenu::_bind_compatibility_methods() { ClassDB::bind_compatibility_method(D_METHOD("add_shortcut", "shortcut", "id", "global"), &PopupMenu::_add_shortcut_bind_compat_36493, DEFVAL(-1), DEFVAL(false)); ClassDB::bind_compatibility_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::_add_icon_shortcut_bind_compat_36493, DEFVAL(-1), DEFVAL(false)); ClassDB::bind_compatibility_method(D_METHOD("clear"), &PopupMenu::_clear_bind_compat_79965); + + ClassDB::bind_compatibility_method(D_METHOD("set_system_menu_root", "special"), &PopupMenu::_set_system_menu_root_compat_87452); + ClassDB::bind_compatibility_method(D_METHOD("get_system_menu_root"), &PopupMenu::_get_system_menu_root_compat_87452); } #endif diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 42100bf9faf..dfe0649d0f7 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -40,18 +40,18 @@ #include "scene/gui/menu_bar.h" #include "scene/theme/theme_db.h" -HashMap PopupMenu::system_menus; +HashMap PopupMenu::system_menus; bool PopupMenu::_set_item_accelerator(int p_index, const Ref &p_ie) { - DisplayServer *ds = DisplayServer::get_singleton(); + NativeMenu *nmenu = NativeMenu::get_singleton(); if (p_ie->get_physical_keycode() == Key::NONE && p_ie->get_keycode() == Key::NONE && p_ie->get_key_label() != Key::NONE) { - ds->global_menu_set_item_accelerator(global_menu_name, p_index, p_ie->get_key_label_with_modifiers()); + nmenu->set_item_accelerator(global_menu, p_index, p_ie->get_key_label_with_modifiers()); return true; } else if (p_ie->get_keycode() != Key::NONE) { - ds->global_menu_set_item_accelerator(global_menu_name, p_index, p_ie->get_keycode_with_modifiers()); + nmenu->set_item_accelerator(global_menu, p_index, p_ie->get_keycode_with_modifiers()); return true; } else if (p_ie->get_physical_keycode() != Key::NONE) { - ds->global_menu_set_item_accelerator(global_menu_name, p_index, ds->keyboard_get_keycode_from_physical(p_ie->get_physical_keycode_with_modifiers())); + nmenu->set_item_accelerator(global_menu, p_index, DisplayServer::get_singleton()->keyboard_get_keycode_from_physical(p_ie->get_physical_keycode_with_modifiers())); return true; } return false; @@ -76,58 +76,60 @@ int PopupMenu::_get_item_checkable_type(int p_index) const { return items[p_index].checkable_type; } -String PopupMenu::bind_global_menu() { +RID PopupMenu::bind_global_menu() { #ifdef TOOLS_ENABLED if (is_part_of_edited_scene()) { - return String(); + return RID(); } #endif - if (!DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { - return String(); + if (!NativeMenu::get_singleton()->has_feature(NativeMenu::FEATURE_GLOBAL_MENU)) { + return RID(); } - if (!global_menu_name.is_empty()) { - return global_menu_name; // Already bound; + if (global_menu.is_valid()) { + return global_menu; // Already bound; } - global_menu_name = "__PopupMenu#" + itos(get_instance_id()); - if (system_menu_name.length() > 0) { - if (system_menus.has(system_menu_name)) { - WARN_PRINT(vformat("Attempting to bind PopupMenu to the special menu %s, but another menu is already bound to it. This menu: %s, current menu: %s", system_menu_name, get_description(), system_menus[system_menu_name]->get_description())); + NativeMenu *nmenu = NativeMenu::get_singleton(); + + if (system_menu_id != NativeMenu::INVALID_MENU_ID) { + if (system_menus.has(system_menu_id)) { + WARN_PRINT(vformat("Attempting to bind PopupMenu to the system menu %s, but another menu is already bound to it. This menu: %s, current menu: %s", nmenu->get_system_menu_name(system_menu_id), get_description(), system_menus[system_menu_id]->get_description())); + global_menu = nmenu->create_menu(); } else { - const Dictionary &supported_special_names = DisplayServer::get_singleton()->global_menu_get_system_menu_roots(); - if (supported_special_names.has(system_menu_name)) { - system_menus[system_menu_name] = this; - global_menu_name = system_menu_name; - } + system_menus[system_menu_id] = this; + system_menu = nmenu->get_system_menu(system_menu_id); + global_menu = system_menu; } + } else { + global_menu = nmenu->create_menu(); } - DisplayServer *ds = DisplayServer::get_singleton(); - ds->global_menu_set_popup_callbacks(global_menu_name, callable_mp(this, &PopupMenu::_about_to_popup), callable_mp(this, &PopupMenu::_about_to_close)); + nmenu->set_popup_open_callback(global_menu, callable_mp(this, &PopupMenu::_about_to_popup)); + nmenu->set_popup_close_callback(global_menu, callable_mp(this, &PopupMenu::_about_to_close)); for (int i = 0; i < items.size(); i++) { Item &item = items.write[i]; if (item.separator) { - ds->global_menu_add_separator(global_menu_name); + nmenu->add_separator(global_menu); } else { - int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), item.shortcut_is_global ? callable_mp(this, &PopupMenu::activate_item) : Callable(), i); + int index = nmenu->add_item(global_menu, item.xl_text, callable_mp(this, &PopupMenu::activate_item), item.shortcut_is_global ? callable_mp(this, &PopupMenu::activate_item) : Callable(), i); if (item.submenu) { - String submenu_name = item.submenu->bind_global_menu(); - ds->global_menu_set_item_submenu(global_menu_name, index, submenu_name); + RID submenu_rid = item.submenu->bind_global_menu(); + nmenu->set_item_submenu(global_menu, index, submenu_rid); item.submenu_bound = true; } if (item.checkable_type == Item::CHECKABLE_TYPE_CHECK_BOX) { - ds->global_menu_set_item_checkable(global_menu_name, index, true); + nmenu->set_item_checkable(global_menu, index, true); } else if (item.checkable_type == Item::CHECKABLE_TYPE_RADIO_BUTTON) { - ds->global_menu_set_item_radio_checkable(global_menu_name, index, true); + nmenu->set_item_radio_checkable(global_menu, index, true); } - ds->global_menu_set_item_checked(global_menu_name, index, item.checked); - ds->global_menu_set_item_disabled(global_menu_name, index, item.disabled); - ds->global_menu_set_item_max_states(global_menu_name, index, item.max_states); - ds->global_menu_set_item_icon(global_menu_name, index, item.icon); - ds->global_menu_set_item_state(global_menu_name, index, item.state); - ds->global_menu_set_item_indentation_level(global_menu_name, index, item.indent); - ds->global_menu_set_item_tooltip(global_menu_name, index, item.tooltip); + nmenu->set_item_checked(global_menu, index, item.checked); + nmenu->set_item_disabled(global_menu, index, item.disabled); + nmenu->set_item_max_states(global_menu, index, item.max_states); + nmenu->set_item_icon(global_menu, index, item.icon); + nmenu->set_item_state(global_menu, index, item.state); + nmenu->set_item_indentation_level(global_menu, index, item.indent); + nmenu->set_item_tooltip(global_menu, index, item.tooltip); if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { Array events = item.shortcut->get_events(); for (int j = 0; j < events.size(); j++) { @@ -137,20 +139,20 @@ String PopupMenu::bind_global_menu() { } } } else if (item.accel != Key::NONE) { - ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + nmenu->set_item_accelerator(global_menu, index, item.accel); } } } - return global_menu_name; + return global_menu; } void PopupMenu::unbind_global_menu() { - if (global_menu_name.is_empty()) { + if (global_menu.is_null()) { return; } - if (global_menu_name == system_menu_name && system_menus[system_menu_name] == this) { - system_menus.erase(system_menu_name); + if (global_menu == system_menu && system_menus[system_menu_id] == this) { + system_menus.erase(system_menu_id); } for (int i = 0; i < items.size(); i++) { @@ -160,27 +162,32 @@ void PopupMenu::unbind_global_menu() { item.submenu_bound = false; } } - DisplayServer::get_singleton()->global_menu_clear(global_menu_name); + if (system_menu != global_menu) { + NativeMenu::get_singleton()->free_menu(global_menu); + } else { + NativeMenu::get_singleton()->clear(global_menu); + } - global_menu_name = String(); + system_menu = RID(); + global_menu = RID(); } bool PopupMenu::is_system_menu() const { - return (global_menu_name == system_menu_name) && (system_menu_name.length() > 0); + return (global_menu == system_menu) && (system_menu_id != NativeMenu::INVALID_MENU_ID); } -void PopupMenu::set_system_menu_root(const String &p_special) { - if (is_inside_tree() && system_menu_name.length() > 0) { +void PopupMenu::set_system_menu(NativeMenu::SystemMenus p_system_menu_id) { + if (is_inside_tree() && system_menu_id != NativeMenu::INVALID_MENU_ID) { unbind_global_menu(); } - system_menu_name = p_special; - if (is_inside_tree() && system_menu_name.length() > 0) { + system_menu_id = p_system_menu_id; + if (is_inside_tree() && system_menu_id != NativeMenu::INVALID_MENU_ID) { bind_global_menu(); } } -String PopupMenu::get_system_menu_root() const { - return system_menu_name; +NativeMenu::SystemMenus PopupMenu::get_system_menu() const { + return system_menu_id; } String PopupMenu::_get_accel_text(const Item &p_item) const { @@ -956,15 +963,12 @@ void PopupMenu::_menu_changed() { void PopupMenu::add_child_notify(Node *p_child) { Window::add_child_notify(p_child); - PopupMenu *pm = Object::cast_to(p_child); - if (!pm) { - return; - } - if (!global_menu_name.is_empty()) { + if (global_menu.is_valid()) { + PopupMenu *pm = Object::cast_to(p_child); for (int i = 0; i < items.size(); i++) { if (items[i].submenu == p_child) { - String submenu_name = pm->bind_global_menu(); - DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, i, submenu_name); + RID submenu_rid = pm->bind_global_menu(); + NativeMenu::get_singleton()->set_item_submenu(global_menu, i, submenu_rid); items.write[i].submenu_bound = true; } } @@ -979,10 +983,10 @@ void PopupMenu::remove_child_notify(Node *p_child) { if (!pm) { return; } - if (Object::cast_to(p_child) && !global_menu_name.is_empty()) { + if (Object::cast_to(p_child) && global_menu.is_valid()) { for (int i = 0; i < items.size(); i++) { if (items[i].submenu == p_child) { - DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, i, String()); + NativeMenu::get_singleton()->set_item_submenu(global_menu, i, RID()); items.write[i].submenu_bound = false; } } @@ -1003,13 +1007,13 @@ void PopupMenu::_notification(int p_what) { if (!is_embedded()) { set_flag(FLAG_NO_FOCUS, true); } - if (system_menu_name.length() > 0) { + if (system_menu_id != NativeMenu::INVALID_MENU_ID) { bind_global_menu(); } } break; case NOTIFICATION_EXIT_TREE: { - if (system_menu_name.length() > 0) { + if (system_menu_id != NativeMenu::INVALID_MENU_ID) { unbind_global_menu(); } } break; @@ -1021,14 +1025,14 @@ void PopupMenu::_notification(int p_what) { } case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_TRANSLATION_CHANGED: { - DisplayServer *ds = DisplayServer::get_singleton(); - bool is_global = !global_menu_name.is_empty(); + NativeMenu *nmenu = NativeMenu::get_singleton(); + bool is_global = global_menu.is_valid(); for (int i = 0; i < items.size(); i++) { Item &item = items.write[i]; item.xl_text = atr(item.text); item.dirty = true; if (is_global) { - ds->global_menu_set_item_text(global_menu_name, i, item.xl_text); + nmenu->set_item_text(global_menu, i, item.xl_text); } _shape_item(i); } @@ -1184,11 +1188,11 @@ void PopupMenu::add_item(const String &p_label, int p_id, Key p_accel) { ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); items.push_back(item); - if (!global_menu_name.is_empty()) { - DisplayServer *ds = DisplayServer::get_singleton(); - int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (global_menu.is_valid()) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + int index = nmenu->add_item(global_menu, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); if (item.accel != Key::NONE) { - ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + nmenu->set_item_accelerator(global_menu, index, item.accel); } } @@ -1206,13 +1210,13 @@ void PopupMenu::add_icon_item(const Ref &p_icon, const String &p_labe item.icon = p_icon; items.push_back(item); - if (!global_menu_name.is_empty()) { - DisplayServer *ds = DisplayServer::get_singleton(); - int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (global_menu.is_valid()) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + int index = nmenu->add_item(global_menu, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); if (item.accel != Key::NONE) { - ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + nmenu->set_item_accelerator(global_menu, index, item.accel); } - ds->global_menu_set_item_icon(global_menu_name, index, item.icon); + nmenu->set_item_icon(global_menu, index, item.icon); } _shape_item(items.size() - 1); @@ -1229,13 +1233,13 @@ void PopupMenu::add_check_item(const String &p_label, int p_id, Key p_accel) { item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); - if (!global_menu_name.is_empty()) { - DisplayServer *ds = DisplayServer::get_singleton(); - int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (global_menu.is_valid()) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + int index = nmenu->add_item(global_menu, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); if (item.accel != Key::NONE) { - ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + nmenu->set_item_accelerator(global_menu, index, item.accel); } - ds->global_menu_set_item_checkable(global_menu_name, index, true); + nmenu->set_item_checkable(global_menu, index, true); } _shape_item(items.size() - 1); @@ -1253,14 +1257,14 @@ void PopupMenu::add_icon_check_item(const Ref &p_icon, const String & item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); - if (!global_menu_name.is_empty()) { - DisplayServer *ds = DisplayServer::get_singleton(); - int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (global_menu.is_valid()) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + int index = nmenu->add_item(global_menu, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); if (item.accel != Key::NONE) { - ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + nmenu->set_item_accelerator(global_menu, index, item.accel); } - ds->global_menu_set_item_icon(global_menu_name, index, item.icon); - ds->global_menu_set_item_checkable(global_menu_name, index, true); + nmenu->set_item_icon(global_menu, index, item.icon); + nmenu->set_item_checkable(global_menu, index, true); } _shape_item(items.size() - 1); @@ -1277,13 +1281,13 @@ void PopupMenu::add_radio_check_item(const String &p_label, int p_id, Key p_acce item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); - if (!global_menu_name.is_empty()) { - DisplayServer *ds = DisplayServer::get_singleton(); - int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (global_menu.is_valid()) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + int index = nmenu->add_item(global_menu, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); if (item.accel != Key::NONE) { - ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + nmenu->set_item_accelerator(global_menu, index, item.accel); } - ds->global_menu_set_item_radio_checkable(global_menu_name, index, true); + nmenu->set_item_radio_checkable(global_menu, index, true); } _shape_item(items.size() - 1); @@ -1301,14 +1305,14 @@ void PopupMenu::add_icon_radio_check_item(const Ref &p_icon, const St item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); - if (!global_menu_name.is_empty()) { - DisplayServer *ds = DisplayServer::get_singleton(); - int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (global_menu.is_valid()) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + int index = nmenu->add_item(global_menu, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); if (item.accel != Key::NONE) { - ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + nmenu->set_item_accelerator(global_menu, index, item.accel); } - ds->global_menu_set_item_icon(global_menu_name, index, item.icon); - ds->global_menu_set_item_radio_checkable(global_menu_name, index, true); + nmenu->set_item_icon(global_menu, index, item.icon); + nmenu->set_item_radio_checkable(global_menu, index, true); } _shape_item(items.size() - 1); @@ -1326,14 +1330,14 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int item.state = p_default_state; items.push_back(item); - if (!global_menu_name.is_empty()) { - DisplayServer *ds = DisplayServer::get_singleton(); - int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (global_menu.is_valid()) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + int index = nmenu->add_item(global_menu, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); if (item.accel != Key::NONE) { - ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + nmenu->set_item_accelerator(global_menu, index, item.accel); } - ds->global_menu_set_item_max_states(global_menu_name, index, item.max_states); - ds->global_menu_set_item_state(global_menu_name, index, item.state); + nmenu->set_item_max_states(global_menu, index, item.max_states); + nmenu->set_item_state(global_menu, index, item.state); } _shape_item(items.size() - 1); @@ -1359,9 +1363,9 @@ void PopupMenu::add_shortcut(const Ref &p_shortcut, int p_id, bool p_g ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, p_allow_echo); items.push_back(item); - if (!global_menu_name.is_empty()) { - DisplayServer *ds = DisplayServer::get_singleton(); - int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), p_global ? callable_mp(this, &PopupMenu::activate_item) : Callable(), items.size() - 1); + if (global_menu.is_valid()) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + int index = nmenu->add_item(global_menu, item.xl_text, callable_mp(this, &PopupMenu::activate_item), p_global ? callable_mp(this, &PopupMenu::activate_item) : Callable(), items.size() - 1); if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { Array events = item.shortcut->get_events(); for (int j = 0; j < events.size(); j++) { @@ -1387,9 +1391,9 @@ void PopupMenu::add_icon_shortcut(const Ref &p_icon, const Refglobal_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), p_global ? callable_mp(this, &PopupMenu::activate_item) : Callable(), items.size() - 1); + if (global_menu.is_valid()) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + int index = nmenu->add_item(global_menu, item.xl_text, callable_mp(this, &PopupMenu::activate_item), p_global ? callable_mp(this, &PopupMenu::activate_item) : Callable(), items.size() - 1); if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { Array events = item.shortcut->get_events(); for (int j = 0; j < events.size(); j++) { @@ -1399,7 +1403,7 @@ void PopupMenu::add_icon_shortcut(const Ref &p_icon, const Refglobal_menu_set_item_icon(global_menu_name, index, item.icon); + nmenu->set_item_icon(global_menu, index, item.icon); } _shape_item(items.size() - 1); @@ -1416,9 +1420,9 @@ void PopupMenu::add_check_shortcut(const Ref &p_shortcut, int p_id, bo item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); - if (!global_menu_name.is_empty()) { - DisplayServer *ds = DisplayServer::get_singleton(); - int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), p_global ? callable_mp(this, &PopupMenu::activate_item) : Callable(), items.size() - 1); + if (global_menu.is_valid()) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + int index = nmenu->add_item(global_menu, item.xl_text, callable_mp(this, &PopupMenu::activate_item), p_global ? callable_mp(this, &PopupMenu::activate_item) : Callable(), items.size() - 1); if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { Array events = item.shortcut->get_events(); for (int j = 0; j < events.size(); j++) { @@ -1428,7 +1432,7 @@ void PopupMenu::add_check_shortcut(const Ref &p_shortcut, int p_id, bo } } } - ds->global_menu_set_item_checkable(global_menu_name, index, true); + nmenu->set_item_checkable(global_menu, index, true); } _shape_item(items.size() - 1); @@ -1446,9 +1450,9 @@ void PopupMenu::add_icon_check_shortcut(const Ref &p_icon, const Ref< item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); - if (!global_menu_name.is_empty()) { - DisplayServer *ds = DisplayServer::get_singleton(); - int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), p_global ? callable_mp(this, &PopupMenu::activate_item) : Callable(), items.size() - 1); + if (global_menu.is_valid()) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + int index = nmenu->add_item(global_menu, item.xl_text, callable_mp(this, &PopupMenu::activate_item), p_global ? callable_mp(this, &PopupMenu::activate_item) : Callable(), items.size() - 1); if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { Array events = item.shortcut->get_events(); for (int j = 0; j < events.size(); j++) { @@ -1458,8 +1462,8 @@ void PopupMenu::add_icon_check_shortcut(const Ref &p_icon, const Ref< } } } - ds->global_menu_set_item_icon(global_menu_name, index, item.icon); - ds->global_menu_set_item_checkable(global_menu_name, index, true); + nmenu->set_item_icon(global_menu, index, item.icon); + nmenu->set_item_checkable(global_menu, index, true); } _shape_item(items.size() - 1); @@ -1476,9 +1480,9 @@ void PopupMenu::add_radio_check_shortcut(const Ref &p_shortcut, int p_ item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); - if (!global_menu_name.is_empty()) { - DisplayServer *ds = DisplayServer::get_singleton(); - int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), p_global ? callable_mp(this, &PopupMenu::activate_item) : Callable(), items.size() - 1); + if (global_menu.is_valid()) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + int index = nmenu->add_item(global_menu, item.xl_text, callable_mp(this, &PopupMenu::activate_item), p_global ? callable_mp(this, &PopupMenu::activate_item) : Callable(), items.size() - 1); if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { Array events = item.shortcut->get_events(); for (int j = 0; j < events.size(); j++) { @@ -1488,7 +1492,7 @@ void PopupMenu::add_radio_check_shortcut(const Ref &p_shortcut, int p_ } } } - ds->global_menu_set_item_radio_checkable(global_menu_name, index, true); + nmenu->set_item_radio_checkable(global_menu, index, true); } _shape_item(items.size() - 1); @@ -1506,9 +1510,9 @@ void PopupMenu::add_icon_radio_check_shortcut(const Ref &p_icon, cons item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); - if (!global_menu_name.is_empty()) { - DisplayServer *ds = DisplayServer::get_singleton(); - int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), p_global ? callable_mp(this, &PopupMenu::activate_item) : Callable(), items.size() - 1); + if (global_menu.is_valid()) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + int index = nmenu->add_item(global_menu, item.xl_text, callable_mp(this, &PopupMenu::activate_item), p_global ? callable_mp(this, &PopupMenu::activate_item) : Callable(), items.size() - 1); if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { Array events = item.shortcut->get_events(); for (int j = 0; j < events.size(); j++) { @@ -1518,8 +1522,8 @@ void PopupMenu::add_icon_radio_check_shortcut(const Ref &p_icon, cons } } } - ds->global_menu_set_item_icon(global_menu_name, index, item.icon); - ds->global_menu_set_item_radio_checkable(global_menu_name, index, true); + nmenu->set_item_icon(global_menu, index, item.icon); + nmenu->set_item_radio_checkable(global_menu, index, true); } _shape_item(items.size() - 1); @@ -1553,11 +1557,11 @@ void PopupMenu::add_submenu_node_item(const String &p_label, PopupMenu *p_submen item.submenu_name = p_submenu->get_name(); items.push_back(item); - if (!global_menu_name.is_empty()) { - DisplayServer *ds = DisplayServer::get_singleton(); - int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); - String submenu_name = p_submenu->bind_global_menu(); - ds->global_menu_set_item_submenu(global_menu_name, index, submenu_name); + if (global_menu.is_valid()) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + int index = nmenu->add_item(global_menu, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + RID submenu_rid = p_submenu->bind_global_menu(); + nmenu->set_item_submenu(global_menu, index, submenu_rid); items.write[index].submenu_bound = true; } @@ -1586,8 +1590,8 @@ void PopupMenu::set_item_text(int p_idx, const String &p_text) { items.write[p_idx].xl_text = atr(p_text); items.write[p_idx].dirty = true; - if (!global_menu_name.is_empty()) { - DisplayServer::get_singleton()->global_menu_set_item_text(global_menu_name, p_idx, items[p_idx].xl_text); + if (global_menu.is_valid()) { + NativeMenu::get_singleton()->set_item_text(global_menu, p_idx, items[p_idx].xl_text); } _shape_item(p_idx); @@ -1633,8 +1637,8 @@ void PopupMenu::set_item_icon(int p_idx, const Ref &p_icon) { items.write[p_idx].icon = p_icon; - if (!global_menu_name.is_empty()) { - DisplayServer::get_singleton()->global_menu_set_item_icon(global_menu_name, p_idx, items[p_idx].icon); + if (global_menu.is_valid()) { + NativeMenu::get_singleton()->set_item_icon(global_menu, p_idx, items[p_idx].icon); } control->queue_redraw(); @@ -1685,8 +1689,8 @@ void PopupMenu::set_item_checked(int p_idx, bool p_checked) { items.write[p_idx].checked = p_checked; - if (!global_menu_name.is_empty()) { - DisplayServer::get_singleton()->global_menu_set_item_checked(global_menu_name, p_idx, p_checked); + if (global_menu.is_valid()) { + NativeMenu::get_singleton()->set_item_checked(global_menu, p_idx, p_checked); } control->queue_redraw(); @@ -1706,8 +1710,8 @@ void PopupMenu::set_item_id(int p_idx, int p_id) { items.write[p_idx].id = p_id; - if (!global_menu_name.is_empty()) { - DisplayServer::get_singleton()->global_menu_set_item_tag(global_menu_name, p_idx, p_id); + if (global_menu.is_valid()) { + NativeMenu::get_singleton()->set_item_tag(global_menu, p_idx, p_id); } control->queue_redraw(); @@ -1728,8 +1732,8 @@ void PopupMenu::set_item_accelerator(int p_idx, Key p_accel) { items.write[p_idx].accel = p_accel; items.write[p_idx].dirty = true; - if (!global_menu_name.is_empty()) { - DisplayServer::get_singleton()->global_menu_set_item_accelerator(global_menu_name, p_idx, p_accel); + if (global_menu.is_valid()) { + NativeMenu::get_singleton()->set_item_accelerator(global_menu, p_idx, p_accel); } control->queue_redraw(); @@ -1764,8 +1768,8 @@ void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) { items.write[p_idx].disabled = p_disabled; - if (!global_menu_name.is_empty()) { - DisplayServer::get_singleton()->global_menu_set_item_disabled(global_menu_name, p_idx, p_disabled); + if (global_menu.is_valid()) { + NativeMenu::get_singleton()->set_item_disabled(global_menu, p_idx, p_disabled); } control->queue_redraw(); @@ -1801,11 +1805,11 @@ void PopupMenu::set_item_submenu_node(int p_idx, PopupMenu *p_submenu) { add_child(p_submenu); } - if (!global_menu_name.is_empty()) { + if (global_menu.is_valid()) { if (items[p_idx].submenu_bound) { PopupMenu *pm = items[p_idx].submenu; if (pm) { - DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, p_idx, String()); + NativeMenu::get_singleton()->set_item_submenu(global_menu, p_idx, RID()); pm->unbind_global_menu(); } items.write[p_idx].submenu_bound = false; @@ -1814,10 +1818,11 @@ void PopupMenu::set_item_submenu_node(int p_idx, PopupMenu *p_submenu) { items.write[p_idx].submenu = p_submenu; - if (!global_menu_name.is_empty()) { - if (items[p_idx].submenu) { - String submenu_name = p_submenu->bind_global_menu(); - DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, p_idx, submenu_name); + if (global_menu.is_valid()) { + PopupMenu *pm = items[p_idx].submenu; + if (pm) { + RID submenu_rid = pm->bind_global_menu(); + NativeMenu::get_singleton()->set_item_submenu(global_menu, p_idx, submenu_rid); items.write[p_idx].submenu_bound = true; } } @@ -1831,8 +1836,8 @@ void PopupMenu::toggle_item_checked(int p_idx) { ERR_FAIL_INDEX(p_idx, items.size()); items.write[p_idx].checked = !items[p_idx].checked; - if (!global_menu_name.is_empty()) { - DisplayServer::get_singleton()->global_menu_set_item_checked(global_menu_name, p_idx, items[p_idx].checked); + if (global_menu.is_valid()) { + NativeMenu::get_singleton()->set_item_checked(global_menu, p_idx, items[p_idx].checked); } control->queue_redraw(); @@ -1987,8 +1992,8 @@ void PopupMenu::set_item_as_checkable(int p_idx, bool p_checkable) { items.write[p_idx].checkable_type = p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE; - if (!global_menu_name.is_empty()) { - DisplayServer::get_singleton()->global_menu_set_item_checkable(global_menu_name, p_idx, p_checkable); + if (global_menu.is_valid()) { + NativeMenu::get_singleton()->set_item_checkable(global_menu, p_idx, p_checkable); } control->queue_redraw(); @@ -2008,8 +2013,8 @@ void PopupMenu::set_item_as_radio_checkable(int p_idx, bool p_radio_checkable) { items.write[p_idx].checkable_type = p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE; - if (!global_menu_name.is_empty()) { - DisplayServer::get_singleton()->global_menu_set_item_radio_checkable(global_menu_name, p_idx, p_radio_checkable); + if (global_menu.is_valid()) { + NativeMenu::get_singleton()->set_item_radio_checkable(global_menu, p_idx, p_radio_checkable); } control->queue_redraw(); @@ -2028,8 +2033,8 @@ void PopupMenu::set_item_tooltip(int p_idx, const String &p_tooltip) { items.write[p_idx].tooltip = p_tooltip; - if (!global_menu_name.is_empty()) { - DisplayServer::get_singleton()->global_menu_set_item_tooltip(global_menu_name, p_idx, p_tooltip); + if (global_menu.is_valid()) { + NativeMenu::get_singleton()->set_item_tooltip(global_menu, p_idx, p_tooltip); } control->queue_redraw(); @@ -2057,9 +2062,9 @@ void PopupMenu::set_item_shortcut(int p_idx, const Ref &p_shortcut, bo _ref_shortcut(items[p_idx].shortcut); } - if (!global_menu_name.is_empty()) { - DisplayServer *ds = DisplayServer::get_singleton(); - ds->global_menu_set_item_accelerator(global_menu_name, p_idx, Key::NONE); + if (global_menu.is_valid()) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + nmenu->set_item_accelerator(global_menu, p_idx, Key::NONE); if (!items[p_idx].shortcut_is_disabled && items[p_idx].shortcut.is_valid() && items[p_idx].shortcut->has_valid_event()) { Array events = items[p_idx].shortcut->get_events(); for (int j = 0; j < events.size(); j++) { @@ -2069,9 +2074,9 @@ void PopupMenu::set_item_shortcut(int p_idx, const Ref &p_shortcut, bo } } if (p_global) { - ds->global_menu_set_item_key_callback(global_menu_name, p_idx, callable_mp(this, &PopupMenu::activate_item)); + nmenu->set_item_key_callback(global_menu, p_idx, callable_mp(this, &PopupMenu::activate_item)); } else { - ds->global_menu_set_item_key_callback(global_menu_name, p_idx, Callable()); + nmenu->set_item_key_callback(global_menu, p_idx, Callable()); } } } @@ -2091,8 +2096,8 @@ void PopupMenu::set_item_indent(int p_idx, int p_indent) { } items.write[p_idx].indent = p_indent; - if (!global_menu_name.is_empty()) { - DisplayServer::get_singleton()->global_menu_set_item_indentation_level(global_menu_name, p_idx, p_indent); + if (global_menu.is_valid()) { + NativeMenu::get_singleton()->set_item_indentation_level(global_menu, p_idx, p_indent); } control->queue_redraw(); @@ -2112,8 +2117,8 @@ void PopupMenu::set_item_max_states(int p_idx, int p_max_states) { items.write[p_idx].max_states = p_max_states; - if (!global_menu_name.is_empty()) { - DisplayServer::get_singleton()->global_menu_set_item_max_states(global_menu_name, p_idx, p_max_states); + if (global_menu.is_valid()) { + NativeMenu::get_singleton()->set_item_max_states(global_menu, p_idx, p_max_states); } control->queue_redraw(); @@ -2132,8 +2137,8 @@ void PopupMenu::set_item_multistate(int p_idx, int p_state) { items.write[p_idx].state = p_state; - if (!global_menu_name.is_empty()) { - DisplayServer::get_singleton()->global_menu_set_item_state(global_menu_name, p_idx, p_state); + if (global_menu.is_valid()) { + NativeMenu::get_singleton()->set_item_state(global_menu, p_idx, p_state); } control->queue_redraw(); @@ -2152,9 +2157,9 @@ void PopupMenu::set_item_shortcut_disabled(int p_idx, bool p_disabled) { items.write[p_idx].shortcut_is_disabled = p_disabled; - if (!global_menu_name.is_empty()) { - DisplayServer *ds = DisplayServer::get_singleton(); - ds->global_menu_set_item_accelerator(global_menu_name, p_idx, Key::NONE); + if (global_menu.is_valid()) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + nmenu->set_item_accelerator(global_menu, p_idx, Key::NONE); if (!items[p_idx].shortcut_is_disabled && items[p_idx].shortcut.is_valid() && items[p_idx].shortcut->has_valid_event()) { Array events = items[p_idx].shortcut->get_events(); for (int j = 0; j < events.size(); j++) { @@ -2181,8 +2186,8 @@ void PopupMenu::toggle_item_multistate(int p_idx) { items.write[p_idx].state = 0; } - if (!global_menu_name.is_empty()) { - DisplayServer::get_singleton()->global_menu_set_item_state(global_menu_name, p_idx, items[p_idx].state); + if (global_menu.is_valid()) { + NativeMenu::get_singleton()->set_item_state(global_menu, p_idx, items[p_idx].state); } control->queue_redraw(); @@ -2238,12 +2243,12 @@ void PopupMenu::set_item_count(int p_count) { return; } - DisplayServer *ds = DisplayServer::get_singleton(); - bool is_global = !global_menu_name.is_empty(); + NativeMenu *nmenu = NativeMenu::get_singleton(); + bool is_global = global_menu.is_valid(); if (is_global && prev_size > p_count) { for (int i = prev_size - 1; i >= p_count; i--) { - ds->global_menu_remove_item(global_menu_name, i); + nmenu->remove_item(global_menu, i); } } @@ -2253,7 +2258,7 @@ void PopupMenu::set_item_count(int p_count) { for (int i = prev_size; i < p_count; i++) { items.write[i].id = i; if (is_global) { - ds->global_menu_add_item(global_menu_name, String(), callable_mp(this, &PopupMenu::activate_item), Callable(), i); + nmenu->add_item(global_menu, String(), callable_mp(this, &PopupMenu::activate_item), Callable(), i); } } } @@ -2405,8 +2410,8 @@ void PopupMenu::remove_item(int p_idx) { items.remove_at(p_idx); - if (!global_menu_name.is_empty()) { - DisplayServer::get_singleton()->global_menu_remove_item(global_menu_name, p_idx); + if (global_menu.is_valid()) { + NativeMenu::get_singleton()->remove_item(global_menu, p_idx); } control->queue_redraw(); @@ -2424,8 +2429,8 @@ void PopupMenu::add_separator(const String &p_text, int p_id) { } items.push_back(sep); - if (!global_menu_name.is_empty()) { - DisplayServer::get_singleton()->global_menu_add_separator(global_menu_name); + if (global_menu.is_valid()) { + NativeMenu::get_singleton()->add_separator(global_menu); } control->queue_redraw(); @@ -2444,15 +2449,15 @@ void PopupMenu::clear(bool p_free_submenus) { } } - if (!global_menu_name.is_empty()) { - DisplayServer *ds = DisplayServer::get_singleton(); + if (global_menu.is_valid()) { + NativeMenu *nmenu = NativeMenu::get_singleton(); for (int i = items.size() - 1; i >= 0; i--) { Item &item = items.write[i]; if (item.submenu) { item.submenu->unbind_global_menu(); item.submenu_bound = false; } - ds->global_menu_remove_item(global_menu_name, i); + nmenu->remove_item(global_menu, i); } } items.clear(); @@ -2709,15 +2714,15 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("get_allow_search"), &PopupMenu::get_allow_search); ClassDB::bind_method(D_METHOD("is_system_menu"), &PopupMenu::is_system_menu); - ClassDB::bind_method(D_METHOD("set_system_menu_root", "special"), &PopupMenu::set_system_menu_root); - ClassDB::bind_method(D_METHOD("get_system_menu_root"), &PopupMenu::get_system_menu_root); + ClassDB::bind_method(D_METHOD("set_system_menu", "system_menu_id"), &PopupMenu::set_system_menu); + ClassDB::bind_method(D_METHOD("get_system_menu"), &PopupMenu::get_system_menu); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_item_selection"), "set_hide_on_item_selection", "is_hide_on_item_selection"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "submenu_popup_delay", PROPERTY_HINT_NONE, "suffix:s"), "set_submenu_popup_delay", "get_submenu_popup_delay"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_search"), "set_allow_search", "get_allow_search"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "system_menu_root", PROPERTY_HINT_ENUM, "Dock (macOS):_dock,Apple Menu(macOS):_apple,Window Menu(macOS):_window,Help Menu(macOS):_help"), "set_system_menu_root", "get_system_menu_root"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "system_menu_id", PROPERTY_HINT_ENUM, "Application Menu:2,Window Menu:3,Help Menu:4,Dock:5"), "set_system_menu", "get_system_menu"); ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "item_"); @@ -2818,4 +2823,5 @@ PopupMenu::PopupMenu() { } PopupMenu::~PopupMenu() { + unbind_global_menu(); } diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 492084b43ff..602d5466a94 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -40,7 +40,7 @@ class PopupMenu : public Popup { GDCLASS(PopupMenu, Popup); - static HashMap system_menus; + static HashMap system_menus; struct Item { Ref icon; @@ -97,8 +97,9 @@ class PopupMenu : public Popup { static inline PropertyListHelper base_property_helper; PropertyListHelper property_helper; - String global_menu_name; - String system_menu_name; + RID global_menu; + RID system_menu; + NativeMenu::SystemMenus system_menu_id = NativeMenu::INVALID_MENU_ID; bool close_allowed = false; bool activated_by_keyboard = false; @@ -221,6 +222,10 @@ protected: void _add_shortcut_bind_compat_36493(const Ref &p_shortcut, int p_id = -1, bool p_global = false); void _add_icon_shortcut_bind_compat_36493(const Ref &p_icon, const Ref &p_shortcut, int p_id = -1, bool p_global = false); void _clear_bind_compat_79965(); + + void _set_system_menu_root_compat_87452(const String &p_special); + String _get_system_menu_root_compat_87452() const; + static void _bind_compatibility_methods(); #endif @@ -231,11 +236,12 @@ public: virtual void _parent_focused() override; - String bind_global_menu(); + RID bind_global_menu(); void unbind_global_menu(); bool is_system_menu() const; - void set_system_menu_root(const String &p_special); - String get_system_menu_root() const; + + void set_system_menu(NativeMenu::SystemMenus p_system_menu_id); + NativeMenu::SystemMenus get_system_menu() const; void add_item(const String &p_label, int p_id = -1, Key p_accel = Key::NONE); void add_icon_item(const Ref &p_icon, const String &p_label, int p_id = -1, Key p_accel = Key::NONE); diff --git a/servers/display_server.cpp b/servers/display_server.cpp index 199152b5e89..72228c25f26 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -52,231 +52,352 @@ void DisplayServer::help_set_search_callbacks(const Callable &p_search_callback, WARN_PRINT("Native help is not supported by this display server."); } +#ifndef DISABLE_DEPRECATED + +RID DisplayServer::_get_rid_from_name(NativeMenu *p_nmenu, const String &p_menu_root) const { + if (p_menu_root == "_main") { + return p_nmenu->get_system_menu(NativeMenu::MAIN_MENU_ID); + } else if (p_menu_root == "_apple") { + return p_nmenu->get_system_menu(NativeMenu::APPLICATION_MENU_ID); + } else if (p_menu_root == "_dock") { + return p_nmenu->get_system_menu(NativeMenu::DOCK_MENU_ID); + } else if (p_menu_root == "_help") { + return p_nmenu->get_system_menu(NativeMenu::HELP_MENU_ID); + } else if (p_menu_root == "_window") { + return p_nmenu->get_system_menu(NativeMenu::WINDOW_MENU_ID); + } else if (menu_names.has(p_menu_root)) { + return menu_names[p_menu_root]; + } + + RID rid = p_nmenu->create_menu(); + menu_names[p_menu_root] = rid; + return rid; +} + int DisplayServer::global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { - WARN_PRINT("Global menus not supported by this display server."); - return -1; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, -1); + return nmenu->add_item(_get_rid_from_name(nmenu, p_menu_root), p_label, p_callback, p_key_callback, p_tag, p_accel, p_index); } int DisplayServer::global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { - WARN_PRINT("Global menus not supported by this display server."); - return -1; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, -1); + return nmenu->add_check_item(_get_rid_from_name(nmenu, p_menu_root), p_label, p_callback, p_key_callback, p_tag, p_accel, p_index); } int DisplayServer::global_menu_add_icon_item(const String &p_menu_root, const Ref &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { - WARN_PRINT("Global menus not supported by this display server."); - return -1; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, -1); + return nmenu->add_icon_item(_get_rid_from_name(nmenu, p_menu_root), p_icon, p_label, p_callback, p_key_callback, p_tag, p_accel, p_index); } int DisplayServer::global_menu_add_icon_check_item(const String &p_menu_root, const Ref &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { - WARN_PRINT("Global menus not supported by this display server."); - return -1; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, -1); + return nmenu->add_icon_check_item(_get_rid_from_name(nmenu, p_menu_root), p_icon, p_label, p_callback, p_key_callback, p_tag, p_accel, p_index); } int DisplayServer::global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { - WARN_PRINT("Global menus not supported by this display server."); - return -1; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, -1); + return nmenu->add_radio_check_item(_get_rid_from_name(nmenu, p_menu_root), p_label, p_callback, p_key_callback, p_tag, p_accel, p_index); } int DisplayServer::global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { - WARN_PRINT("Global menus not supported by this display server."); - return -1; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, -1); + return nmenu->add_icon_radio_check_item(_get_rid_from_name(nmenu, p_menu_root), p_icon, p_label, p_callback, p_key_callback, p_tag, p_accel, p_index); } int DisplayServer::global_menu_add_multistate_item(const String &p_menu_root, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { - WARN_PRINT("Global menus not supported by this display server."); - return -1; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, -1); + return nmenu->add_multistate_item(_get_rid_from_name(nmenu, p_menu_root), p_label, p_max_states, p_default_state, p_callback, p_key_callback, p_tag, p_accel, p_index); } -void DisplayServer::global_menu_set_popup_callbacks(const String &p_menu_root, const Callable &p_open_callbacs, const Callable &p_close_callback) { - WARN_PRINT("Global menus not supported by this display server."); +void DisplayServer::global_menu_set_popup_callbacks(const String &p_menu_root, const Callable &p_open_callback, const Callable &p_close_callback) { + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + nmenu->set_popup_open_callback(_get_rid_from_name(nmenu, p_menu_root), p_open_callback); + nmenu->set_popup_open_callback(_get_rid_from_name(nmenu, p_menu_root), p_close_callback); } int DisplayServer::global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index) { - WARN_PRINT("Global menus not supported by this display server."); - return -1; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, -1); + return nmenu->add_submenu_item(_get_rid_from_name(nmenu, p_menu_root), p_label, _get_rid_from_name(nmenu, p_submenu), Variant(), p_index); } int DisplayServer::global_menu_add_separator(const String &p_menu_root, int p_index) { - WARN_PRINT("Global menus not supported by this display server."); - return -1; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, -1); + return nmenu->add_separator(_get_rid_from_name(nmenu, p_menu_root), p_index); } int DisplayServer::global_menu_get_item_index_from_text(const String &p_menu_root, const String &p_text) const { - WARN_PRINT("Global menus not supported by this display server."); - return -1; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, -1); + return nmenu->find_item_index_with_text(_get_rid_from_name(nmenu, p_menu_root), p_text); } int DisplayServer::global_menu_get_item_index_from_tag(const String &p_menu_root, const Variant &p_tag) const { - WARN_PRINT("Global menus not supported by this display server."); - return -1; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, -1); + return nmenu->find_item_index_with_tag(_get_rid_from_name(nmenu, p_menu_root), p_tag); } void DisplayServer::global_menu_set_item_callback(const String &p_menu_root, int p_idx, const Callable &p_callback) { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + nmenu->set_item_callback(_get_rid_from_name(nmenu, p_menu_root), p_idx, p_callback); } void DisplayServer::global_menu_set_item_hover_callbacks(const String &p_menu_root, int p_idx, const Callable &p_callback) { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + nmenu->set_item_hover_callbacks(_get_rid_from_name(nmenu, p_menu_root), p_idx, p_callback); } void DisplayServer::global_menu_set_item_key_callback(const String &p_menu_root, int p_idx, const Callable &p_key_callback) { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + nmenu->set_item_key_callback(_get_rid_from_name(nmenu, p_menu_root), p_idx, p_key_callback); } bool DisplayServer::global_menu_is_item_checked(const String &p_menu_root, int p_idx) const { - WARN_PRINT("Global menus not supported by this display server."); - return false; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, false); + return nmenu->is_item_checked(_get_rid_from_name(nmenu, p_menu_root), p_idx); } bool DisplayServer::global_menu_is_item_checkable(const String &p_menu_root, int p_idx) const { - WARN_PRINT("Global menus not supported by this display server."); - return false; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, false); + return nmenu->is_item_checkable(_get_rid_from_name(nmenu, p_menu_root), p_idx); } bool DisplayServer::global_menu_is_item_radio_checkable(const String &p_menu_root, int p_idx) const { - WARN_PRINT("Global menus not supported by this display server."); - return false; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, false); + return nmenu->is_item_radio_checkable(_get_rid_from_name(nmenu, p_menu_root), p_idx); } Callable DisplayServer::global_menu_get_item_callback(const String &p_menu_root, int p_idx) const { - WARN_PRINT("Global menus not supported by this display server."); - return Callable(); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, Callable()); + return nmenu->get_item_callback(_get_rid_from_name(nmenu, p_menu_root), p_idx); } Callable DisplayServer::global_menu_get_item_key_callback(const String &p_menu_root, int p_idx) const { - WARN_PRINT("Global menus not supported by this display server."); - return Callable(); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, Callable()); + return nmenu->get_item_key_callback(_get_rid_from_name(nmenu, p_menu_root), p_idx); } Variant DisplayServer::global_menu_get_item_tag(const String &p_menu_root, int p_idx) const { - WARN_PRINT("Global menus not supported by this display server."); - return Variant(); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, Variant()); + return nmenu->get_item_tag(_get_rid_from_name(nmenu, p_menu_root), p_idx); } String DisplayServer::global_menu_get_item_text(const String &p_menu_root, int p_idx) const { - WARN_PRINT("Global menus not supported by this display server."); - return String(); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, String()); + return nmenu->get_item_text(_get_rid_from_name(nmenu, p_menu_root), p_idx); } String DisplayServer::global_menu_get_item_submenu(const String &p_menu_root, int p_idx) const { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, String()); + RID rid = nmenu->get_item_submenu(_get_rid_from_name(nmenu, p_menu_root), p_idx); + if (!nmenu->is_system_menu(rid)) { + for (HashMap::Iterator E = menu_names.begin(); E;) { + if (E->value == rid) { + return E->key; + } + } + } return String(); } Key DisplayServer::global_menu_get_item_accelerator(const String &p_menu_root, int p_idx) const { - WARN_PRINT("Global menus not supported by this display server."); - return Key::NONE; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, Key::NONE); + return nmenu->get_item_accelerator(_get_rid_from_name(nmenu, p_menu_root), p_idx); } bool DisplayServer::global_menu_is_item_disabled(const String &p_menu_root, int p_idx) const { - WARN_PRINT("Global menus not supported by this display server."); - return false; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, false); + return nmenu->is_item_disabled(_get_rid_from_name(nmenu, p_menu_root), p_idx); } bool DisplayServer::global_menu_is_item_hidden(const String &p_menu_root, int p_idx) const { - WARN_PRINT("Global menus not supported by this display server."); - return false; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, false); + return nmenu->is_item_hidden(_get_rid_from_name(nmenu, p_menu_root), p_idx); } String DisplayServer::global_menu_get_item_tooltip(const String &p_menu_root, int p_idx) const { - WARN_PRINT("Global menus not supported by this display server."); - return String(); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, String()); + return nmenu->get_item_tooltip(_get_rid_from_name(nmenu, p_menu_root), p_idx); } int DisplayServer::global_menu_get_item_state(const String &p_menu_root, int p_idx) const { - WARN_PRINT("Global menus not supported by this display server."); - return -1; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, -1); + return nmenu->get_item_state(_get_rid_from_name(nmenu, p_menu_root), p_idx); } int DisplayServer::global_menu_get_item_max_states(const String &p_menu_root, int p_idx) const { - WARN_PRINT("Global menus not supported by this display server."); - return -1; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, -1); + return nmenu->get_item_max_states(_get_rid_from_name(nmenu, p_menu_root), p_idx); } Ref DisplayServer::global_menu_get_item_icon(const String &p_menu_root, int p_idx) const { - WARN_PRINT("Global menus not supported by this display server."); - return Ref(); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, Ref()); + return nmenu->get_item_icon(_get_rid_from_name(nmenu, p_menu_root), p_idx); } int DisplayServer::global_menu_get_item_indentation_level(const String &p_menu_root, int p_idx) const { - WARN_PRINT("Global menus not supported by this display server."); - return 0; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, 0); + return nmenu->get_item_indentation_level(_get_rid_from_name(nmenu, p_menu_root), p_idx); } void DisplayServer::global_menu_set_item_checked(const String &p_menu_root, int p_idx, bool p_checked) { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + nmenu->set_item_checked(_get_rid_from_name(nmenu, p_menu_root), p_idx, p_checked); } void DisplayServer::global_menu_set_item_checkable(const String &p_menu_root, int p_idx, bool p_checkable) { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + nmenu->set_item_checkable(_get_rid_from_name(nmenu, p_menu_root), p_idx, p_checkable); } void DisplayServer::global_menu_set_item_radio_checkable(const String &p_menu_root, int p_idx, bool p_checkable) { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + nmenu->set_item_radio_checkable(_get_rid_from_name(nmenu, p_menu_root), p_idx, p_checkable); } void DisplayServer::global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag) { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + nmenu->set_item_tag(_get_rid_from_name(nmenu, p_menu_root), p_idx, p_tag); } void DisplayServer::global_menu_set_item_text(const String &p_menu_root, int p_idx, const String &p_text) { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + nmenu->set_item_text(_get_rid_from_name(nmenu, p_menu_root), p_idx, p_text); } void DisplayServer::global_menu_set_item_submenu(const String &p_menu_root, int p_idx, const String &p_submenu) { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + nmenu->set_item_submenu(_get_rid_from_name(nmenu, p_menu_root), p_idx, _get_rid_from_name(nmenu, p_submenu)); } void DisplayServer::global_menu_set_item_accelerator(const String &p_menu_root, int p_idx, Key p_keycode) { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + nmenu->set_item_accelerator(_get_rid_from_name(nmenu, p_menu_root), p_idx, p_keycode); } void DisplayServer::global_menu_set_item_disabled(const String &p_menu_root, int p_idx, bool p_disabled) { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + nmenu->set_item_disabled(_get_rid_from_name(nmenu, p_menu_root), p_idx, p_disabled); } void DisplayServer::global_menu_set_item_hidden(const String &p_menu_root, int p_idx, bool p_hidden) { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + nmenu->set_item_hidden(_get_rid_from_name(nmenu, p_menu_root), p_idx, p_hidden); } void DisplayServer::global_menu_set_item_tooltip(const String &p_menu_root, int p_idx, const String &p_tooltip) { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + nmenu->set_item_tooltip(_get_rid_from_name(nmenu, p_menu_root), p_idx, p_tooltip); } void DisplayServer::global_menu_set_item_state(const String &p_menu_root, int p_idx, int p_state) { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + nmenu->set_item_state(_get_rid_from_name(nmenu, p_menu_root), p_idx, p_state); } void DisplayServer::global_menu_set_item_max_states(const String &p_menu_root, int p_idx, int p_max_states) { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + nmenu->set_item_max_states(_get_rid_from_name(nmenu, p_menu_root), p_idx, p_max_states); } void DisplayServer::global_menu_set_item_icon(const String &p_menu_root, int p_idx, const Ref &p_icon) { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + nmenu->set_item_icon(_get_rid_from_name(nmenu, p_menu_root), p_idx, p_icon); } void DisplayServer::global_menu_set_item_indentation_level(const String &p_menu_root, int p_idx, int p_level) { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + nmenu->set_item_indentation_level(_get_rid_from_name(nmenu, p_menu_root), p_idx, p_level); } int DisplayServer::global_menu_get_item_count(const String &p_menu_root) const { - WARN_PRINT("Global menus not supported by this display server."); - return 0; + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, 0); + return nmenu->get_item_count(_get_rid_from_name(nmenu, p_menu_root)); } void DisplayServer::global_menu_remove_item(const String &p_menu_root, int p_idx) { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + nmenu->remove_item(_get_rid_from_name(nmenu, p_menu_root), p_idx); } void DisplayServer::global_menu_clear(const String &p_menu_root) { - WARN_PRINT("Global menus not supported by this display server."); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL(nmenu); + RID rid = _get_rid_from_name(nmenu, p_menu_root); + nmenu->clear(rid); + if (!nmenu->is_system_menu(rid)) { + nmenu->free_menu(rid); + menu_names.erase(p_menu_root); + } } Dictionary DisplayServer::global_menu_get_system_menu_roots() const { - WARN_PRINT("Global menus not supported by this display server."); - return Dictionary(); + NativeMenu *nmenu = NativeMenu::get_singleton(); + ERR_FAIL_NULL_V(nmenu, Dictionary()); + + Dictionary out; + if (nmenu->has_system_menu(NativeMenu::DOCK_MENU_ID)) { + out["_dock"] = "@Dock"; + } + if (nmenu->has_system_menu(NativeMenu::APPLICATION_MENU_ID)) { + out["_apple"] = "@Apple"; + } + if (nmenu->has_system_menu(NativeMenu::WINDOW_MENU_ID)) { + out["_window"] = "Window"; + } + if (nmenu->has_system_menu(NativeMenu::HELP_MENU_ID)) { + out["_help"] = "Help"; + } + return out; } +#endif + bool DisplayServer::tts_is_speaking() const { WARN_PRINT("TTS is not supported by this display server."); return false; @@ -640,6 +761,7 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("help_set_search_callbacks", "search_callback", "action_callback"), &DisplayServer::help_set_search_callbacks); +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("global_menu_set_popup_callbacks", "menu_root", "open_callback", "close_callback"), &DisplayServer::global_menu_set_popup_callbacks); ClassDB::bind_method(D_METHOD("global_menu_add_submenu_item", "menu_root", "label", "submenu", "index"), &DisplayServer::global_menu_add_submenu_item, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("global_menu_add_item", "menu_root", "label", "callback", "key_callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); @@ -695,6 +817,7 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("global_menu_clear", "menu_root"), &DisplayServer::global_menu_clear); ClassDB::bind_method(D_METHOD("global_menu_get_system_menu_roots"), &DisplayServer::global_menu_get_system_menu_roots); +#endif ClassDB::bind_method(D_METHOD("tts_is_speaking"), &DisplayServer::tts_is_speaking); ClassDB::bind_method(D_METHOD("tts_is_paused"), &DisplayServer::tts_is_paused); @@ -866,7 +989,9 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("tablet_get_current_driver"), &DisplayServer::tablet_get_current_driver); ClassDB::bind_method(D_METHOD("tablet_set_current_driver", "name"), &DisplayServer::tablet_set_current_driver); +#ifndef DISABLE_DEPRECATED BIND_ENUM_CONSTANT(FEATURE_GLOBAL_MENU); +#endif BIND_ENUM_CONSTANT(FEATURE_SUBWINDOWS); BIND_ENUM_CONSTANT(FEATURE_TOUCHSCREEN); BIND_ENUM_CONSTANT(FEATURE_MOUSE); diff --git a/servers/display_server.h b/servers/display_server.h index 7608d76b304..85b791ca690 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -36,6 +36,8 @@ #include "core/os/os.h" #include "core/variant/callable.h" +#include "native_menu.h" + class Texture2D; class Image; @@ -45,6 +47,12 @@ class DisplayServer : public Object { static DisplayServer *singleton; static bool hidpi_allowed; +#ifndef DISABLE_DEPRECATED + mutable HashMap menu_names; + + RID _get_rid_from_name(NativeMenu *p_nmenu, const String &p_menu_root) const; +#endif + public: _FORCE_INLINE_ static DisplayServer *get_singleton() { return singleton; @@ -106,7 +114,9 @@ protected: public: enum Feature { +#ifndef DISABLE_DEPRECATED FEATURE_GLOBAL_MENU, +#endif FEATURE_SUBWINDOWS, FEATURE_TOUCHSCREEN, FEATURE_MOUSE, @@ -137,6 +147,7 @@ public: virtual void help_set_search_callbacks(const Callable &p_search_callback = Callable(), const Callable &p_action_callback = Callable()); +#ifndef DISABLE_DEPRECATED virtual void global_menu_set_popup_callbacks(const String &p_menu_root, const Callable &p_open_callback = Callable(), const Callable &p_close_callback = Callable()); virtual int global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index = -1); @@ -193,6 +204,7 @@ public: virtual void global_menu_clear(const String &p_menu_root); virtual Dictionary global_menu_get_system_menu_roots() const; +#endif struct TTSUtterance { String text; diff --git a/servers/native_menu.cpp b/servers/native_menu.cpp new file mode 100644 index 00000000000..4372c107077 --- /dev/null +++ b/servers/native_menu.cpp @@ -0,0 +1,422 @@ +/**************************************************************************/ +/* native_menu.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "native_menu.h" + +#include "scene/resources/image_texture.h" + +NativeMenu *NativeMenu::singleton = nullptr; + +void NativeMenu::_bind_methods() { + ClassDB::bind_method(D_METHOD("has_feature", "feature"), &NativeMenu::has_feature); + + ClassDB::bind_method(D_METHOD("has_system_menu", "menu_id"), &NativeMenu::has_system_menu); + 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("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); + + ClassDB::bind_method(D_METHOD("get_size", "rid"), &NativeMenu::get_size); + ClassDB::bind_method(D_METHOD("popup", "rid", "position"), &NativeMenu::popup); + + ClassDB::bind_method(D_METHOD("set_popup_open_callback", "rid", "callback"), &NativeMenu::set_popup_open_callback); + ClassDB::bind_method(D_METHOD("get_popup_open_callback", "rid"), &NativeMenu::get_popup_open_callback); + ClassDB::bind_method(D_METHOD("set_popup_close_callback", "rid", "callback"), &NativeMenu::set_popup_close_callback); + ClassDB::bind_method(D_METHOD("get_popup_close_callback", "rid"), &NativeMenu::get_popup_close_callback); + ClassDB::bind_method(D_METHOD("set_minimum_width", "rid", "width"), &NativeMenu::set_minimum_width); + ClassDB::bind_method(D_METHOD("get_minimum_width", "rid"), &NativeMenu::get_minimum_width); + + ClassDB::bind_method(D_METHOD("add_submenu_item", "rid", "label", "submenu_rid", "tag", "index"), &NativeMenu::add_submenu_item, DEFVAL(Variant()), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("add_item", "rid", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("add_check_item", "rid", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("add_icon_item", "rid", "icon", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_icon_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("add_icon_check_item", "rid", "icon", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_icon_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("add_radio_check_item", "rid", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_radio_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("add_icon_radio_check_item", "rid", "icon", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_icon_radio_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("add_multistate_item", "rid", "label", "max_states", "default_state", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_multistate_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("add_separator", "rid", "index"), &NativeMenu::add_separator, DEFVAL(-1)); + + ClassDB::bind_method(D_METHOD("find_item_index_with_text", "rid", "text"), &NativeMenu::find_item_index_with_text); + ClassDB::bind_method(D_METHOD("find_item_index_with_tag", "rid", "tag"), &NativeMenu::find_item_index_with_tag); + + ClassDB::bind_method(D_METHOD("is_item_checked", "rid", "idx"), &NativeMenu::is_item_checked); + ClassDB::bind_method(D_METHOD("is_item_checkable", "rid", "idx"), &NativeMenu::is_item_checkable); + ClassDB::bind_method(D_METHOD("is_item_radio_checkable", "rid", "idx"), &NativeMenu::is_item_radio_checkable); + ClassDB::bind_method(D_METHOD("get_item_callback", "rid", "idx"), &NativeMenu::get_item_callback); + ClassDB::bind_method(D_METHOD("get_item_key_callback", "rid", "idx"), &NativeMenu::get_item_key_callback); + ClassDB::bind_method(D_METHOD("get_item_tag", "rid", "idx"), &NativeMenu::get_item_tag); + ClassDB::bind_method(D_METHOD("get_item_text", "rid", "idx"), &NativeMenu::get_item_text); + ClassDB::bind_method(D_METHOD("get_item_submenu", "rid", "idx"), &NativeMenu::get_item_submenu); + ClassDB::bind_method(D_METHOD("get_item_accelerator", "rid", "idx"), &NativeMenu::get_item_accelerator); + ClassDB::bind_method(D_METHOD("is_item_disabled", "rid", "idx"), &NativeMenu::is_item_disabled); + ClassDB::bind_method(D_METHOD("is_item_hidden", "rid", "idx"), &NativeMenu::is_item_hidden); + ClassDB::bind_method(D_METHOD("get_item_tooltip", "rid", "idx"), &NativeMenu::get_item_tooltip); + ClassDB::bind_method(D_METHOD("get_item_state", "rid", "idx"), &NativeMenu::get_item_state); + ClassDB::bind_method(D_METHOD("get_item_max_states", "rid", "idx"), &NativeMenu::get_item_max_states); + ClassDB::bind_method(D_METHOD("get_item_icon", "rid", "idx"), &NativeMenu::get_item_icon); + ClassDB::bind_method(D_METHOD("get_item_indentation_level", "rid", "idx"), &NativeMenu::get_item_indentation_level); + + ClassDB::bind_method(D_METHOD("set_item_checked", "rid", "idx", "checked"), &NativeMenu::set_item_checked); + ClassDB::bind_method(D_METHOD("set_item_checkable", "rid", "idx", "checkable"), &NativeMenu::set_item_checkable); + ClassDB::bind_method(D_METHOD("set_item_radio_checkable", "rid", "idx", "checkable"), &NativeMenu::set_item_radio_checkable); + ClassDB::bind_method(D_METHOD("set_item_callback", "rid", "idx", "callback"), &NativeMenu::set_item_callback); + ClassDB::bind_method(D_METHOD("set_item_hover_callbacks", "rid", "idx", "callback"), &NativeMenu::set_item_hover_callbacks); + ClassDB::bind_method(D_METHOD("set_item_key_callback", "rid", "idx", "key_callback"), &NativeMenu::set_item_key_callback); + ClassDB::bind_method(D_METHOD("set_item_tag", "rid", "idx", "tag"), &NativeMenu::set_item_tag); + ClassDB::bind_method(D_METHOD("set_item_text", "rid", "idx", "text"), &NativeMenu::set_item_text); + ClassDB::bind_method(D_METHOD("set_item_submenu", "rid", "idx", "submenu_rid"), &NativeMenu::set_item_submenu); + ClassDB::bind_method(D_METHOD("set_item_accelerator", "rid", "idx", "keycode"), &NativeMenu::set_item_accelerator); + ClassDB::bind_method(D_METHOD("set_item_disabled", "rid", "idx", "disabled"), &NativeMenu::set_item_disabled); + ClassDB::bind_method(D_METHOD("set_item_hidden", "rid", "idx", "hidden"), &NativeMenu::set_item_hidden); + ClassDB::bind_method(D_METHOD("set_item_tooltip", "rid", "idx", "tooltip"), &NativeMenu::set_item_tooltip); + ClassDB::bind_method(D_METHOD("set_item_state", "rid", "idx", "state"), &NativeMenu::set_item_state); + ClassDB::bind_method(D_METHOD("set_item_max_states", "rid", "idx", "max_states"), &NativeMenu::set_item_max_states); + ClassDB::bind_method(D_METHOD("set_item_icon", "rid", "idx", "icon"), &NativeMenu::set_item_icon); + ClassDB::bind_method(D_METHOD("set_item_indentation_level", "rid", "idx", "level"), &NativeMenu::set_item_indentation_level); + + ClassDB::bind_method(D_METHOD("get_item_count", "rid"), &NativeMenu::get_item_count); + ClassDB::bind_method(D_METHOD("is_system_menu", "rid"), &NativeMenu::is_system_menu); + + ClassDB::bind_method(D_METHOD("remove_item", "rid", "idx"), &NativeMenu::remove_item); + ClassDB::bind_method(D_METHOD("clear", "rid"), &NativeMenu::clear); + + BIND_ENUM_CONSTANT(FEATURE_GLOBAL_MENU); + BIND_ENUM_CONSTANT(FEATURE_POPUP_MENU); + + BIND_ENUM_CONSTANT(INVALID_MENU_ID); + BIND_ENUM_CONSTANT(MAIN_MENU_ID); + BIND_ENUM_CONSTANT(APPLICATION_MENU_ID); + BIND_ENUM_CONSTANT(WINDOW_MENU_ID); + BIND_ENUM_CONSTANT(HELP_MENU_ID); + BIND_ENUM_CONSTANT(DOCK_MENU_ID); +} + +bool NativeMenu::has_feature(Feature p_feature) const { + return false; +} + +bool NativeMenu::has_system_menu(SystemMenus p_menu_id) const { + return false; +} + +RID NativeMenu::get_system_menu(SystemMenus p_menu_id) const { + WARN_PRINT("Global menus are not supported on this platform."); + return RID(); +} + +String NativeMenu::get_system_menu_name(SystemMenus p_menu_id) const { + switch (p_menu_id) { + case MAIN_MENU_ID: + return "Main menu"; + case APPLICATION_MENU_ID: + return "Application menu"; + case WINDOW_MENU_ID: + return "Window menu"; + case HELP_MENU_ID: + return "Help menu"; + case DOCK_MENU_ID: + return "Dock menu"; + default: + return "Invalid"; + } +} + +RID NativeMenu::create_menu() { + WARN_PRINT("Global menus are not supported on this platform."); + return RID(); +} + +bool NativeMenu::has_menu(const RID &p_rid) const { + WARN_PRINT("Global menus are not supported on this platform."); + return false; +} + +void NativeMenu::free_menu(const RID &p_rid) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +Size2 NativeMenu::get_size(const RID &p_rid) const { + WARN_PRINT("Global menus are not supported on this platform."); + return Size2(); +} + +void NativeMenu::popup(const RID &p_rid, const Vector2i &p_position) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +void NativeMenu::set_popup_open_callback(const RID &p_rid, const Callable &p_callback) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +Callable NativeMenu::get_popup_open_callback(const RID &p_rid) const { + WARN_PRINT("Global menus are not supported on this platform."); + return Callable(); +} + +void NativeMenu::set_popup_close_callback(const RID &p_rid, const Callable &p_callback) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +Callable NativeMenu::get_popup_close_callback(const RID &p_rid) const { + WARN_PRINT("Global menus are not supported on this platform."); + return Callable(); +} + +void NativeMenu::set_minimum_width(const RID &p_rid, float p_width) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +float NativeMenu::get_minimum_width(const RID &p_rid) const { + WARN_PRINT("Global menus are not supported on this platform."); + return 0.f; +} + +int NativeMenu::add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag, int p_index) { + WARN_PRINT("Global menus are not supported on this platform."); + return -1; +} + +int NativeMenu::add_item(const RID &p_rid, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { + WARN_PRINT("Global menus are not supported on this platform."); + return -1; +} + +int NativeMenu::add_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { + WARN_PRINT("Global menus are not supported on this platform."); + return -1; +} + +int NativeMenu::add_icon_item(const RID &p_rid, const Ref &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { + WARN_PRINT("Global menus are not supported on this platform."); + return -1; +} + +int NativeMenu::add_icon_check_item(const RID &p_rid, const Ref &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { + WARN_PRINT("Global menus are not supported on this platform."); + return -1; +} + +int NativeMenu::add_radio_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { + WARN_PRINT("Global menus are not supported on this platform."); + return -1; +} + +int NativeMenu::add_icon_radio_check_item(const RID &p_rid, const Ref &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { + WARN_PRINT("Global menus are not supported on this platform."); + return -1; +} + +int NativeMenu::add_multistate_item(const RID &p_rid, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) { + WARN_PRINT("Global menus are not supported on this platform."); + return -1; +} + +int NativeMenu::add_separator(const RID &p_rid, int p_index) { + WARN_PRINT("Global menus are not supported on this platform."); + return -1; +} + +int NativeMenu::find_item_index_with_text(const RID &p_rid, const String &p_text) const { + WARN_PRINT("Global menus are not supported on this platform."); + return -1; +} + +int NativeMenu::find_item_index_with_tag(const RID &p_rid, const Variant &p_tag) const { + WARN_PRINT("Global menus are not supported on this platform."); + return -1; +} + +bool NativeMenu::is_item_checked(const RID &p_rid, int p_idx) const { + WARN_PRINT("Global menus are not supported on this platform."); + return false; +} + +bool NativeMenu::is_item_checkable(const RID &p_rid, int p_idx) const { + WARN_PRINT("Global menus are not supported on this platform."); + return false; +} + +bool NativeMenu::is_item_radio_checkable(const RID &p_rid, int p_idx) const { + WARN_PRINT("Global menus are not supported on this platform."); + return false; +} + +Callable NativeMenu::get_item_callback(const RID &p_rid, int p_idx) const { + WARN_PRINT("Global menus are not supported on this platform."); + return Callable(); +} + +Callable NativeMenu::get_item_key_callback(const RID &p_rid, int p_idx) const { + WARN_PRINT("Global menus are not supported on this platform."); + return Callable(); +} + +Variant NativeMenu::get_item_tag(const RID &p_rid, int p_idx) const { + WARN_PRINT("Global menus are not supported on this platform."); + return Variant(); +} + +String NativeMenu::get_item_text(const RID &p_rid, int p_idx) const { + WARN_PRINT("Global menus are not supported on this platform."); + return String(); +} + +RID NativeMenu::get_item_submenu(const RID &p_rid, int p_idx) const { + WARN_PRINT("Global menus are not supported on this platform."); + return RID(); +} + +Key NativeMenu::get_item_accelerator(const RID &p_rid, int p_idx) const { + WARN_PRINT("Global menus are not supported on this platform."); + return Key::NONE; +} + +bool NativeMenu::is_item_disabled(const RID &p_rid, int p_idx) const { + WARN_PRINT("Global menus are not supported on this platform."); + return false; +} + +bool NativeMenu::is_item_hidden(const RID &p_rid, int p_idx) const { + WARN_PRINT("Global menus are not supported on this platform."); + return false; +} + +String NativeMenu::get_item_tooltip(const RID &p_rid, int p_idx) const { + WARN_PRINT("Global menus are not supported on this platform."); + return String(); +} + +int NativeMenu::get_item_state(const RID &p_rid, int p_idx) const { + WARN_PRINT("Global menus are not supported on this platform."); + return -1; +} + +int NativeMenu::get_item_max_states(const RID &p_rid, int p_idx) const { + WARN_PRINT("Global menus are not supported on this platform."); + return -1; +} + +Ref NativeMenu::get_item_icon(const RID &p_rid, int p_idx) const { + WARN_PRINT("Global menus are not supported on this platform."); + return Ref(); +} + +int NativeMenu::get_item_indentation_level(const RID &p_rid, int p_idx) const { + WARN_PRINT("Global menus are not supported on this platform."); + return 0; +} + +void NativeMenu::set_item_checked(const RID &p_rid, int p_idx, bool p_checked) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +void NativeMenu::set_item_checkable(const RID &p_rid, int p_idx, bool p_checkable) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +void NativeMenu::set_item_radio_checkable(const RID &p_rid, int p_idx, bool p_checkable) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +void NativeMenu::set_item_callback(const RID &p_rid, int p_idx, const Callable &p_callback) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +void NativeMenu::set_item_key_callback(const RID &p_rid, int p_idx, const Callable &p_key_callback) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +void NativeMenu::set_item_hover_callbacks(const RID &p_rid, int p_idx, const Callable &p_callback) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +void NativeMenu::set_item_tag(const RID &p_rid, int p_idx, const Variant &p_tag) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +void NativeMenu::set_item_text(const RID &p_rid, int p_idx, const String &p_text) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +void NativeMenu::set_item_submenu(const RID &p_rid, int p_idx, const RID &p_submenu_rid) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +void NativeMenu::set_item_accelerator(const RID &p_rid, int p_idx, Key p_keycode) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +void NativeMenu::set_item_disabled(const RID &p_rid, int p_idx, bool p_disabled) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +void NativeMenu::set_item_hidden(const RID &p_rid, int p_idx, bool p_hidden) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +void NativeMenu::set_item_tooltip(const RID &p_rid, int p_idx, const String &p_tooltip) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +void NativeMenu::set_item_state(const RID &p_rid, int p_idx, int p_state) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +void NativeMenu::set_item_max_states(const RID &p_rid, int p_idx, int p_max_states) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +void NativeMenu::set_item_icon(const RID &p_rid, int p_idx, const Ref &p_icon) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +void NativeMenu::set_item_indentation_level(const RID &p_rid, int p_idx, int p_level) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +int NativeMenu::get_item_count(const RID &p_rid) const { + WARN_PRINT("Global menus are not supported on this platform."); + return 0; +} + +bool NativeMenu::is_system_menu(const RID &p_rid) const { + WARN_PRINT("Global menus are not supported on this platform."); + return false; +} + +void NativeMenu::remove_item(const RID &p_rid, int p_idx) { + WARN_PRINT("Global menus are not supported on this platform."); +} + +void NativeMenu::clear(const RID &p_rid) { + WARN_PRINT("Global menus are not supported on this platform."); +} diff --git a/servers/native_menu.h b/servers/native_menu.h new file mode 100644 index 00000000000..bb61caa633e --- /dev/null +++ b/servers/native_menu.h @@ -0,0 +1,154 @@ +/**************************************************************************/ +/* native_menu.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef NATIVE_MENU_H +#define NATIVE_MENU_H + +#include "core/input/input.h" +#include "core/io/resource.h" +#include "core/os/os.h" +#include "core/variant/callable.h" + +class Texture2D; + +class NativeMenu : public Object { + GDCLASS(NativeMenu, Object) + + static NativeMenu *singleton; + +protected: + static void _bind_methods(); + +public: + _FORCE_INLINE_ static NativeMenu *get_singleton() { + return singleton; + } + + enum Feature { + FEATURE_GLOBAL_MENU, + FEATURE_POPUP_MENU, + }; + + enum SystemMenus { + INVALID_MENU_ID, + MAIN_MENU_ID, + APPLICATION_MENU_ID, + WINDOW_MENU_ID, + HELP_MENU_ID, + DOCK_MENU_ID, + }; + + virtual bool has_feature(Feature p_feature) const; + + virtual bool has_system_menu(SystemMenus p_menu_id) const; + virtual RID get_system_menu(SystemMenus p_menu_id) const; + virtual String get_system_menu_name(SystemMenus p_menu_id) const; + + virtual RID create_menu(); + virtual bool has_menu(const RID &p_rid) const; + virtual void free_menu(const RID &p_rid); + + virtual Size2 get_size(const RID &p_rid) const; + virtual void popup(const RID &p_rid, const Vector2i &p_position); + + virtual void set_popup_open_callback(const RID &p_rid, const Callable &p_callback); + virtual Callable get_popup_open_callback(const RID &p_rid) const; + virtual void set_popup_close_callback(const RID &p_rid, const Callable &p_callback); + virtual Callable get_popup_close_callback(const RID &p_rid) const; + virtual void set_minimum_width(const RID &p_rid, float p_width); + virtual float get_minimum_width(const RID &p_rid) const; + + virtual int add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag = Variant(), int p_index = -1); + virtual int add_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); + virtual int add_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); + virtual int add_icon_item(const RID &p_rid, const Ref &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); + virtual int add_icon_check_item(const RID &p_rid, const Ref &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); + virtual int add_radio_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); + virtual int add_icon_radio_check_item(const RID &p_rid, const Ref &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); + virtual int add_multistate_item(const RID &p_rid, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1); + virtual int add_separator(const RID &p_rid, int p_index = -1); + + virtual int find_item_index_with_text(const RID &p_rid, const String &p_text) const; + virtual int find_item_index_with_tag(const RID &p_rid, const Variant &p_tag) const; + + virtual bool is_item_checked(const RID &p_rid, int p_idx) const; + virtual bool is_item_checkable(const RID &p_rid, int p_idx) const; + virtual bool is_item_radio_checkable(const RID &p_rid, int p_idx) const; + virtual Callable get_item_callback(const RID &p_rid, int p_idx) const; + virtual Callable get_item_key_callback(const RID &p_rid, int p_idx) const; + virtual Variant get_item_tag(const RID &p_rid, int p_idx) const; + virtual String get_item_text(const RID &p_rid, int p_idx) const; + virtual RID get_item_submenu(const RID &p_rid, int p_idx) const; + virtual Key get_item_accelerator(const RID &p_rid, int p_idx) const; + virtual bool is_item_disabled(const RID &p_rid, int p_idx) const; + virtual bool is_item_hidden(const RID &p_rid, int p_idx) const; + virtual String get_item_tooltip(const RID &p_rid, int p_idx) const; + virtual int get_item_state(const RID &p_rid, int p_idx) const; + virtual int get_item_max_states(const RID &p_rid, int p_idx) const; + virtual Ref get_item_icon(const RID &p_rid, int p_idx) const; + virtual int get_item_indentation_level(const RID &p_rid, int p_idx) const; + + virtual void set_item_checked(const RID &p_rid, int p_idx, bool p_checked); + virtual void set_item_checkable(const RID &p_rid, int p_idx, bool p_checkable); + virtual void set_item_radio_checkable(const RID &p_rid, int p_idx, bool p_checkable); + virtual void set_item_callback(const RID &p_rid, int p_idx, const Callable &p_callback); + virtual void set_item_key_callback(const RID &p_rid, int p_idx, const Callable &p_key_callback); + virtual void set_item_hover_callbacks(const RID &p_rid, int p_idx, const Callable &p_callback); + virtual void set_item_tag(const RID &p_rid, int p_idx, const Variant &p_tag); + virtual void set_item_text(const RID &p_rid, int p_idx, const String &p_text); + virtual void set_item_submenu(const RID &p_rid, int p_idx, const RID &p_submenu_rid); + virtual void set_item_accelerator(const RID &p_rid, int p_idx, Key p_keycode); + virtual void set_item_disabled(const RID &p_rid, int p_idx, bool p_disabled); + virtual void set_item_hidden(const RID &p_rid, int p_idx, bool p_hidden); + virtual void set_item_tooltip(const RID &p_rid, int p_idx, const String &p_tooltip); + virtual void set_item_state(const RID &p_rid, int p_idx, int p_state); + virtual void set_item_max_states(const RID &p_rid, int p_idx, int p_max_states); + virtual void set_item_icon(const RID &p_rid, int p_idx, const Ref &p_icon); + virtual void set_item_indentation_level(const RID &p_rid, int p_idx, int p_level); + + virtual int get_item_count(const RID &p_rid) const; + virtual bool is_system_menu(const RID &p_rid) const; + + virtual void remove_item(const RID &p_rid, int p_idx); + virtual void clear(const RID &p_rid); + + NativeMenu() { + singleton = this; + } + + ~NativeMenu() { + singleton = nullptr; + } +}; + +VARIANT_ENUM_CAST(NativeMenu::Feature); +VARIANT_ENUM_CAST(NativeMenu::SystemMenus); + +#endif // NATIVE_MENU_H diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index ee2a65ad913..8547ebfad6e 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -60,6 +60,7 @@ #include "movie_writer/movie_writer.h" #include "movie_writer/movie_writer_mjpeg.h" #include "movie_writer/movie_writer_pngwav.h" +#include "native_menu.h" #include "rendering/renderer_compositor.h" #include "rendering/renderer_rd/framebuffer_cache_rd.h" #include "rendering/renderer_rd/storage_rd/render_data_rd.h" @@ -162,6 +163,8 @@ void register_server_types() { GDREGISTER_ABSTRACT_CLASS(RenderingServer); GDREGISTER_CLASS(AudioServer); + GDREGISTER_CLASS(NativeMenu); + GDREGISTER_ABSTRACT_CLASS(NavigationServer2D); GDREGISTER_ABSTRACT_CLASS(NavigationServer3D); GDREGISTER_CLASS(NavigationPathQueryParameters2D); @@ -361,6 +364,7 @@ void register_server_singletons() { #ifndef _3D_DISABLED Engine::get_singleton()->add_singleton(Engine::Singleton("PhysicsServer3D", PhysicsServer3D::get_singleton(), "PhysicsServer3D")); #endif // _3D_DISABLED + Engine::get_singleton()->add_singleton(Engine::Singleton("NativeMenu", NativeMenu::get_singleton(), "NativeMenu")); Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer2D", NavigationServer2D::get_singleton(), "NavigationServer2D")); Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer3D", NavigationServer3D::get_singleton(), "NavigationServer3D")); Engine::get_singleton()->add_singleton(Engine::Singleton("XRServer", XRServer::get_singleton(), "XRServer"));