Browse Source

Merge pull request #54533 from rafallus/menubutton_items

Rémi Verschelde 3 years ago
parent
commit
f00ba792b3

+ 3 - 0
doc/classes/MenuButton.xml

@@ -30,6 +30,9 @@
 		<member name="action_mode" type="int" setter="set_action_mode" getter="get_action_mode" override="true" enum="BaseButton.ActionMode" default="0" />
 		<member name="flat" type="bool" setter="set_flat" getter="is_flat" override="true" default="true" />
 		<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" override="true" enum="Control.FocusMode" default="0" />
+		<member name="items_count" type="int" setter="set_item_count" getter="get_item_count" default="0">
+			The number of items currently in the list.
+		</member>
 		<member name="switch_on_hover" type="bool" setter="set_switch_on_hover" getter="is_switch_on_hover" default="false">
 			If [code]true[/code], when the cursor hovers above another [MenuButton] within the same parent which also has [code]switch_on_hover[/code] enabled, it will close the current [MenuButton] and open the other one.
 		</member>

+ 3 - 6
doc/classes/PopupMenu.xml

@@ -199,12 +199,6 @@
 				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.
 			</description>
 		</method>
-		<method name="get_item_count" qualifiers="const">
-			<return type="int" />
-			<description>
-				Returns the number of items in the [PopupMenu].
-			</description>
-		</method>
 		<method name="get_item_icon" qualifiers="const">
 			<return type="Texture2D" />
 			<argument index="0" name="idx" type="int" />
@@ -511,6 +505,9 @@
 		<member name="hide_on_state_item_selection" type="bool" setter="set_hide_on_state_item_selection" getter="is_hide_on_state_item_selection" default="false">
 			If [code]true[/code], hides the [PopupMenu] when a state item is selected.
 		</member>
+		<member name="items_count" type="int" setter="set_item_count" getter="get_item_count" default="0">
+			The number of items currently in the list.
+		</member>
 		<member name="submenu_popup_delay" type="float" setter="set_submenu_popup_delay" getter="get_submenu_popup_delay" default="0.3">
 			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.
 		</member>

+ 63 - 11
scene/gui/menu_button.cpp

@@ -110,14 +110,6 @@ PopupMenu *MenuButton::get_popup() const {
 	return popup;
 }
 
-void MenuButton::_set_items(const Array &p_items) {
-	popup->set("items", p_items);
-}
-
-Array MenuButton::_get_items() const {
-	return popup->get("items");
-}
-
 void MenuButton::set_switch_on_hover(bool p_enabled) {
 	switch_on_hover = p_enabled;
 }
@@ -126,6 +118,16 @@ bool MenuButton::is_switch_on_hover() {
 	return switch_on_hover;
 }
 
+void MenuButton::set_item_count(int p_count) {
+	ERR_FAIL_COND(p_count < 0);
+	popup->set_item_count(p_count);
+	notify_property_list_changed();
+}
+
+int MenuButton::get_item_count() const {
+	return popup->get_item_count();
+}
+
 void MenuButton::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_VISIBILITY_CHANGED: {
@@ -146,16 +148,66 @@ void MenuButton::_notification(int p_what) {
 	}
 }
 
