Prechádzať zdrojové kódy

Merge pull request #59410 from bruvzg/mac_menu_features

Rémi Verschelde 3 rokov pred
rodič
commit
7538ad81ac

+ 325 - 4
doc/classes/DisplayServer.xml

@@ -136,7 +136,74 @@
 			<argument index="1" name="label" type="String" />
 			<argument index="2" name="callback" type="Callable" />
 			<argument index="3" name="tag" type="Variant" default="null" />
+			<argument index="4" name="accelerator" type="int" enum="Key" default="0" />
+			<argument index="5" name="index" type="int" default="-1" />
 			<description>
+				Adds a new checkable item with text [code]label[/code] to the global menu with ID [code]menu_root[/code].
+				[b]Note:[/b] This method is implemented on macOS.
+				[b]Supported system menu IDs:[/b]
+				[codeblock]
+				"" - Main menu (macOS).
+				"_dock" - Dock popup menu (macOS).
+				[/codeblock]
+			</description>
+		</method>
+		<method name="global_menu_add_icon_check_item">
+			<return type="void" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="icon" type="Texture2D" />
+			<argument index="2" name="label" type="String" />
+			<argument index="3" name="callback" type="Callable" />
+			<argument index="4" name="tag" type="Variant" default="null" />
+			<argument index="5" name="accelerator" type="int" enum="Key" default="0" />
+			<argument index="6" name="index" type="int" default="-1" />
+			<description>
+				Adds a new checkable item with text [code]label[/code] and icon [code]icon[/code] to the global menu with ID [code]menu_root[/code].
+				[b]Note:[/b] This method is implemented on macOS.
+				[b]Supported system menu IDs:[/b]
+				[codeblock]
+				"" - Main menu (macOS).
+				"_dock" - Dock popup menu (macOS).
+				[/codeblock]
+			</description>
+		</method>
+		<method name="global_menu_add_icon_item">
+			<return type="void" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="icon" type="Texture2D" />
+			<argument index="2" name="label" type="String" />
+			<argument index="3" name="callback" type="Callable" />
+			<argument index="4" name="tag" type="Variant" default="null" />
+			<argument index="5" name="accelerator" type="int" enum="Key" default="0" />
+			<argument index="6" name="index" type="int" default="-1" />
+			<description>
+				Adds a new item with text [code]label[/code] and icon [code]icon[/code] to the global menu with ID [code]menu_root[/code].
+				[b]Note:[/b] This method is implemented on macOS.
+				[b]Supported system menu IDs:[/b]
+				[codeblock]
+				"" - Main menu (macOS).
+				"_dock" - Dock popup menu (macOS).
+				[/codeblock]
+			</description>
+		</method>
+		<method name="global_menu_add_icon_radio_check_item">
+			<return type="void" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="icon" type="Texture2D" />
+			<argument index="2" name="label" type="String" />
+			<argument index="3" name="callback" type="Callable" />
+			<argument index="4" name="tag" type="Variant" default="null" />
+			<argument index="5" name="accelerator" type="int" enum="Key" default="0" />
+			<argument index="6" name="index" type="int" default="-1" />
+			<description>
+				Adds a new radio-checkable item with text [code]label[/code] and icon [code]icon[/code] to the global menu with ID [code]menu_root[/code].
+				[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 global_menu_set_item_checked] for more info on how to control it.
+				[b]Note:[/b] This method is implemented on macOS.
+				[b]Supported system menu IDs:[/b]
+				[codeblock]
+				"" - Main menu (macOS).
+				"_dock" - Dock popup menu (macOS).
+				[/codeblock]
 			</description>
 		</method>
 		<method name="global_menu_add_item">
@@ -145,13 +212,70 @@
 			<argument index="1" name="label" type="String" />
 			<argument index="2" name="callback" type="Callable" />
 			<argument index="3" name="tag" type="Variant" default="null" />
+			<argument index="4" name="accelerator" type="int" enum="Key" default="0" />
+			<argument index="5" name="index" type="int" default="-1" />
+			<description>
+				Adds a new item with text [code]label[/code] to the global menu with ID [code]menu_root[/code].
+				[b]Note:[/b] This method is implemented on macOS.
+				[b]Supported system menu IDs:[/b]
+				[codeblock]
+				"" - Main menu (macOS).
+				"_dock" - Dock popup menu (macOS).
+				[/codeblock]
+			</description>
+		</method>
+		<method name="global_menu_add_multistate_item">
+			<return type="void" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="labe" type="String" />
+			<argument index="2" name="max_states" type="int" />
+			<argument index="3" name="default_state" type="int" />
+			<argument index="4" name="callback" type="Callable" />
+			<argument index="5" name="tag" type="Variant" default="null" />
+			<argument index="6" name="accelerator" type="int" enum="Key" default="0" />
+			<argument index="7" name="index" type="int" default="-1" />
+			<description>
+				Adds a new item with text [code]label[/code] to the global menu with ID [code]menu_root[/code].
+				Contrarily to normal binary items, multistate items can have more than two states, as defined by [code]max_states[/code]. Each press or activate of the item will increase the state by one. The default value is defined by [code]default_state[/code].
+				[b]Note:[/b] This method is implemented on macOS.
+				[b]Supported system menu IDs:[/b]
+				[codeblock]
+				"" - Main menu (macOS).
+				"_dock" - Dock popup menu (macOS).
+				[/codeblock]
+			</description>
+		</method>
+		<method name="global_menu_add_radio_check_item">
+			<return type="void" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="label" type="String" />
+			<argument index="2" name="callback" type="Callable" />
+			<argument index="3" name="tag" type="Variant" default="null" />
+			<argument index="4" name="accelerator" type="int" enum="Key" default="0" />
+			<argument index="5" name="index" type="int" default="-1" />
 			<description>
+				Adds a new radio-checkable item with text [code]label[/code] to the global menu with ID [code]menu_root[/code].
+				[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 global_menu_set_item_checked] for more info on how to control it.
+				[b]Note:[/b] This method is implemented on macOS.
+				[b]Supported system menu IDs:[/b]
+				[codeblock]
+				"" - Main menu (macOS).
+				"_dock" - Dock popup menu (macOS).
+				[/codeblock]
 			</description>
 		</method>
 		<method name="global_menu_add_separator">
 			<return type="void" />
 			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="index" type="int" default="-1" />
 			<description>
