summaryrefslogtreecommitdiffstats
path: root/scene/gui/popup_menu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui/popup_menu.cpp')
-rw-r--r--scene/gui/popup_menu.cpp162
1 files changed, 76 insertions, 86 deletions
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index fda909af79..e69f713798 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -111,13 +111,10 @@ String PopupMenu::bind_global_menu() {
ds->global_menu_add_separator(global_menu_name);
} else {
int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), item.shortcut_is_global ? callable_mp(this, &PopupMenu::activate_item) : Callable(), i);
- if (!item.submenu.is_empty()) {
- PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(item.submenu));
- if (pm) {
- String submenu_name = pm->bind_global_menu();
- ds->global_menu_set_item_submenu(global_menu_name, index, submenu_name);
- item.submenu_bound = true;
- }
+ if (item.submenu) {
+ String submenu_name = item.submenu->bind_global_menu();
+ ds->global_menu_set_item_submenu(global_menu_name, index, submenu_name);
+ item.submenu_bound = true;
}
if (item.checkable_type == Item::CHECKABLE_TYPE_CHECK_BOX) {
ds->global_menu_set_item_checkable(global_menu_name, index, true);
@@ -158,11 +155,8 @@ void PopupMenu::unbind_global_menu() {
for (int i = 0; i < items.size(); i++) {
Item &item = items.write[i];
- if (!item.submenu.is_empty()) {
- PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(item.submenu));
- if (pm) {
- pm->unbind_global_menu();
- }
+ if (item.submenu) {
+ item.submenu->unbind_global_menu();
item.submenu_bound = false;
}
}
@@ -251,7 +245,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
accel_max_w = MAX(accel_w, accel_max_w);
}
- if (!items[i].submenu.is_empty()) {
+ if (items[i].submenu) {
item_size.width += theme_cache.submenu->get_width();
}
@@ -335,10 +329,7 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
}
void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) {
- Node *n = get_node_or_null(items[p_over].submenu);
- ERR_FAIL_NULL_MSG(n, "Item subnode does not exist: '" + items[p_over].submenu + "'.");
- Popup *submenu_popup = Object::cast_to<Popup>(n);
- ERR_FAIL_NULL_MSG(submenu_popup, "Item subnode is not a Popup: '" + items[p_over].submenu + "'.");
+ Popup *submenu_popup = items[p_over].submenu;
if (submenu_popup->is_visible()) {
return; // Already visible.
}
@@ -551,7 +542,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
}
}
} else if (p_event->is_action("ui_right", true) && p_event->is_pressed()) {
- if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator && !items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) {
+ if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator && items[mouse_over].submenu && submenu_over != mouse_over) {
_activate_submenu(mouse_over, true);
set_input_as_handled();
} else {
@@ -564,7 +555,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
}
} else if (p_event->is_action("ui_accept", true) && p_event->is_pressed()) {
if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator) {
- if (!items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) {
+ if (items[mouse_over].submenu && submenu_over != mouse_over) {
_activate_submenu(mouse_over, true);
} else {
activate_item(mouse_over);
@@ -620,7 +611,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
return;
}
- if (!items[over].submenu.is_empty()) {
+ if (items[over].submenu) {
_activate_submenu(over);
return;
}
@@ -657,7 +648,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
return;
}
- if (!items[over].submenu.is_empty() && submenu_over != over) {
+ if (items[over].submenu && submenu_over != over) {
submenu_over = over;
submenu_timer->start();
}
@@ -858,7 +849,7 @@ void PopupMenu::_draw_items() {
}
// Submenu arrow on right hand side.
- if (!items[i].submenu.is_empty()) {
+ if (items[i].submenu) {
if (rtl) {
submenu->draw(ci, Point2(scroll_width + theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
} else {
@@ -974,11 +965,13 @@ void PopupMenu::_menu_changed() {
void PopupMenu::add_child_notify(Node *p_child) {
Window::add_child_notify(p_child);
- if (Object::cast_to<PopupMenu>(p_child) && !global_menu_name.is_empty()) {
- String node_name = p_child->get_name();
- PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(node_name));
+ PopupMenu *pm = Object::cast_to<PopupMenu>(p_child);
+ if (!pm) {
+ return;
+ }
+ if (!global_menu_name.is_empty()) {
for (int i = 0; i < items.size(); i++) {
- if (items[i].submenu == node_name) {
+ if (items[i].submenu == p_child) {
String submenu_name = pm->bind_global_menu();
DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, i, submenu_name);
items.write[i].submenu_bound = true;
@@ -996,9 +989,8 @@ void PopupMenu::remove_child_notify(Node *p_child) {
return;
}
if (Object::cast_to<PopupMenu>(p_child) && !global_menu_name.is_empty()) {
- String node_name = p_child->get_name();
for (int i = 0; i < items.size(); i++) {
- if (items[i].submenu == node_name) {
+ if (items[i].submenu == p_child) {
DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, i, String());
items.write[i].submenu_bound = false;
}
@@ -1056,7 +1048,7 @@ void PopupMenu::_notification(int p_what) {
} break;
case NOTIFICATION_WM_MOUSE_EXIT: {
- if (mouse_over >= 0 && (items[mouse_over].submenu.is_empty() || submenu_over != -1)) {
+ if (mouse_over >= 0 && (!items[mouse_over].submenu || submenu_over != -1)) {
mouse_over = -1;
control->queue_redraw();
}
@@ -1166,21 +1158,10 @@ void PopupMenu::_notification(int p_what) {
}
for (int i = 0; i < items.size(); i++) {
- if (items[i].submenu.is_empty()) {
+ if (!items[i].submenu) {
continue;
}
-
- Node *n = get_node(items[i].submenu);
- if (!n) {
- continue;
- }
-
- PopupMenu *pm = Object::cast_to<PopupMenu>(n);
- if (!pm || !pm->is_visible()) {
- continue;
- }
-
- pm->hide();
+ items[i].submenu->hide();
}
set_process_internal(false);
@@ -1563,9 +1544,18 @@ void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, cons
}
void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, int p_id) {
- String submenu_name_safe = p_submenu.replace("@", "_"); // Allow special characters for auto-generated names.
- if (submenu_name_safe.validate_node_name() != submenu_name_safe) {
- ERR_FAIL_MSG(vformat("Invalid node name '%s' for a submenu, the following characters are not allowed:\n%s", p_submenu, String::get_invalid_node_name_characters(true)));
+ PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(p_submenu));
+ ERR_FAIL_NULL_MSG(pm, vformat("Child PopupMenu \"%s\" does not exist.", p_submenu));
+
+ add_submenu_node_item(p_label, pm, p_id);
+}
+
+void PopupMenu::add_submenu_node_item(const String &p_label, PopupMenu *p_submenu, int p_id) {
+ ERR_FAIL_NULL(p_submenu);
+
+ if (p_submenu->get_parent() != this) {
+ ERR_FAIL_COND_MSG(p_submenu->get_parent() != nullptr, vformat("The submenu \"%s\" already has a different parent.", p_submenu->get_name()));
+ add_child(p_submenu);
}
Item item;
@@ -1573,17 +1563,15 @@ void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu,
item.xl_text = atr(p_label);
item.id = p_id == -1 ? items.size() : p_id;
item.submenu = p_submenu;
+ item.submenu_name = p_submenu->get_name();
items.push_back(item);
if (!global_menu_name.is_empty()) {
DisplayServer *ds = DisplayServer::get_singleton();
int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1);
- PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(item.submenu)); // Find first menu with this name.
- if (pm) {
- String submenu_name = pm->bind_global_menu();
- ds->global_menu_set_item_submenu(global_menu_name, index, submenu_name);
- items.write[index].submenu_bound = true;
- }
+ String submenu_name = p_submenu->bind_global_menu();
+ ds->global_menu_set_item_submenu(global_menu_name, index, submenu_name);
+ items.write[index].submenu_bound = true;
}
_shape_item(items.size() - 1);
@@ -1804,18 +1792,31 @@ void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) {
}
ERR_FAIL_INDEX(p_idx, items.size());
- if (items[p_idx].submenu == p_submenu) {
+ if (items[p_idx].submenu_name == p_submenu) {
return;
}
- String submenu_name_safe = p_submenu.replace("@", "_"); // Allow special characters for auto-generated names.
- if (submenu_name_safe.validate_node_name() != submenu_name_safe) {
- ERR_FAIL_MSG(vformat("Invalid node name '%s' for a submenu, the following characters are not allowed:\n%s", p_submenu, String::get_invalid_node_name_characters(true)));
+ PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(p_submenu));
+ ERR_FAIL_NULL_MSG(pm, vformat("Child PopupMenu \"%s\" does not exist.", p_submenu));
+
+ set_item_submenu_node(p_idx, pm);
+}
+
+void PopupMenu::set_item_submenu_node(int p_idx, PopupMenu *p_submenu) {
+ ERR_FAIL_NULL(p_submenu);
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
+ ERR_FAIL_INDEX(p_idx, items.size());
+
+ if (p_submenu->get_parent() != this) {
+ ERR_FAIL_COND_MSG(p_submenu->get_parent() != nullptr, vformat("The submenu \"%s\" already has a different parent.", p_submenu->get_name()));
+ add_child(p_submenu);
}
if (!global_menu_name.is_empty()) {
if (items[p_idx].submenu_bound) {
- PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(items[p_idx].submenu));
+ PopupMenu *pm = items[p_idx].submenu;
if (pm) {
DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, p_idx, String());
pm->unbind_global_menu();
@@ -1827,13 +1828,10 @@ void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) {
items.write[p_idx].submenu = p_submenu;
if (!global_menu_name.is_empty()) {
- if (!items[p_idx].submenu.is_empty()) {
- PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(items[p_idx].submenu));
- if (pm) {
- String submenu_name = pm->bind_global_menu();
- DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, p_idx, submenu_name);
- items.write[p_idx].submenu_bound = true;
- }
+ if (items[p_idx].submenu) {
+ String submenu_name = p_submenu->bind_global_menu();
+ DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, p_idx, submenu_name);
+ items.write[p_idx].submenu_bound = true;
}
}
@@ -1937,6 +1935,11 @@ int PopupMenu::get_item_index(int p_id) const {
String PopupMenu::get_item_submenu(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, items.size(), "");
+ return items[p_idx].submenu_name;
+}
+
+PopupMenu *PopupMenu::get_item_submenu_node(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, items.size(), nullptr);
return items[p_idx].submenu;
}
@@ -2333,18 +2336,8 @@ bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_fo
return true;
}
- if (!items[i].submenu.is_empty()) {
- Node *n = get_node(items[i].submenu);
- if (!n) {
- continue;
- }
-
- PopupMenu *pm = Object::cast_to<PopupMenu>(n);
- if (!pm) {
- continue;
- }
-
- if (pm->activate_item_by_event(p_event, p_for_global_only)) {
+ if (items[i].submenu) {
+ if (items[i].submenu->activate_item_by_event(p_event, p_for_global_only)) {
return true;
}
}
@@ -2458,12 +2451,9 @@ void PopupMenu::clear(bool p_free_submenus) {
_unref_shortcut(I.shortcut);
}
- if (p_free_submenus && !I.submenu.is_empty()) {
- Node *submenu = get_node_or_null(I.submenu);
- if (submenu) {
- remove_child(submenu);
- submenu->queue_free();
- }
+ if (p_free_submenus && I.submenu) {
+ remove_child(I.submenu);
+ I.submenu->queue_free();
}
}
@@ -2471,11 +2461,8 @@ void PopupMenu::clear(bool p_free_submenus) {
DisplayServer *ds = DisplayServer::get_singleton();
for (int i = items.size() - 1; i >= 0; i--) {
Item &item = items.write[i];
- if (!item.submenu.is_empty()) {
- PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(item.submenu));
- if (pm) {
- pm->unbind_global_menu();
- }
+ if (item.submenu) {
+ item.submenu->unbind_global_menu();
item.submenu_bound = false;
}
ds->global_menu_remove_item(global_menu_name, i);
@@ -2654,6 +2641,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_icon_radio_check_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_radio_check_shortcut, DEFVAL(-1), DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_submenu_item", "label", "submenu", "id"), &PopupMenu::add_submenu_item, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("add_submenu_node_item", "label", "submenu", "id"), &PopupMenu::add_submenu_node_item, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("set_item_text", "index", "text"), &PopupMenu::set_item_text);
ClassDB::bind_method(D_METHOD("set_item_text_direction", "index", "direction"), &PopupMenu::set_item_text_direction);
@@ -2667,6 +2655,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_metadata", "index", "metadata"), &PopupMenu::set_item_metadata);
ClassDB::bind_method(D_METHOD("set_item_disabled", "index", "disabled"), &PopupMenu::set_item_disabled);
ClassDB::bind_method(D_METHOD("set_item_submenu", "index", "submenu"), &PopupMenu::set_item_submenu);
+ ClassDB::bind_method(D_METHOD("set_item_submenu_node", "index", "submenu"), &PopupMenu::set_item_submenu_node);
ClassDB::bind_method(D_METHOD("set_item_as_separator", "index", "enable"), &PopupMenu::set_item_as_separator);
ClassDB::bind_method(D_METHOD("set_item_as_checkable", "index", "enable"), &PopupMenu::set_item_as_checkable);
ClassDB::bind_method(D_METHOD("set_item_as_radio_checkable", "index", "enable"), &PopupMenu::set_item_as_radio_checkable);
@@ -2693,6 +2682,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_metadata", "index"), &PopupMenu::get_item_metadata);
ClassDB::bind_method(D_METHOD("is_item_disabled", "index"), &PopupMenu::is_item_disabled);
ClassDB::bind_method(D_METHOD("get_item_submenu", "index"), &PopupMenu::get_item_submenu);
+ ClassDB::bind_method(D_METHOD("get_item_submenu_node", "index"), &PopupMenu::get_item_submenu_node);
ClassDB::bind_method(D_METHOD("is_item_separator", "index"), &PopupMenu::is_item_separator);
ClassDB::bind_method(D_METHOD("is_item_checkable", "index"), &PopupMenu::is_item_checkable);
ClassDB::bind_method(D_METHOD("is_item_radio_checkable", "index"), &PopupMenu::is_item_radio_checkable);