+bool MenuButton::_set(const StringName &p_name, const Variant &p_value) {
+	Vector<String> components = String(p_name).split("/", true, 2);
+	if (components.size() >= 2 && components[0] == "popup") {
+		bool valid;
+		popup->set(String(p_name).trim_prefix("popup/"), p_value, &valid);
+		return valid;
+	}
+	return false;
+}
+
+bool MenuButton::_get(const StringName &p_name, Variant &r_ret) const {
+	Vector<String> components = String(p_name).split("/", true, 2);
+	if (components.size() >= 2 && components[0] == "popup") {
+		bool valid;
+		r_ret = popup->get(String(p_name).trim_prefix("popup/"), &valid);
+		return valid;
+	}
+	return false;
+}
+
+void MenuButton::_get_property_list(List<PropertyInfo> *p_list) const {
+	for (int i = 0; i < popup->get_item_count(); i++) {
+		p_list->push_back(PropertyInfo(Variant::STRING, vformat("popup/item_%d/text", i)));
+
+		PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("popup/item_%d/icon", i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D");
+		pi.usage &= ~(popup->get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0);
+		p_list->push_back(pi);
+
+		pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/checkable", i), PROPERTY_HINT_ENUM, "No,As checkbox,As radio button");
+		pi.usage &= ~(!popup->is_item_checkable(i) ? PROPERTY_USAGE_STORAGE : 0);
+		p_list->push_back(pi);
+
+		pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/checked", i));
+		pi.usage &= ~(!popup->is_item_checked(i) ? PROPERTY_USAGE_STORAGE : 0);
+		p_list->push_back(pi);
+
+		pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/id", i), PROPERTY_HINT_RANGE, "1,10,1,or_greater");
+		p_list->push_back(pi);
+
+		pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/disabled", i));
+		pi.usage &= ~(!popup->is_item_disabled(i) ? PROPERTY_USAGE_STORAGE : 0);
+		p_list->push_back(pi);
+
+		pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/separator", i));
+		pi.usage &= ~(!popup->is_item_separator(i) ? PROPERTY_USAGE_STORAGE : 0);
+		p_list->push_back(pi);
+	}
+}
+
 void MenuButton::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_popup"), &MenuButton::get_popup);
-	ClassDB::bind_method(D_METHOD("_set_items"), &MenuButton::_set_items);
-	ClassDB::bind_method(D_METHOD("_get_items"), &MenuButton::_get_items);
 	ClassDB::bind_method(D_METHOD("set_switch_on_hover", "enable"), &MenuButton::set_switch_on_hover);
 	ClassDB::bind_method(D_METHOD("is_switch_on_hover"), &MenuButton::is_switch_on_hover);
 	ClassDB::bind_method(D_METHOD("set_disable_shortcuts", "disabled"), &MenuButton::set_disable_shortcuts);
 
-	ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
+	ClassDB::bind_method(D_METHOD("set_item_count"), &MenuButton::set_item_count);
+	ClassDB::bind_method(D_METHOD("get_item_count"), &MenuButton::get_item_count);
+
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "switch_on_hover"), "set_switch_on_hover", "is_switch_on_hover");
+	ADD_ARRAY_COUNT("Items", "items_count", "set_item_count", "get_item_count", "popup/item_");
 
 	ADD_SIGNAL(MethodInfo("about_to_popup"));
 }

+ 6 - 3
scene/gui/menu_button.h

@@ -44,15 +44,15 @@ class MenuButton : public Button {
 
 	Vector2i mouse_pos_adjusted;
 
-	Array _get_items() const;
-	void _set_items(const Array &p_items);
-
 	virtual void gui_input(const Ref<InputEvent> &p_event) override;
 
 	void _popup_visibility_changed(bool p_visible);
 
 protected:
 	void _notification(int p_what);
+	bool _set(const StringName &p_name, const Variant &p_value);
+	bool _get(const StringName &p_name, Variant &r_ret) const;
+	void _get_property_list(List<PropertyInfo> *p_list) const;
 	static void _bind_methods();
 	virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override;
 
@@ -64,6 +64,9 @@ public:
 	bool is_switch_on_hover();
 	void set_disable_shortcuts(bool p_disabled);
 
+	void set_item_count(int p_count);
+	int get_item_count() const;
+
 	MenuButton();
 	~MenuButton();
 };

+ 153 - 64
scene/gui/popup_menu.cpp

