diff options
Diffstat (limited to 'scene/gui/popup_menu.cpp')
-rw-r--r-- | scene/gui/popup_menu.cpp | 162 |
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); |