+				Adds a separator between items to the global menu with ID [code]menu_root[/code]. Separators also occupy an index.
+				[b]Note:[/b] This method is implemented on macOS.
+				[b]Supported system menu IDs:[/b]
+				[codeblock]
+				"" - Main menu (macOS).
+				"_dock" - Dock popup menu (macOS).
+				[/codeblock]
 			</description>
 		</method>
 		<method name="global_menu_add_submenu_item">
@@ -159,41 +283,127 @@
 			<argument index="0" name="menu_root" type="String" />
 			<argument index="1" name="label" type="String" />
 			<argument index="2" name="submenu" type="String" />
+			<argument index="3" name="index" type="int" default="-1" />
 			<description>
+				Adds an item that will act as a submenu of the global menu [code]menu_root[/code]. The [code]submenu[/code] argument is the ID of the global menu root that will be shown when the item is clicked.
+				[b]Note:[/b] This method is implemented on macOS.
+				[b]Supported system menu IDs:[/b]
+				[codeblock]
+				"" - Main menu (macOS).
+				"_dock" - Dock popup menu (macOS).
+				[/codeblock]
 			</description>
 		</method>
 		<method name="global_menu_clear">
 			<return type="void" />
 			<argument index="0" name="menu_root" type="String" />
 			<description>
+				Removes all items from the global menu with ID [code]menu_root[/code].
+				[b]Note:[/b] This method is implemented on macOS.
+				[b]Supported system menu IDs:[/b]
+				[codeblock]
+				"" - Main menu (macOS).
+				"_dock" - Dock popup menu (macOS).
+				[/codeblock]
 			</description>
 		</method>
-		<method name="global_menu_get_item_callback">
+		<method name="global_menu_get_item_accelerator" qualifiers="const">
+			<return type="int" enum="Key" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="idx" type="int" />
+			<description>
+				Returns the accelerator of the item at index [code]idx[/code]. Accelerators are special combinations of keys that activate the item, no matter which control is focused.
+				[b]Note:[/b] This method is implemented on macOS.
+			</description>
+		</method>
+		<method name="global_menu_get_item_callback" qualifiers="const">
 			<return type="Callable" />
 			<argument index="0" name="menu_root" type="String" />
 			<argument index="1" name="idx" type="int" />
 			<description>
+				Returns the callback of the item at index [code]idx[/code].
+				[b]Note:[/b] This method is implemented on macOS.
+			</description>
+		</method>
+		<method name="global_menu_get_item_icon" qualifiers="const">
+			<return type="Texture2D" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="idx" type="int" />
+			<description>
+				Returns the icon of the item at index [code]idx[/code].
+				[b]Note:[/b] This method is implemented on macOS.
+			</description>
+		</method>
+		<method name="global_menu_get_item_index_from_tag" qualifiers="const">
+			<return type="int" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="tag" type="Variant" />
+			<description>
+				Returns the index of the item with the specified [code]tag[/code]. Index is automatically assigned to each item by the engine. Index can not be set manually.
+				[b]Note:[/b] This method is implemented on macOS.
 			</description>
 		</method>
-		<method name="global_menu_get_item_submenu">
+		<method name="global_menu_get_item_index_from_text" qualifiers="const">
+			<return type="int" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="text" type="String" />
+			<description>
+				Returns the index of the item with the specified [code]text[/code]. Index is automatically assigned to each item by the engine. Index can not be set manually.
+				[b]Note:[/b] This method is implemented on macOS.
+			</description>
+		</method>
+		<method name="global_menu_get_item_max_states" qualifiers="const">
+			<return type="int" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="idx" type="int" />
+			<description>
+				Returns number of states of an multistate item. See [method global_menu_add_multistate_item] for details.
+				[b]Note:[/b] This method is implemented on macOS.
+			</description>
+		</method>
+		<method name="global_menu_get_item_state" qualifiers="const">
+			<return type="int" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="idx" type="int" />
+			<description>
+				Returns the state of an multistate item. See [method global_menu_add_multistate_item] for details.
+				[b]Note:[/b] This method is implemented on macOS.
+			</description>
+		</method>
+		<method name="global_menu_get_item_submenu" qualifiers="const">
 			<return type="String" />
 			<argument index="0" name="menu_root" type="String" />
 			<argument index="1" name="idx" type="int" />
 			<description>
+				Returns the submenu ID of the item at index [code]idx[/code]. See [method global_menu_add_submenu_item] for more info on how to add a submenu.
+				[b]Note:[/b] This method is implemented on macOS.
 			</description>
 		</method>
-		<method name="global_menu_get_item_tag">
+		<method name="global_menu_get_item_tag" qualifiers="const">
 			<return type="Variant" />
 			<argument index="0" name="menu_root" type="String" />
 			<argument index="1" name="idx" type="int" />
 			<description>
+				Returns the metadata of the specified item, which might be of any type. You can set it with [method global_menu_set_item_tag], which provides a simple way of assigning context data to items.
+				[b]Note:[/b] This method is implemented on macOS.
 			</description>
 		</method>
-		<method name="global_menu_get_item_text">
+		<method name="global_menu_get_item_text" qualifiers="const">
 			<return type="String" />
 			<argument index="0" name="menu_root" type="String" />
 			<argument index="1" name="idx" type="int" />
 			<description>
+				Returns the text of the item at index [code]idx[/code].
+				[b]Note:[/b] This method is implemented on macOS.
+			</description>
+		</method>
+		<method name="global_menu_get_item_tooltip" qualifiers="const">
+			<return type="String" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="idx" type="int" />
+			<description>
+				Returns the tooltip associated with the specified index index [code]idx[/code].
+				[b]Note:[/b] This method is implemented on macOS.
 			</description>
 		</method>
 		<method name="global_menu_is_item_checkable" qualifiers="const">
@@ -201,6 +411,8 @@
 			<argument index="0" name="menu_root" type="String" />
 			<argument index="1" name="idx" type="int" />
 			<description>
+				Returns [code]true[/code] if the item at index [code]idx[/code] is checkable in some way, i.e. if it has a checkbox or radio button.
+				[b]Note:[/b] This method is implemented on macOS.
 			</description>
 		</method>
 		<method name="global_menu_is_item_checked" qualifiers="const">
@@ -208,6 +420,28 @@
 			<argument index="0" name="menu_root" type="String" />
 			<argument index="1" name="idx" type="int" />
 			<description>