@@ -820,6 +820,7 @@ void PopupMenu::add_item(const String &p_label, int p_id, uint32_t p_accel) {
 	_shape_item(items.size() - 1);
 	control->update();
 	child_controls_changed();
+	notify_property_list_changed();
 }
 
 void PopupMenu::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id, uint32_t p_accel) {
@@ -830,6 +831,7 @@ void PopupMenu::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_labe
 	_shape_item(items.size() - 1);
 	control->update();
 	child_controls_changed();
+	notify_property_list_changed();
 }
 
 void PopupMenu::add_check_item(const String &p_label, int p_id, uint32_t p_accel) {
@@ -1271,6 +1273,14 @@ int PopupMenu::get_current_index() const {
 	return mouse_over;
 }
 
+void PopupMenu::set_item_count(int p_count) {
+	ERR_FAIL_COND(p_count < 0);
+	items.resize(p_count);
+	control->update();
+	child_controls_changed();
+	notify_property_list_changed();
+}
+
 int PopupMenu::get_item_count() const {
 	return items.size();
 }
@@ -1420,27 +1430,7 @@ void PopupMenu::clear() {
 	mouse_over = -1;
 	control->update();
 	child_controls_changed();
-}
-
-Array PopupMenu::_get_items() const {
-	Array items;
-	for (int i = 0; i < get_item_count(); i++) {
-		items.push_back(get_item_text(i));
-		items.push_back(get_item_icon(i));
-		// For compatibility, use false/true for no/checkbox and integers for other values
-		int ct = this->items[i].checkable_type;
-		items.push_back(Variant(ct <= Item::CHECKABLE_TYPE_CHECK_BOX ? is_item_checkable(i) : ct));
-		items.push_back(is_item_checked(i));
-		items.push_back(is_item_disabled(i));
-
-		items.push_back(get_item_id(i));
-		items.push_back(get_item_accelerator(i));
-		items.push_back(get_item_metadata(i));
-		items.push_back(get_item_submenu(i));
-		items.push_back(is_item_separator(i));
-	}
-
-	return items;
+	notify_property_list_changed();
 }
 
 void PopupMenu::_ref_shortcut(Ref<Shortcut> p_sc) {
@@ -1461,45 +1451,6 @@ void PopupMenu::_unref_shortcut(Ref<Shortcut> p_sc) {
 	}
 }
 
-void PopupMenu::_set_items(const Array &p_items) {
-	ERR_FAIL_COND(p_items.size() % 10);
-	clear();
-
-	for (int i = 0; i < p_items.size(); i += 10) {
-		String text = p_items[i + 0];
-		Ref<Texture2D> icon = p_items[i + 1];
-		// For compatibility, use false/true for no/checkbox and integers for other values
-		bool checkable = p_items[i + 2];
-		bool radio_checkable = (int)p_items[i + 2] == Item::CHECKABLE_TYPE_RADIO_BUTTON;
-		bool checked = p_items[i + 3];
-		bool disabled = p_items[i + 4];
-
-		int id = p_items[i + 5];
-		int accel = p_items[i + 6];
-		Variant meta = p_items[i + 7];
-		String subm = p_items[i + 8];
-		bool sep = p_items[i + 9];
-
-		int idx = get_item_count();
-		add_item(text, id);
-		set_item_icon(idx, icon);
-		if (checkable) {
-			if (radio_checkable) {
-				set_item_as_radio_checkable(idx, true);
-			} else {
-				set_item_as_checkable(idx, true);
-			}
-		}
-		set_item_checked(idx, checked);
-		set_item_disabled(idx, disabled);
-		set_item_id(idx, id);
-		set_item_metadata(idx, meta);
-		set_item_as_separator(idx, sep);
-		set_item_accelerator(idx, accel);
-		set_item_submenu(idx, subm);
-	}
-}
-
 // Hide on item selection determines whether or not the popup will close after item selection
 void PopupMenu::set_hide_on_item_selection(bool p_enabled) {
 	hide_on_item_selection = p_enabled;
@@ -1581,6 +1532,145 @@ void PopupMenu::take_mouse_focus() {
 	}
 }
 
+bool PopupMenu::_set(const StringName &p_name, const Variant &p_value) {
+	Vector<String> components = String(p_name).split("/", true, 2);
+	if (components.size() >= 2 && components[0].begins_with("item_") && components[0].trim_prefix("item_").is_valid_int()) {
+		int item_index = components[0].trim_prefix("item_").to_int();
+		String property = components[1];
+		if (property == "text") {
+			set_item_text(item_index, p_value);
+			return true;
+		} else if (property == "icon") {
+			set_item_icon(item_index, p_value);
+			return true;
+		} else if (property == "checkable") {
+			bool radio_checkable = (int)p_value == Item::CHECKABLE_TYPE_RADIO_BUTTON;
+			if (radio_checkable) {
+				set_item_as_radio_checkable(item_index, true);
+			} else {
+				bool checkable = p_value;
+				set_item_as_checkable(item_index, checkable);
+			}
+			return true;
+		} else if (property == "checked") {
+			set_item_checked(item_index, p_value);
+			return true;
+		} else if (property == "id") {
+			set_item_id(item_index, p_value);
+			return true;
+		} else if (components[1] == "disabled") {
+			set_item_disabled(item_index, p_value);
+			return true;
+		} else if (property == "separator") {
+			set_item_as_separator(item_index, p_value);
+			return true;
+		}
+	}
+#ifndef DISABLE_DEPRECATED
+	// Compatibility.
+	if (p_name == "items") {
+		Array arr = p_value;
+		ERR_FAIL_COND_V(arr.size() % 10, false);
+		clear();
+
+		for (int i = 0; i < arr.size(); i += 10) {
+			String text = arr[i + 0];
+			Ref<Texture2D> icon = arr[i + 1];
+			// For compatibility, use false/true for no/checkbox and integers for other values
+			bool checkable = arr[i + 2];
+			bool radio_checkable = (int)arr[i + 2] == Item::CHECKABLE_TYPE_RADIO_BUTTON;
+			bool checked = arr[i + 3];
+			bool disabled = arr[i + 4];
+
+			int id = arr[i + 5];
+			int accel = arr[i + 6];
+			Variant meta = arr[i + 7];
+			String subm = arr[i + 8];
+			bool sep = arr[i + 9];
+
+			int idx = get_item_count();
+			add_item(text, id);
+			set_item_icon(idx, icon);
+			if (checkable) {
+				if (radio_checkable) {
+					set_item_as_radio_checkable(idx, true);
+				} else {
+					set_item_as_checkable(idx, true);
+				}
+			}
+			set_item_checked(idx, checked);
+			set_item_disabled(idx, disabled);
+			set_item_id(idx, id);
+			set_item_metadata(idx, meta);
+			set_item_as_separator(idx, sep);
+			set_item_accelerator(idx, accel);
+			set_item_submenu(idx, subm);
+		}
+	}
+#endif
+	return false;
+}
+
+bool PopupMenu::_get(const StringName &p_name, Variant &r_ret) const {
+	Vector<String> components = String(p_name).split("/", true, 2);
+	if (components.size() >= 2 && components[0].begins_with("item_") && components[0].trim_prefix("item_").is_valid_int()) {
+		int item_index = components[0].trim_prefix("item_").to_int();
+		String property = components[1];
+		if (property == "text") {
+			r_ret = get_item_text(item_index);
+			return true;
+		} else if (property == "icon") {
+			r_ret = get_item_icon(item_index);
+			return true;
+		} else if (property == "checkable") {
+			r_ret = this->items[item_index].checkable_type;
+			return true;
+		} else if (property == "checked") {
+			r_ret = is_item_checked(item_index);
+			return true;
+		} else if (property == "id") {
+			r_ret = get_item_id(item_index);
+			return true;
+		} else if (components[1] == "disabled") {
+			r_ret = is_item_disabled(item_index);
+			return true;
+		} else if (property == "separator") {
+			r_ret = is_item_separator(item_index);
+			return true;
+		}
+	}
+	return false;
+}
+
+void PopupMenu::_get_property_list(List<PropertyInfo> *p_list) const {
+	for (int i = 0; i < items.size(); i++) {
+		p_list->push_back(PropertyInfo(Variant::STRING, vformat("item_%d/text", i)));
+
+		PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("item_%d/icon", i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D");
+		pi.usage &= ~(get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0);
+		p_list->push_back(pi);
+
+		pi = PropertyInfo(Variant::INT, vformat("item_%d/checkable", i), PROPERTY_HINT_ENUM, "No,As checkbox,As radio button");
+		pi.usage &= ~(!is_item_checkable(i) ? PROPERTY_USAGE_STORAGE : 0);
+		p_list->push_back(pi);
+
+		pi = PropertyInfo(Variant::BOOL, vformat("item_%d/checked", i));
+		pi.usage &= ~(!is_item_checked(i) ? PROPERTY_USAGE_STORAGE : 0);
+		p_list->push_back(pi);
+
+		pi = PropertyInfo(Variant::INT, vformat("item_%d/id", i), PROPERTY_HINT_RANGE, "1,10,1,or_greater");
+		p_list->push_back(pi);
+
+		pi = PropertyInfo(Variant::BOOL, vformat("item_%d/disabled", i));
+		pi.usage &= ~(!is_item_disabled(i) ? PROPERTY_USAGE_STORAGE : 0);
+		p_list->push_back(pi);
+
+		pi = PropertyInfo(Variant::BOOL, vformat("item_%d/separator", i));
+		pi.usage &= ~(!is_item_separator(i) ? PROPERTY_USAGE_STORAGE : 0);
+		p_list->push_back(pi);
+	}
+}
+
 void PopupMenu::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("add_item", "label", "id", "accel"), &PopupMenu::add_item, DEFVAL(-1), DEFVAL(0));
 	ClassDB::bind_method(D_METHOD("add_icon_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_item, DEFVAL(-1), DEFVAL(0));
@@ -1643,6 +1733,7 @@ void PopupMenu::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_item_shortcut", "idx"), &PopupMenu::get_item_shortcut);
 
 	ClassDB::bind_method(D_METHOD("get_current_index"), &PopupMenu::get_current_index);
+	ClassDB::bind_method(D_METHOD("set_item_count"), &PopupMenu::set_item_count);
 	ClassDB::bind_method(D_METHOD("get_item_count"), &PopupMenu::get_item_count);
 
 	ClassDB::bind_method(D_METHOD("remove_item", "idx"), &PopupMenu::remove_item);
@@ -1650,9 +1741,6 @@ void PopupMenu::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("add_separator", "label", "id"), &PopupMenu::add_separator, DEFVAL(String()), DEFVAL(-1));
 	ClassDB::bind_method(D_METHOD("clear"), &PopupMenu::clear);
 
-	ClassDB::bind_method(D_METHOD("_set_items"), &PopupMenu::_set_items);
-	ClassDB::bind_method(D_METHOD("_get_items"), &PopupMenu::_get_items);
-
 	ClassDB::bind_method(D_METHOD("set_hide_on_item_selection", "enable"), &PopupMenu::set_hide_on_item_selection);
 	ClassDB::bind_method(D_METHOD("is_hide_on_item_selection"), &PopupMenu::is_hide_on_item_selection);
 
@@ -1668,13 +1756,14 @@ void PopupMenu::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_allow_search", "allow"), &PopupMenu::set_allow_search);
 	ClassDB::bind_method(D_METHOD("get_allow_search"), &PopupMenu::get_allow_search);
 
-	ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
 	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"), "set_submenu_popup_delay", "get_submenu_popup_delay");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_search"), "set_allow_search", "get_allow_search");
 