+				Returns [code]true[/code] if the item at index [code]idx[/code] is checked.
+				[b]Note:[/b] This method is implemented on macOS.
+			</description>
+		</method>
+		<method name="global_menu_is_item_disabled" qualifiers="const">
+			<return type="bool" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="idx" type="int" />
+			<description>
+				Returns [code]true[/code] if the item at index [code]idx[/code] is disabled. When it is disabled it can't be selected, or its action invoked.
+				See [method global_menu_set_item_disabled] for more info on how to disable an item.
+				[b]Note:[/b] This method is implemented on macOS.
+			</description>
+		</method>
+		<method name="global_menu_is_item_radio_checkable" qualifiers="const">
+			<return type="bool" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="idx" type="int" />
+			<description>
+				Returns [code]true[/code] if the item at index [code]idx[/code] 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 on macOS.
 			</description>
 		</method>
 		<method name="global_menu_remove_item">
@@ -215,6 +449,19 @@
 			<argument index="0" name="menu_root" type="String" />
 			<argument index="1" name="idx" type="int" />
 			<description>
+				Removes the item at index [code]idx[/code] from the global menu [code]menu_root[/code].
+				[b]Note:[/b] The indices of items after the removed item will be shifted by one.
+				[b]Note:[/b] This method is implemented on macOS.
+			</description>
+		</method>
+		<method name="global_menu_set_item_accelerator">
+			<return type="void" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="idx" type="int" />
+			<argument index="2" name="keycode" type="int" enum="Key" />
+			<description>
+				Sets the accelerator of the item at index [code]idx[/code].
+				[b]Note:[/b] This method is implemented on macOS.
 			</description>
 		</method>
 		<method name="global_menu_set_item_callback">
@@ -223,6 +470,8 @@
 			<argument index="1" name="idx" type="int" />
 			<argument index="2" name="callback" type="Callable" />
 			<description>
+				Sets the callback of the item at index [code]idx[/code]. Callback is emitted when an item is pressed or its accelerator is activated.
+				[b]Note:[/b] This method is implemented on macOS.
 			</description>
 		</method>
 		<method name="global_menu_set_item_checkable">
@@ -231,6 +480,8 @@
 			<argument index="1" name="idx" type="int" />
 			<argument index="2" name="checkable" type="bool" />
 			<description>
+				Sets whether the item at index [code]idx[/code] has a checkbox. If [code]false[/code], sets the type of the item to plain text.
+				[b]Note:[/b] This method is implemented on macOS.
 			</description>
 		</method>
 		<method name="global_menu_set_item_checked">
@@ -239,6 +490,60 @@
 			<argument index="1" name="idx" type="int" />
 			<argument index="2" name="checked" type="bool" />
 			<description>
+				Sets the checkstate status of the item at index [code]idx[/code].
+				[b]Note:[/b] This method is implemented on macOS.
+			</description>
+		</method>
+		<method name="global_menu_set_item_disabled">
+			<return type="void" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="idx" type="int" />
+			<argument index="2" name="disabled" type="bool" />
+			<description>
+				Enables/disables the item at index [code]idx[/code]. When it is disabled, it can't be selected and its action can't be invoked.
+				[b]Note:[/b] This method is implemented on macOS.
+			</description>
+		</method>
+		<method name="global_menu_set_item_icon">
+			<return type="void" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="idx" type="int" />
+			<argument index="2" name="icon" type="Texture2D" />
+			<description>
+				Replaces the [Texture2D] icon of the specified [code]idx[/code].
+				[b]Note:[/b] This method is implemented on macOS.
+				[b]Note:[/b] This method is not supported by macOS "_dock" menu items.
+			</description>
+		</method>
+		<method name="global_menu_set_item_max_states">
+			<return type="void" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="idx" type="int" />
+			<argument index="2" name="max_states" type="int" />
+			<description>
+				Sets number of state of an multistate item. See [method global_menu_add_multistate_item] for details.
+				[b]Note:[/b] This method is implemented on macOS.
+			</description>
+		</method>
+		<method name="global_menu_set_item_radio_checkable">
+			<return type="void" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="idx" type="int" />
+			<argument index="2" name="checkable" type="bool" />
+			<description>
+				Sets the type of the item at the specified index [code]idx[/code] 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 on macOS.
+			</description>
+		</method>
+		<method name="global_menu_set_item_state">
+			<return type="void" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="idx" type="int" />
+			<argument index="2" name="state" type="int" />
+			<description>
+				Sets the state of an multistate item. See [method global_menu_add_multistate_item] for details.
+				[b]Note:[/b] This method is implemented on macOS.
 			</description>
 		</method>
 		<method name="global_menu_set_item_submenu">
@@ -247,6 +552,8 @@
 			<argument index="1" name="idx" type="int" />
 			<argument index="2" name="submenu" type="String" />
 			<description>
+				Sets the submenu of the item at index [code]idx[/code]. The submenu is the ID of a global menu root that would be shown when the item is clicked.
+				[b]Note:[/b] This method is implemented on macOS.
 			</description>
 		</method>
 		<method name="global_menu_set_item_tag">
@@ -255,6 +562,8 @@
 			<argument index="1" name="idx" type="int" />
 			<argument index="2" name="tag" type="Variant" />
 			<description>
+				Sets the metadata of an item, which may be of any type. You can later get it with [method global_menu_get_item_tag], which provides a simple way of assigning context data to items.
+				[b]Note:[/b] This method is implemented on macOS.
 			</description>
 		</method>
 		<method name="global_menu_set_item_text">
@@ -263,6 +572,18 @@
 			<argument index="1" name="idx" type="int" />
 			<argument index="2" name="text" type="String" />
 			<description>
+				Sets the text of the item at index [code]idx[/code].
+				[b]Note:[/b] This method is implemented on macOS.
+			</description>
+		</method>
+		<method name="global_menu_set_item_tooltip">
+			<return type="void" />
+			<argument index="0" name="menu_root" type="String" />
+			<argument index="1" name="idx" type="int" />
+			<argument index="2" name="tooltip" type="String" />
+			<description>
+				Sets the [String] tooltip of the item at the specified index [code]idx[/code].
+				[b]Note:[/b] This method is implemented on macOS.
 			</description>
 		</method>
 		<method name="has_feature" qualifiers="const">

+ 31 - 8
platform/osx/display_server_osx.h

@@ -186,6 +186,7 @@ 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<Image> &p_image) const;
 
 	static NSCursor *_cursor_from_selector(SEL p_selector, SEL p_fallback = nil);
 
@@ -217,24 +218,46 @@ public:
 	virtual bool has_feature(Feature p_feature) const override;
 	virtual String get_name() const override;
 
-	virtual void global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag = Variant()) override;
-	virtual void global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag = Variant()) override;
-	virtual void global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu) override;
-	virtual void global_menu_add_separator(const String &p_menu_root) override;
+	virtual void global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
+	virtual void global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
+	virtual void global_menu_add_icon_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
+	virtual void global_menu_add_icon_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
+	virtual void global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
+	virtual void global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
+	virtual void 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 Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
+	virtual void global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index = -1) override;
+	virtual void 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 Callable global_menu_get_item_callback(const String &p_menu_root, int p_idx) override;
-	virtual Variant global_menu_get_item_tag(const String &p_menu_root, int p_idx) override;
-	virtual String global_menu_get_item_text(const String &p_menu_root, int p_idx) override;
-	virtual String global_menu_get_item_submenu(const String &p_menu_root, int p_idx) 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 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 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<Texture2D> global_menu_get_item_icon(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_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_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<Texture2D> &p_icon) override;
 
 	virtual int global_menu_get_item_count(const String &p_menu_root) const override;
 

+ 515 - 20
platform/osx/display_server_osx.mm

@@ -91,6 +91,7 @@ NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) {
 		// 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];
 			submenu[p_menu_root] = n_menu;
 		}
 		menu = submenu[p_menu_root];
@@ -472,6 +473,40 @@ void DisplayServerOSX::_keyboard_layout_changed(CFNotificationCenterRef center,
 	}
 }
 
+NSImage *DisplayServerOSX::_convert_to_nsimg(Ref<Image> &p_image) const {
+	p_image->convert(Image::FORMAT_RGBA8);
+	NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
+			initWithBitmapDataPlanes:NULL
+						  pixelsWide:p_image->get_width()
+						  pixelsHigh:p_image->get_height()
+					   bitsPerSample:8
+					 samplesPerPixel:4
+							hasAlpha:YES
+							isPlanar:NO
+					  colorSpaceName:NSDeviceRGBColorSpace
+						 bytesPerRow:int(p_image->get_width()) * 4
+						bitsPerPixel:32];
+	ERR_FAIL_COND_V(imgrep == nil, nil);
+	uint8_t *pixels = [imgrep bitmapData];
+
+	int len = p_image->get_width() * p_image->get_height();
+	const uint8_t *r = p_image->get_data().ptr();
+
+	/* Premultiply the alpha channel */
+	for (int i = 0; i < len; i++) {
+		uint8_t alpha = r[i * 4 + 3];
+		pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255);
+		pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255);
+		pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255);
+		pixels[i * 4 + 3] = alpha;
+	}
+
+	NSImage *nsimg = [[NSImage alloc] initWithSize:NSMakeSize(p_image->get_width(), p_image->get_height())];
+	ERR_FAIL_COND_V(nsimg == nil, nil);
+	[nsimg addRepresentation:imgrep];
+	return nsimg;
+}
+
 NSCursor *DisplayServerOSX::_cursor_from_selector(SEL p_selector, SEL p_fallback) {
 	if ([NSCursor respondsToSelector:p_selector]) {
 		id object = [NSCursor performSelector:p_selector];
@@ -498,7 +533,14 @@ void DisplayServerOSX::menu_callback(id p_sender) {
 	GodotMenuItem *value = [p_sender representedObject];
 
 	if (value) {
-		if (value->checkable) {
+		if (value->max_states > 0) {
+			value->state++;
+			if (value->state >= value->max_states) {
+				value->state = 0;
+			}
+		}
+
+		if (value->checkable_type == CHECKABLE_TYPE_CHECK_BOX) {
 			if ([p_sender state] == NSControlStateValueOff) {
 				[p_sender setState:NSControlStateValueOn];
 			} else {
@@ -671,35 +713,195 @@ String DisplayServerOSX::get_name() const {
 	return "OSX";
 }
 
-void DisplayServerOSX::global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag) {
+void DisplayServerOSX::global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) {
+	_THREAD_SAFE_METHOD_
+
+	NSMenu *menu = _get_menu_root(p_menu_root);
+	if (menu) {
+		String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK);
+		NSMenuItem *menu_item;
+		if (p_index != -1) {
+			menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index];
+		} else {
+			menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
+		}
+		GodotMenuItem *obj = [[GodotMenuItem alloc] init];
+		obj->callback = p_callback;
+		obj->meta = p_tag;
+		obj->checkable_type = CHECKABLE_TYPE_NONE;
+		obj->max_states = 0;
+		obj->state = 0;
+		[menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)];
+		[menu_item setRepresentedObject:obj];
+	}
+}
+
+void DisplayServerOSX::global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) {
+	_THREAD_SAFE_METHOD_
+
+	NSMenu *menu = _get_menu_root(p_menu_root);
+	if (menu) {
+		String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK);
+		NSMenuItem *menu_item;
+		if (p_index != -1) {
+			menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index];
+		} else {
+			menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
+		}
+		GodotMenuItem *obj = [[GodotMenuItem alloc] init];
+		obj->callback = p_callback;
+		obj->meta = p_tag;
+		obj->checkable_type = CHECKABLE_TYPE_CHECK_BOX;
+		obj->max_states = 0;
+		obj->state = 0;
+		[menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)];
+		[menu_item setRepresentedObject:obj];
+	}
+}
+
+void DisplayServerOSX::global_menu_add_icon_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) {
+	_THREAD_SAFE_METHOD_
+
+	NSMenu *menu = _get_menu_root(p_menu_root);
+	if (menu) {
+		String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK);
+		NSMenuItem *menu_item;
+		if (p_index != -1) {
+			menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index];
+		} else {
+			menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
+		}
+		GodotMenuItem *obj = [[GodotMenuItem alloc] init];
+		obj->callback = p_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:KeyMappingOSX::keycode_get_native_mask(p_accel)];
+		[menu_item setRepresentedObject:obj];
+	}
+}
+
+void DisplayServerOSX::global_menu_add_icon_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) {
+	_THREAD_SAFE_METHOD_
+
+	NSMenu *menu = _get_menu_root(p_menu_root);
+	if (menu) {
+		String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK);
+		NSMenuItem *menu_item;
+		if (p_index != -1) {
+			menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index];
+		} else {
+			menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
+		}
+		GodotMenuItem *obj = [[GodotMenuItem alloc] init];
+		obj->callback = p_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:KeyMappingOSX::keycode_get_native_mask(p_accel)];
+		[menu_item setRepresentedObject:obj];
+	}
+}
+
+void DisplayServerOSX::global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) {
+	_THREAD_SAFE_METHOD_
+
+	NSMenu *menu = _get_menu_root(p_menu_root);
+	if (menu) {
+		String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK);
+		NSMenuItem *menu_item;
+		if (p_index != -1) {
+			menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index];
+		} else {
+			menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
+		}
+		GodotMenuItem *obj = [[GodotMenuItem alloc] init];
+		obj->callback = p_callback;
+		obj->meta = p_tag;
+		obj->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON;
+		obj->max_states = 0;
+		obj->state = 0;
+		[menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)];
+		[menu_item setRepresentedObject:obj];
+	}
+}
+
+void DisplayServerOSX::global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) {
 	_THREAD_SAFE_METHOD_
 
 	NSMenu *menu = _get_menu_root(p_menu_root);
 	if (menu) {
-		NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""];
+		String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK);
+		NSMenuItem *menu_item;
+		if (p_index != -1) {
+			menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index];
+		} else {
+			menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
+		}
 		GodotMenuItem *obj = [[GodotMenuItem alloc] init];
 		obj->callback = p_callback;
 		obj->meta = p_tag;