+	ADD_ARRAY_COUNT("Items", "items_count", "set_item_count", "get_item_count", "item_");
+
 	ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "id")));
 	ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "id")));
 	ADD_SIGNAL(MethodInfo("index_pressed", PropertyInfo(Variant::INT, "index")));

+ 5 - 3
scene/gui/popup_menu.h

@@ -117,9 +117,6 @@ class PopupMenu : public Popup {
 	bool hide_on_multistate_item_selection = false;
 	Vector2 moved;
 
-	Array _get_items() const;
-	void _set_items(const Array &p_items);
-
 	Map<Ref<Shortcut>, int> shortcut_refcount;
 
 	void _ref_shortcut(Ref<Shortcut> p_sc);
@@ -141,6 +138,9 @@ class PopupMenu : public Popup {
 
 protected:
 	void _notification(int p_what);
+	bool _set(const StringName &p_name, const Variant &p_value);
+	bool _get(const StringName &p_name, Variant &r_ret) const;
+	void _get_property_list(List<PropertyInfo> *p_list) const;
 	static void _bind_methods();
 
 public:
@@ -213,6 +213,8 @@ public:
 	int get_item_state(int p_idx) const;
 
 	int get_current_index() const;
+
+	void set_item_count(int p_count);
 	int get_item_count() const;
 
 	bool activate_item_by_event(const Ref<InputEvent> &p_event, bool p_for_global_only = false);