-		obj->checkable = false;
+		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:KeyMappingOSX::keycode_get_native_mask(p_accel)];
 		[menu_item setRepresentedObject:obj];
 	}
 }
 
-void DisplayServerOSX::global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag) {
+void DisplayServerOSX::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 Variant &p_tag, Key p_accel, int p_index) {
 	_THREAD_SAFE_METHOD_
 
 	NSMenu *menu = _get_menu_root(p_menu_root);
 	if (menu) {
-		NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""];
+		String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK);
+		NSMenuItem *menu_item;
+		if (p_index != -1) {
+			menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index];
+		} else {
+			menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
+		}
 		GodotMenuItem *obj = [[GodotMenuItem alloc] init];
 		obj->callback = p_callback;
 		obj->meta = p_tag;
-		obj->checkable = true;
+		obj->checkable_type = CHECKABLE_TYPE_NONE;
+		obj->max_states = p_max_states;
+		obj->state = p_default_state;
+		[menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)];
 		[menu_item setRepresentedObject:obj];
 	}
 }
 
-void DisplayServerOSX::global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu) {
+void DisplayServerOSX::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);
@@ -713,18 +915,58 @@ void DisplayServerOSX::global_menu_add_submenu_item(const String &p_menu_root, c
 			ERR_PRINT("Can't set submenu to menu that is already a submenu of some other menu!");
 			return;
 		}
-		NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@""];
+		NSMenuItem *menu_item;
+		if (p_index != -1) {
+			menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@"" atIndex:p_index];
+		} else {
+			menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@""];
+		}
+		[sub_menu setTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()]];
 		[menu setSubmenu:sub_menu forItem:menu_item];
 	}
 }
 
-void DisplayServerOSX::global_menu_add_separator(const String &p_menu_root) {
+void DisplayServerOSX::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) {
-		[menu addItem:[NSMenuItem separatorItem]];
+		if (p_index != -1) {
+			[menu insertItem:[NSMenuItem separatorItem] atIndex:p_index];
+		} else {
+			[menu addItem:[NSMenuItem separatorItem]];
+		}
+	}
+}
+
+int DisplayServerOSX::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) {
+		return [menu indexOfItemWithTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]];
 	}
+
+	return -1;
+}
+
+int DisplayServerOSX::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) {
+		for (NSInteger i = 0; i < [menu numberOfItems]; 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;
+				}
+			}
+		}
+	}
+
+	return -1;
 }
 
 bool DisplayServerOSX::global_menu_is_item_checked(const String &p_menu_root, int p_idx) const {
@@ -749,14 +991,30 @@ bool DisplayServerOSX::global_menu_is_item_checkable(const String &p_menu_root,
 		if (menu_item) {
 			GodotMenuItem *obj = [menu_item representedObject];
 			if (obj) {
-				return obj->checkable;
+				return obj->checkable_type == CHECKABLE_TYPE_CHECK_BOX;
+			}
+		}
+	}
+	return false;
+}
+
+bool DisplayServerOSX::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) {
+		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 DisplayServerOSX::global_menu_get_item_callback(const String &p_menu_root, int p_idx) {
+Callable DisplayServerOSX::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);
@@ -772,7 +1030,7 @@ Callable DisplayServerOSX::global_menu_get_item_callback(const String &p_menu_ro
 	return Callable();
 }
 
-Variant DisplayServerOSX::global_menu_get_item_tag(const String &p_menu_root, int p_idx) {
+Variant DisplayServerOSX::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);
@@ -788,22 +1046,20 @@ Variant DisplayServerOSX::global_menu_get_item_tag(const String &p_menu_root, in
 	return Variant();
 }
 
-String DisplayServerOSX::global_menu_get_item_text(const String &p_menu_root, int p_idx) {
+String DisplayServerOSX::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) {
 		const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
 		if (menu_item) {
-			String ret;
-			ret.parse_utf8([[menu_item title] UTF8String]);
-			return ret;
+			return String::utf8([[menu_item title] UTF8String]);
 		}
 	}
 	return String();
 }
 
-String DisplayServerOSX::global_menu_get_item_submenu(const String &p_menu_root, int p_idx) {
+String DisplayServerOSX::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);
@@ -823,6 +1079,116 @@ String DisplayServerOSX::global_menu_get_item_submenu(const String &p_menu_root,
 	return String();
 }
 
+Key DisplayServerOSX::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) {
+		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 DisplayServerOSX::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) {
+		const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+		if (menu_item) {
+			return ![menu_item isEnabled];
+		}
+	}
+	return false;
+}
+
+String DisplayServerOSX::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) {
+		const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+		if (menu_item) {
+			return String::utf8([[menu_item toolTip] UTF8String]);
+		}
+	}
+	return String();
+}
+
+int DisplayServerOSX::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) {
+		const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+		if (menu_item) {
+			GodotMenuItem *obj = [menu_item representedObject];
+			if (obj) {
+				return obj->state;
+			}
+		}
+	}
+	return 0;
+}
+
+int DisplayServerOSX::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) {
+		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<Texture2D> DisplayServerOSX::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) {
+		const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+		if (menu_item) {
+			GodotMenuItem *obj = [menu_item representedObject];
+			if (obj) {
+				if (obj->img.is_valid()) {
+					Ref<ImageTexture> txt;
+					txt.instantiate();
+					txt->create_from_image(obj->img);
+					return txt;
+				}
+			}
+		}
+	}
+	return Ref<Texture2D>();
+}
+
 void DisplayServerOSX::global_menu_set_item_checked(const String &p_menu_root, int p_idx, bool p_checked) {
 	_THREAD_SAFE_METHOD_
 
@@ -853,7 +1219,23 @@ void DisplayServerOSX::global_menu_set_item_checkable(const String &p_menu_root,
 		NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
 		if (menu_item) {
 			GodotMenuItem *obj = [menu_item representedObject];
-			obj->checkable = p_checkable;
+			obj->checkable_type = (p_checkable) ? CHECKABLE_TYPE_CHECK_BOX : CHECKABLE_TYPE_NONE;
+		}
+	}
+}
+
+void DisplayServerOSX::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) {
+		if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
+			return;
+		}
+		NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+		if (menu_item) {
+			GodotMenuItem *obj = [menu_item representedObject];
+			obj->checkable_type = (p_checkable) ? CHECKABLE_TYPE_RADIO_BUTTON : CHECKABLE_TYPE_NONE;
 		}
 	}
 }
@@ -929,6 +1311,116 @@ void DisplayServerOSX::global_menu_set_item_submenu(const String &p_menu_root, i
 	}
 }
 
+void DisplayServerOSX::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) {
+		if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
+			return;
+		}
+		NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+		if (menu_item) {
+			[menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_keycode)];
+			String keycode = KeyMappingOSX::keycode_get_native_string(p_keycode & KeyModifierMask::CODE_MASK);
+			[menu_item setKeyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
+		}
+	}
+}
+
+void DisplayServerOSX::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) {
+		if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
+			return;
+		}
+		NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+		if (menu_item) {
+			[menu_item setEnabled:(!p_disabled)];
+		}
+	}
+}
+
+void DisplayServerOSX::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) {
+		if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
+			return;
+		}
+		NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+		if (menu_item) {
+			[menu_item setToolTip:[NSString stringWithUTF8String:p_tooltip.utf8().get_data()]];
+		}
+	}
+}
+
+void DisplayServerOSX::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) {
+		if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
+			return;
+		}
+		NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+		if (menu_item) {
+			GodotMenuItem *obj = [menu_item representedObject];
+			if (obj) {
+				obj->state = p_state;
+			}
+		}
+	}
+}
+
+void DisplayServerOSX::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) {
+		if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
+			return;
+		}
+		NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+		if (menu_item) {
+			GodotMenuItem *obj = [menu_item representedObject];
+			if (obj) {
+				obj->max_states = p_max_states;
+			}
+		}
+	}
+}
+
+void DisplayServerOSX::global_menu_set_item_icon(const String &p_menu_root, int p_idx, const Ref<Texture2D> &p_icon) {
+	_THREAD_SAFE_METHOD_
+
+	NSMenu *menu = _get_menu_root(p_menu_root);
+	if (menu) {
+		if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
+			return;
+		}
+		NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+		if (menu_item) {
+			GodotMenuItem *obj = [menu_item representedObject];
+			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<Image>();
+				[menu_item setImage:nil];
+			}
+		}
+	}
+}
+
 int DisplayServerOSX::global_menu_get_item_count(const String &p_menu_root) const {
 	_THREAD_SAFE_METHOD_
 
@@ -2628,11 +3120,13 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
 
 	// Setup Dock menu.
 	dock_menu = [[NSMenu alloc] initWithTitle:@"_dock"];
+	[dock_menu setAutoenablesItems:NO];
 
 	// Setup Apple menu.
 	apple_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 addItem:[NSMenuItem separatorItem]];
 
@@ -2660,6 +3154,7 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
 	NSMenu *main_menu = [NSApp mainMenu];
 	menu_item = [main_menu addItemWithTitle:@"" action:nil keyEquivalent:@""];
 	[main_menu setSubmenu:apple_menu forItem:menu_item];
+	[main_menu setAutoenablesItems:NO];
 
 	//!!!!!!!!!!!!!!!!!!!!!!!!!!
 	//TODO - do Vulkan and OpenGL support checks, driver selection and fallback

+ 10 - 1
platform/osx/godot_menu_item.h

@@ -36,12 +36,21 @@
 #import <AppKit/AppKit.h>
 #import <Foundation/Foundation.h>
 
+enum GlobalMenuCheckType {
+	CHECKABLE_TYPE_NONE,
+	CHECKABLE_TYPE_CHECK_BOX,
+	CHECKABLE_TYPE_RADIO_BUTTON,
+};
+
 @interface GodotMenuItem : NSObject {
 @public
 	Callable callback;
 	Variant meta;
 	int id;
-	bool checkable;
+	GlobalMenuCheckType checkable_type;
+	int max_states;
+	int state;
+	Ref<Image> img;
 }
 
 @end

+ 127 - 12
servers/display_server.cpp

@@ -44,22 +44,52 @@ DisplayServer::DisplayServerCreate DisplayServer::server_create_functions[Displa
 
 int DisplayServer::server_create_count = 1;
 
-void DisplayServer::global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag) {
+void DisplayServer::global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) {
 	WARN_PRINT("Global menus not supported by this display server.");
 }
 
-void DisplayServer::global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag) {
+void DisplayServer::global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) {
 	WARN_PRINT("Global menus not supported by this display server.");
 }
 
-void DisplayServer::global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu) {
+void DisplayServer::global_menu_add_icon_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) {
 	WARN_PRINT("Global menus not supported by this display server.");
 }
 
-void DisplayServer::global_menu_add_separator(const String &p_menu_root) {
+void DisplayServer::global_menu_add_icon_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) {
 	WARN_PRINT("Global menus not supported by this display server.");
 }
 
+void DisplayServer::global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) {
+	WARN_PRINT("Global menus not supported by this display server.");
+}
+
+void DisplayServer::global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) {
+	WARN_PRINT("Global menus not supported by this display server.");
+}
+
+void 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 Variant &p_tag, Key p_accel, int p_index) {
+	WARN_PRINT("Global menus not supported by this display server.");
+}
+
+void 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.");
+}
+
+void DisplayServer::global_menu_add_separator(const String &p_menu_root, int p_index) {
+	WARN_PRINT("Global menus not supported by this display server.");
+}
+
+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;
+}
+
+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;
+}
+
 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.");
 }
@@ -74,26 +104,61 @@ bool DisplayServer::global_menu_is_item_checkable(const String &p_menu_root, int
 	return false;
 }
 
-Callable DisplayServer::global_menu_get_item_callback(const String &p_menu_root, int 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;
+}
+
+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();
 }
 
-Variant DisplayServer::global_menu_get_item_tag(const String &p_menu_root, int 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();
 }
 
-String DisplayServer::global_menu_get_item_text(const String &p_menu_root, int 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();
 }
 
-String DisplayServer::global_menu_get_item_submenu(const String &p_menu_root, int 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.");
 	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;
+}
+
+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;
+}
+
+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();
+}
+
+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;
+}
+
+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;
+}
+
+Ref<Texture2D> 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<Texture2D>();
+}
+
 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.");
 }
@@ -102,6 +167,10 @@ void DisplayServer::global_menu_set_item_checkable(const String &p_menu_root, in
 	WARN_PRINT("Global menus not supported by this display server.");
 }
 
+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.");
+}
+
 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.");
 }
@@ -114,6 +183,30 @@ void DisplayServer::global_menu_set_item_submenu(const String &p_menu_root, int
 	WARN_PRINT("Global menus not supported by this display server.");
 }
 
+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.");
+}
+
+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.");
+}
+
+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.");
+}
+
+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.");
+}
+
+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.");
+}
+
+void DisplayServer::global_menu_set_item_icon(const String &p_menu_root, int p_idx, const Ref<Texture2D> &p_icon) {
+	WARN_PRINT("Global menus not supported by this display server.");
+}
+
 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;
@@ -341,24 +434,46 @@ void DisplayServer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("has_feature", "feature"), &DisplayServer::has_feature);
 	ClassDB::bind_method(D_METHOD("get_name"), &DisplayServer::get_name);
 
-	ClassDB::bind_method(D_METHOD("global_menu_add_item", "menu_root", "label", "callback", "tag"), &DisplayServer::global_menu_add_item, DEFVAL(Variant()));
-	ClassDB::bind_method(D_METHOD("global_menu_add_check_item", "menu_root", "label", "callback", "tag"), &DisplayServer::global_menu_add_check_item, DEFVAL(Variant()));
-	ClassDB::bind_method(D_METHOD("global_menu_add_submenu_item", "menu_root", "label", "submenu"), &DisplayServer::global_menu_add_submenu_item);
-	ClassDB::bind_method(D_METHOD("global_menu_add_separator", "menu_root"), &DisplayServer::global_menu_add_separator);
+	ClassDB::bind_method(D_METHOD("global_menu_add_item", "menu_root", "label", "callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_item, DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
+	ClassDB::bind_method(D_METHOD("global_menu_add_check_item", "menu_root", "label", "callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_check_item, DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
+	ClassDB::bind_method(D_METHOD("global_menu_add_icon_item", "menu_root", "icon", "label", "callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_icon_item, DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
+	ClassDB::bind_method(D_METHOD("global_menu_add_icon_check_item", "menu_root", "icon", "label", "callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_icon_check_item, DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
+	ClassDB::bind_method(D_METHOD("global_menu_add_radio_check_item", "menu_root", "label", "callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_radio_check_item, DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
+	ClassDB::bind_method(D_METHOD("global_menu_add_icon_radio_check_item", "menu_root", "icon", "label", "callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_icon_radio_check_item, DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
+	ClassDB::bind_method(D_METHOD("global_menu_add_multistate_item", "menu_root", "labe", "max_states", "default_state", "callback", "tag", "accelerator", "index"), &DisplayServer::global_menu_add_multistate_item, DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
+	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_separator", "menu_root", "index"), &DisplayServer::global_menu_add_separator, DEFVAL(-1));
+
+	ClassDB::bind_method(D_METHOD("global_menu_get_item_index_from_text", "menu_root", "text"), &DisplayServer::global_menu_get_item_index_from_text);
+	ClassDB::bind_method(D_METHOD("global_menu_get_item_index_from_tag", "menu_root", "tag"), &DisplayServer::global_menu_get_item_index_from_tag);
 
 	ClassDB::bind_method(D_METHOD("global_menu_is_item_checked", "menu_root", "idx"), &DisplayServer::global_menu_is_item_checked);
 	ClassDB::bind_method(D_METHOD("global_menu_is_item_checkable", "menu_root", "idx"), &DisplayServer::global_menu_is_item_checkable);
+	ClassDB::bind_method(D_METHOD("global_menu_is_item_radio_checkable", "menu_root", "idx"), &DisplayServer::global_menu_is_item_radio_checkable);
 	ClassDB::bind_method(D_METHOD("global_menu_get_item_callback", "menu_root", "idx"), &DisplayServer::global_menu_get_item_callback);
 	ClassDB::bind_method(D_METHOD("global_menu_get_item_tag", "menu_root", "idx"), &DisplayServer::global_menu_get_item_tag);
 	ClassDB::bind_method(D_METHOD("global_menu_get_item_text", "menu_root", "idx"), &DisplayServer::global_menu_get_item_text);
 	ClassDB::bind_method(D_METHOD("global_menu_get_item_submenu", "menu_root", "idx"), &DisplayServer::global_menu_get_item_submenu);
+	ClassDB::bind_method(D_METHOD("global_menu_get_item_accelerator", "menu_root", "idx"), &DisplayServer::global_menu_get_item_accelerator);
+	ClassDB::bind_method(D_METHOD("global_menu_is_item_disabled", "menu_root", "idx"), &DisplayServer::global_menu_is_item_disabled);
+	ClassDB::bind_method(D_METHOD("global_menu_get_item_tooltip", "menu_root", "idx"), &DisplayServer::global_menu_get_item_tooltip);
+	ClassDB::bind_method(D_METHOD("global_menu_get_item_state", "menu_root", "idx"), &DisplayServer::global_menu_get_item_state);
+	ClassDB::bind_method(D_METHOD("global_menu_get_item_max_states", "menu_root", "idx"), &DisplayServer::global_menu_get_item_max_states);
+	ClassDB::bind_method(D_METHOD("global_menu_get_item_icon", "menu_root", "idx"), &DisplayServer::global_menu_get_item_icon);
 
 	ClassDB::bind_method(D_METHOD("global_menu_set_item_checked", "menu_root", "idx", "checked"), &DisplayServer::global_menu_set_item_checked);
 	ClassDB::bind_method(D_METHOD("global_menu_set_item_checkable", "menu_root", "idx", "checkable"), &DisplayServer::global_menu_set_item_checkable);
+	ClassDB::bind_method(D_METHOD("global_menu_set_item_radio_checkable", "menu_root", "idx", "checkable"), &DisplayServer::global_menu_set_item_radio_checkable);
 	ClassDB::bind_method(D_METHOD("global_menu_set_item_callback", "menu_root", "idx", "callback"), &DisplayServer::global_menu_set_item_callback);
 	ClassDB::bind_method(D_METHOD("global_menu_set_item_tag", "menu_root", "idx", "tag"), &DisplayServer::global_menu_set_item_tag);
 	ClassDB::bind_method(D_METHOD("global_menu_set_item_text", "menu_root", "idx", "text"), &DisplayServer::global_menu_set_item_text);
 	ClassDB::bind_method(D_METHOD("global_menu_set_item_submenu", "menu_root", "idx", "submenu"), &DisplayServer::global_menu_set_item_submenu);
+	ClassDB::bind_method(D_METHOD("global_menu_set_item_accelerator", "menu_root", "idx", "keycode"), &DisplayServer::global_menu_set_item_accelerator);
+	ClassDB::bind_method(D_METHOD("global_menu_set_item_disabled", "menu_root", "idx", "disabled"), &DisplayServer::global_menu_set_item_disabled);
+	ClassDB::bind_method(D_METHOD("global_menu_set_item_tooltip", "menu_root", "idx", "tooltip"), &DisplayServer::global_menu_set_item_tooltip);
+	ClassDB::bind_method(D_METHOD("global_menu_set_item_state", "menu_root", "idx", "state"), &DisplayServer::global_menu_set_item_state);
+	ClassDB::bind_method(D_METHOD("global_menu_set_item_max_states", "menu_root", "idx", "max_states"), &DisplayServer::global_menu_set_item_max_states);
+	ClassDB::bind_method(D_METHOD("global_menu_set_item_icon", "menu_root", "idx", "icon"), &DisplayServer::global_menu_set_item_icon);
 
 	ClassDB::bind_method(D_METHOD("global_menu_remove_item", "menu_root", "idx"), &DisplayServer::global_menu_remove_item);
 	ClassDB::bind_method(D_METHOD("global_menu_clear", "menu_root"), &DisplayServer::global_menu_clear);

+ 30 - 8
servers/display_server.h

@@ -126,24 +126,46 @@ public:
 	virtual bool has_feature(Feature p_feature) const = 0;
 	virtual String get_name() const = 0;
 
-	virtual void global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag = Variant());
-	virtual void global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag = Variant());
-	virtual void global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu);
-	virtual void global_menu_add_separator(const String &p_menu_root);
+	virtual void global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
+	virtual void global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
+	virtual void global_menu_add_icon_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
+	virtual void global_menu_add_icon_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
+	virtual void global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
+	virtual void global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
+	virtual void 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 Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1);
+	virtual void global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index = -1);
+	virtual void global_menu_add_separator(const String &p_menu_root, int p_index = -1);
+
+	virtual int global_menu_get_item_index_from_text(const String &p_menu_root, const String &p_text) const;
+	virtual int global_menu_get_item_index_from_tag(const String &p_menu_root, const Variant &p_tag) const;
 
 	virtual bool global_menu_is_item_checked(const String &p_menu_root, int p_idx) const;
 	virtual bool global_menu_is_item_checkable(const String &p_menu_root, int p_idx) const;
-	virtual Callable global_menu_get_item_callback(const String &p_menu_root, int p_idx);
-	virtual Variant global_menu_get_item_tag(const String &p_menu_root, int p_idx);
-	virtual String global_menu_get_item_text(const String &p_menu_root, int p_idx);
-	virtual String global_menu_get_item_submenu(const String &p_menu_root, int p_idx);
+	virtual bool global_menu_is_item_radio_checkable(const String &p_menu_root, int p_idx) const;
+	virtual Callable global_menu_get_item_callback(const String &p_menu_root, int p_idx) const;
+	virtual Variant global_menu_get_item_tag(const String &p_menu_root, int p_idx) const;
+	virtual String global_menu_get_item_text(const String &p_menu_root, int p_idx) const;
+	virtual String global_menu_get_item_submenu(const String &p_menu_root, int p_idx) const;
+	virtual Key global_menu_get_item_accelerator(const String &p_menu_root, int p_idx) const;
+	virtual bool global_menu_is_item_disabled(const String &p_menu_root, int p_idx) const;
+	virtual String global_menu_get_item_tooltip(const String &p_menu_root, int p_idx) const;
+	virtual int global_menu_get_item_state(const String &p_menu_root, int p_idx) const;
+	virtual int global_menu_get_item_max_states(const String &p_menu_root, int p_idx) const;
+	virtual Ref<Texture2D> global_menu_get_item_icon(const String &p_menu_root, int p_idx) const;
 
 	virtual void global_menu_set_item_checked(const String &p_menu_root, int p_idx, bool p_checked);
 	virtual void global_menu_set_item_checkable(const String &p_menu_root, int p_idx, bool p_checkable);
+	virtual void global_menu_set_item_radio_checkable(const String &p_menu_root, int p_idx, bool p_checkable);
 	virtual void global_menu_set_item_callback(const String &p_menu_root, int p_idx, const Callable &p_callback);
 	virtual void global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag);
 	virtual void global_menu_set_item_text(const String &p_menu_root, int p_idx, const String &p_text);
 	virtual void global_menu_set_item_submenu(const String &p_menu_root, int p_idx, const String &p_submenu);
+	virtual void global_menu_set_item_accelerator(const String &p_menu_root, int p_idx, Key p_keycode);
+	virtual void global_menu_set_item_disabled(const String &p_menu_root, int p_idx, bool p_disabled);
+	virtual void global_menu_set_item_tooltip(const String &p_menu_root, int p_idx, const String &p_tooltip);
+	virtual void global_menu_set_item_state(const String &p_menu_root, int p_idx, int p_state);
+	virtual void global_menu_set_item_max_states(const String &p_menu_root, int p_idx, int p_max_states);
+	virtual void global_menu_set_item_icon(const String &p_menu_root, int p_idx, const Ref<Texture2D> &p_icon);
 
 	virtual int global_menu_get_item_count(const String &p_menu_root) const;