diff options
Diffstat (limited to 'scene/gui')
| -rw-r--r-- | scene/gui/code_edit.cpp | 45 | ||||
| -rw-r--r-- | scene/gui/code_edit.h | 1 | ||||
| -rw-r--r-- | scene/gui/dialogs.cpp | 1 | ||||
| -rw-r--r-- | scene/gui/file_dialog.cpp | 16 | ||||
| -rw-r--r-- | scene/gui/file_dialog.h | 2 | ||||
| -rw-r--r-- | scene/gui/menu_bar.cpp | 228 | ||||
| -rw-r--r-- | scene/gui/menu_bar.h | 24 | ||||
| -rw-r--r-- | scene/gui/popup_menu.cpp | 461 | ||||
| -rw-r--r-- | scene/gui/popup_menu.h | 9 | ||||
| -rw-r--r-- | scene/gui/subviewport_container.cpp | 16 | ||||
| -rw-r--r-- | scene/gui/subviewport_container.h | 2 | ||||
| -rw-r--r-- | scene/gui/text_edit.cpp | 18 |
12 files changed, 689 insertions, 134 deletions
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index e7a2a26a29..20fcf9cba7 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -3223,7 +3223,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() { int line_height = get_line_height(); if (GDVIRTUAL_IS_OVERRIDDEN(_filter_code_completion_candidates)) { - code_completion_options.clear(); + Vector<ScriptLanguage::CodeCompletionOption> code_completion_options_new; code_completion_base = ""; /* Build options argument. */ @@ -3273,11 +3273,15 @@ void CodeEdit::_filter_code_completion_candidates_impl() { if (theme_cache.font.is_valid()) { max_width = MAX(max_width, theme_cache.font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + offset); } - code_completion_options.push_back(option); + code_completion_options_new.push_back(option); } + if (_should_reset_selected_option_for_new_options(code_completion_options_new)) { + code_completion_current_selected = 0; + } + code_completion_options = code_completion_options_new; + code_completion_longest_line = MIN(max_width, theme_cache.code_completion_max_width * theme_cache.font_size); - code_completion_current_selected = 0; code_completion_force_item_center = -1; code_completion_active = true; queue_redraw(); @@ -3352,7 +3356,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() { /* For now handle only traditional quoted strings. */ bool single_quote = in_string != -1 && first_quote_col > 0 && delimiters[in_string].start_key == "'"; - code_completion_options.clear(); + Vector<ScriptLanguage::CodeCompletionOption> code_completion_options_new; code_completion_base = string_to_complete; /* Don't autocomplete setting numerical values. */ @@ -3384,7 +3388,8 @@ void CodeEdit::_filter_code_completion_candidates_impl() { if (string_to_complete.length() == 0) { option.get_option_characteristics(string_to_complete); - code_completion_options.push_back(option); + code_completion_options_new.push_back(option); + if (theme_cache.font.is_valid()) { max_width = MAX(max_width, theme_cache.font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + offset); } @@ -3459,7 +3464,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() { } } - code_completion_options.push_back(option); + code_completion_options_new.push_back(option); if (theme_cache.font.is_valid()) { max_width = MAX(max_width, theme_cache.font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + offset); } @@ -3467,26 +3472,46 @@ void CodeEdit::_filter_code_completion_candidates_impl() { } /* No options to complete, cancel. */ - if (code_completion_options.size() == 0) { + if (code_completion_options_new.size() == 0) { cancel_code_completion(); return; } /* A perfect match, stop completion. */ - if (code_completion_options.size() == 1 && string_to_complete == code_completion_options[0].display) { + if (code_completion_options_new.size() == 1 && string_to_complete == code_completion_options_new[0].display) { cancel_code_completion(); return; } - code_completion_options.sort_custom<CodeCompletionOptionCompare>(); + code_completion_options_new.sort_custom<CodeCompletionOptionCompare>(); + if (_should_reset_selected_option_for_new_options(code_completion_options_new)) { + code_completion_current_selected = 0; + } + code_completion_options = code_completion_options_new; code_completion_longest_line = MIN(max_width, theme_cache.code_completion_max_width * theme_cache.font_size); - code_completion_current_selected = 0; code_completion_force_item_center = -1; code_completion_active = true; queue_redraw(); } +// Assumes both the new_options and the code_completion_options are sorted. +bool CodeEdit::_should_reset_selected_option_for_new_options(const Vector<ScriptLanguage::CodeCompletionOption> &p_new_options) { + if (code_completion_current_selected >= p_new_options.size()) { + return true; + } + + for (int i = 0; i < code_completion_options.size() && i < p_new_options.size(); i++) { + if (i > code_completion_current_selected) { + return false; + } + if (code_completion_options[i].display != p_new_options[i].display) { + return true; + } + } + return false; +} + void CodeEdit::_lines_edited_from(int p_from_line, int p_to_line) { _update_delimiter_cache(p_from_line, p_to_line); diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index 97c435b52d..4b0629d29d 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -222,6 +222,7 @@ private: void _update_scroll_selected_line(float p_mouse_y); void _filter_code_completion_candidates_impl(); + bool _should_reset_selected_option_for_new_options(const Vector<ScriptLanguage::CodeCompletionOption> &p_new_options); /* Line length guidelines */ TypedArray<int> line_length_guideline_columns; diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index aff0ed6f06..957a8f276e 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -405,6 +405,7 @@ AcceptDialog::AcceptDialog() { set_transient(true); set_exclusive(true); set_clamp_to_embedder(true); + set_keep_title_visible(true); bg_panel = memnew(Panel); add_child(bg_panel, false, INTERNAL_MODE_FRONT); diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 3857281a66..be05273a09 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -56,6 +56,12 @@ void FileDialog::_focus_file_text() { } void FileDialog::popup(const Rect2i &p_rect) { +#ifdef TOOLS_ENABLED + if (is_part_of_edited_scene()) { + ConfirmationDialog::popup(p_rect); + } +#endif + if (access == ACCESS_FILESYSTEM && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) { DisplayServer::get_singleton()->file_dialog_show(get_title(), dir->get_text(), file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), filters, callable_mp(this, &FileDialog::_native_dialog_cb)); } else { @@ -64,6 +70,13 @@ void FileDialog::popup(const Rect2i &p_rect) { } void FileDialog::set_visible(bool p_visible) { +#ifdef TOOLS_ENABLED + if (is_part_of_edited_scene()) { + ConfirmationDialog::set_visible(p_visible); + return; + } +#endif + if (access == ACCESS_FILESYSTEM && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) { if (p_visible) { DisplayServer::get_singleton()->file_dialog_show(get_title(), dir->get_text(), file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), filters, callable_mp(this, &FileDialog::_native_dialog_cb)); @@ -73,7 +86,7 @@ void FileDialog::set_visible(bool p_visible) { } } -void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files) { +void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files, int p_filter) { if (p_ok) { if (p_files.size() > 0) { String f = p_files[0]; @@ -90,6 +103,7 @@ void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files) { } file->set_text(f); dir->set_text(f.get_base_dir()); + _filter_selected(p_filter); } } else { file->set_text(""); diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index 1a87b79fdd..8ae84fc9dc 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -159,7 +159,7 @@ private: virtual void shortcut_input(const Ref<InputEvent> &p_event) override; - void _native_dialog_cb(bool p_ok, const Vector<String> &p_files); + void _native_dialog_cb(bool p_ok, const Vector<String> &p_files, int p_filter); bool _is_open_should_be_disabled(); diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp index 13a42d0407..371d6c69af 100644 --- a/scene/gui/menu_bar.cpp +++ b/scene/gui/menu_bar.cpp @@ -202,52 +202,6 @@ void MenuBar::_popup_visibility_changed(bool p_visible) { } } -void MenuBar::_update_submenu(const String &p_menu_name, PopupMenu *p_child) { - int count = p_child->get_item_count(); - global_menus.insert(p_menu_name); - for (int i = 0; i < count; i++) { - if (p_child->is_item_separator(i)) { - DisplayServer::get_singleton()->global_menu_add_separator(p_menu_name); - } else if (!p_child->get_item_submenu(i).is_empty()) { - Node *n = p_child->get_node_or_null(p_child->get_item_submenu(i)); - ERR_FAIL_NULL_MSG(n, "Item subnode does not exist: '" + p_child->get_item_submenu(i) + "'."); - PopupMenu *pm = Object::cast_to<PopupMenu>(n); - ERR_FAIL_NULL_MSG(pm, "Item subnode is not a PopupMenu: '" + p_child->get_item_submenu(i) + "'."); - - DisplayServer::get_singleton()->global_menu_add_submenu_item(p_menu_name, atr(p_child->get_item_text(i)), p_menu_name + "/" + itos(i)); - _update_submenu(p_menu_name + "/" + itos(i), pm); - } else { - int index = DisplayServer::get_singleton()->global_menu_add_item(p_menu_name, atr(p_child->get_item_text(i)), callable_mp(p_child, &PopupMenu::activate_item), Callable(), i); - - if (p_child->is_item_checkable(i)) { - DisplayServer::get_singleton()->global_menu_set_item_checkable(p_menu_name, index, true); - } - if (p_child->is_item_radio_checkable(i)) { - DisplayServer::get_singleton()->global_menu_set_item_radio_checkable(p_menu_name, index, true); - } - DisplayServer::get_singleton()->global_menu_set_item_checked(p_menu_name, index, p_child->is_item_checked(i)); - DisplayServer::get_singleton()->global_menu_set_item_disabled(p_menu_name, index, p_child->is_item_disabled(i)); - DisplayServer::get_singleton()->global_menu_set_item_max_states(p_menu_name, index, p_child->get_item_max_states(i)); - DisplayServer::get_singleton()->global_menu_set_item_icon(p_menu_name, index, p_child->get_item_icon(i)); - DisplayServer::get_singleton()->global_menu_set_item_state(p_menu_name, index, p_child->get_item_state(i)); - DisplayServer::get_singleton()->global_menu_set_item_indentation_level(p_menu_name, index, p_child->get_item_indent(i)); - DisplayServer::get_singleton()->global_menu_set_item_tooltip(p_menu_name, index, p_child->get_item_tooltip(i)); - if (!p_child->is_item_shortcut_disabled(i) && p_child->get_item_shortcut(i).is_valid() && p_child->get_item_shortcut(i)->has_valid_event()) { - Array events = p_child->get_item_shortcut(i)->get_events(); - for (int j = 0; j < events.size(); j++) { - Ref<InputEventKey> ie = events[j]; - if (ie.is_valid()) { - DisplayServer::get_singleton()->global_menu_set_item_accelerator(p_menu_name, index, ie->get_keycode_with_modifiers()); - break; - } - } - } else if (p_child->get_item_accelerator(i) != Key::NONE) { - DisplayServer::get_singleton()->global_menu_set_item_accelerator(p_menu_name, i, p_child->get_item_accelerator(i)); - } - } - } -} - bool MenuBar::is_native_menu() const { #ifdef TOOLS_ENABLED if (is_part_of_edited_scene()) { @@ -258,52 +212,67 @@ bool MenuBar::is_native_menu() const { return (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU) && is_native); } -void MenuBar::_clear_menu() { +String MenuBar::bind_global_menu() { +#ifdef TOOLS_ENABLED + if (is_part_of_edited_scene()) { + return String(); + } +#endif if (!DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { - return; + return String(); } - // Remove root menu items. - int count = DisplayServer::get_singleton()->global_menu_get_item_count("_main"); - for (int i = count - 1; i >= 0; i--) { - if (global_menus.has(DisplayServer::get_singleton()->global_menu_get_item_submenu("_main", i))) { - DisplayServer::get_singleton()->global_menu_remove_item("_main", i); + if (!global_menu_name.is_empty()) { + return global_menu_name; // Already bound. + } + + DisplayServer *ds = DisplayServer::get_singleton(); + global_menu_name = "__MenuBar#" + itos(get_instance_id()); + + int global_start_idx = -1; + int count = ds->global_menu_get_item_count("_main"); + String prev_tag; + for (int i = 0; i < count; i++) { + String tag = ds->global_menu_get_item_tag("_main", i).operator String().get_slice("#", 1); + if (!tag.is_empty() && tag != prev_tag) { + if (i >= start_index) { + global_start_idx = i; + break; + } } + prev_tag = tag; } - // Erase submenu contents. - for (const String &E : global_menus) { - DisplayServer::get_singleton()->global_menu_clear(E); + if (global_start_idx == -1) { + global_start_idx = count; } - global_menus.clear(); -} -void MenuBar::_update_menu() { - _clear_menu(); + Vector<PopupMenu *> popups = _get_popups(); + for (int i = 0; i < menu_cache.size(); i++) { + String submenu_name = popups[i]->bind_global_menu(); + int index = ds->global_menu_add_submenu_item("_main", menu_cache[i].name, submenu_name, global_start_idx + i); + ds->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(i)); + ds->global_menu_set_item_hidden("_main", index, menu_cache[i].hidden); + ds->global_menu_set_item_disabled("_main", index, menu_cache[i].disabled); + ds->global_menu_set_item_tooltip("_main", index, menu_cache[i].tooltip); + } + + return global_menu_name; +} - if (!is_visible_in_tree()) { +void MenuBar::unbind_global_menu() { + if (global_menu_name.is_empty()) { return; } - int index = start_index; - if (is_native_menu()) { - Vector<PopupMenu *> popups = _get_popups(); - String root_name = "MenuBar<" + String::num_int64((uint64_t)this, 16) + ">"; - for (int i = 0; i < popups.size(); i++) { - if (menu_cache[i].hidden) { - continue; - } - String menu_name = atr(String(popups[i]->get_meta("_menu_name", popups[i]->get_name()))); - - index = DisplayServer::get_singleton()->global_menu_add_submenu_item("_main", menu_name, root_name + "/" + itos(i), index); - if (menu_cache[i].disabled) { - DisplayServer::get_singleton()->global_menu_set_item_disabled("_main", index, true); - } - _update_submenu(root_name + "/" + itos(i), popups[i]); - index++; - } + DisplayServer *ds = DisplayServer::get_singleton(); + int global_start = _find_global_start_index(); + Vector<PopupMenu *> popups = _get_popups(); + for (int i = menu_cache.size() - 1; i >= 0; i--) { + popups[i]->unbind_global_menu(); + ds->global_menu_remove_item("_main", global_start + i); } - update_minimum_size(); - queue_redraw(); + + global_menu_name = String(); } void MenuBar::_notification(int p_what) { @@ -312,25 +281,43 @@ void MenuBar::_notification(int p_what) { if (get_menu_count() > 0) { _refresh_menu_names(); } + if (is_native_menu()) { + bind_global_menu(); + } } break; case NOTIFICATION_EXIT_TREE: { - _clear_menu(); + unbind_global_menu(); } break; case NOTIFICATION_MOUSE_EXIT: { focused_menu = -1; selected_menu = -1; queue_redraw(); } break; - case NOTIFICATION_TRANSLATION_CHANGED: + case NOTIFICATION_TRANSLATION_CHANGED: { + DisplayServer *ds = DisplayServer::get_singleton(); + bool is_global = !global_menu_name.is_empty(); + int global_start = _find_global_start_index(); + for (int i = 0; i < menu_cache.size(); i++) { + shape(menu_cache.write[i]); + if (is_global) { + ds->global_menu_set_item_text("_main", global_start + i, atr(menu_cache[i].name)); + } + } + } break; case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_THEME_CHANGED: { for (int i = 0; i < menu_cache.size(); i++) { shape(menu_cache.write[i]); } - _update_menu(); } break; case NOTIFICATION_VISIBILITY_CHANGED: { - _update_menu(); + if (is_native_menu()) { + if (is_visible_in_tree()) { + bind_global_menu(); + } else { + unbind_global_menu(); + } + } } break; case NOTIFICATION_DRAW: { if (is_native_menu()) { @@ -512,14 +499,20 @@ void MenuBar::shape(Menu &p_menu) { } void MenuBar::_refresh_menu_names() { + DisplayServer *ds = DisplayServer::get_singleton(); + bool is_global = !global_menu_name.is_empty(); + int global_start = _find_global_start_index(); + Vector<PopupMenu *> popups = _get_popups(); for (int i = 0; i < popups.size(); i++) { if (!popups[i]->has_meta("_menu_name") && String(popups[i]->get_name()) != get_menu_title(i)) { menu_cache.write[i].name = popups[i]->get_name(); shape(menu_cache.write[i]); + if (is_global) { + ds->global_menu_set_item_text("_main", global_start + i, atr(menu_cache[i].name)); + } } } - _update_menu(); } Vector<PopupMenu *> MenuBar::_get_popups() const { @@ -560,11 +553,14 @@ void MenuBar::add_child_notify(Node *p_child) { menu_cache.push_back(menu); p_child->connect("renamed", callable_mp(this, &MenuBar::_refresh_menu_names)); - p_child->connect("menu_changed", callable_mp(this, &MenuBar::_update_menu)); p_child->connect("about_to_popup", callable_mp(this, &MenuBar::_popup_visibility_changed).bind(true)); p_child->connect("popup_hide", callable_mp(this, &MenuBar::_popup_visibility_changed).bind(false)); - _update_menu(); + if (!global_menu_name.is_empty()) { + String submenu_name = pm->bind_global_menu(); + int index = DisplayServer::get_singleton()->global_menu_add_submenu_item("_main", atr(menu.name), submenu_name, _find_global_start_index() + menu_cache.size() - 1); + DisplayServer::get_singleton()->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(menu_cache.size() - 1)); + } } void MenuBar::move_child_notify(Node *p_child) { @@ -586,9 +582,20 @@ void MenuBar::move_child_notify(Node *p_child) { } Menu menu = menu_cache[old_idx]; menu_cache.remove_at(old_idx); - menu_cache.insert(get_menu_idx_from_control(pm), menu); + int new_idx = get_menu_idx_from_control(pm); + menu_cache.insert(new_idx, menu); - _update_menu(); + if (!global_menu_name.is_empty()) { + int global_start = _find_global_start_index(); + if (old_idx != -1) { + DisplayServer::get_singleton()->global_menu_remove_item("_main", global_start + old_idx); + } + if (new_idx != -1) { + String submenu_name = pm->bind_global_menu(); + int index = DisplayServer::get_singleton()->global_menu_add_submenu_item("_main", atr(menu.name), submenu_name, global_start + new_idx); + DisplayServer::get_singleton()->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(new_idx)); + } + } } void MenuBar::remove_child_notify(Node *p_child) { @@ -603,15 +610,17 @@ void MenuBar::remove_child_notify(Node *p_child) { menu_cache.remove_at(idx); + if (!global_menu_name.is_empty()) { + pm->unbind_global_menu(); + DisplayServer::get_singleton()->global_menu_remove_item("_main", _find_global_start_index() + idx); + } + p_child->remove_meta("_menu_name"); p_child->remove_meta("_menu_tooltip"); p_child->disconnect("renamed", callable_mp(this, &MenuBar::_refresh_menu_names)); - p_child->disconnect("menu_changed", callable_mp(this, &MenuBar::_update_menu)); p_child->disconnect("about_to_popup", callable_mp(this, &MenuBar::_popup_visibility_changed)); p_child->disconnect("popup_hide", callable_mp(this, &MenuBar::_popup_visibility_changed)); - - _update_menu(); } void MenuBar::_bind_methods() { @@ -699,7 +708,8 @@ void MenuBar::set_text_direction(Control::TextDirection p_text_direction) { ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); if (text_direction != p_text_direction) { text_direction = p_text_direction; - _update_menu(); + update_minimum_size(); + queue_redraw(); } } @@ -710,7 +720,8 @@ Control::TextDirection MenuBar::get_text_direction() const { void MenuBar::set_language(const String &p_language) { if (language != p_language) { language = p_language; - _update_menu(); + update_minimum_size(); + queue_redraw(); } } @@ -732,7 +743,10 @@ bool MenuBar::is_flat() const { void MenuBar::set_start_index(int p_index) { if (start_index != p_index) { start_index = p_index; - _update_menu(); + if (!global_menu_name.is_empty()) { + unbind_global_menu(); + bind_global_menu(); + } } } @@ -742,11 +756,12 @@ int MenuBar::get_start_index() const { void MenuBar::set_prefer_global_menu(bool p_enabled) { if (is_native != p_enabled) { + is_native = p_enabled; if (is_native) { - _clear_menu(); + bind_global_menu(); + } else { + unbind_global_menu(); } - is_native = p_enabled; - _update_menu(); } } @@ -790,7 +805,9 @@ void MenuBar::set_menu_title(int p_menu, const String &p_title) { } menu_cache.write[p_menu].name = p_title; shape(menu_cache.write[p_menu]); - _update_menu(); + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_text("_main", _find_global_start_index() + p_menu, atr(menu_cache[p_menu].name)); + } } String MenuBar::get_menu_title(int p_menu) const { @@ -802,7 +819,10 @@ void MenuBar::set_menu_tooltip(int p_menu, const String &p_tooltip) { ERR_FAIL_INDEX(p_menu, menu_cache.size()); PopupMenu *pm = get_menu_popup(p_menu); pm->set_meta("_menu_tooltip", p_tooltip); - menu_cache.write[p_menu].name = p_tooltip; + menu_cache.write[p_menu].tooltip = p_tooltip; + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_tooltip("_main", _find_global_start_index() + p_menu, p_tooltip); + } } String MenuBar::get_menu_tooltip(int p_menu) const { @@ -813,7 +833,9 @@ String MenuBar::get_menu_tooltip(int p_menu) const { void MenuBar::set_menu_disabled(int p_menu, bool p_disabled) { ERR_FAIL_INDEX(p_menu, menu_cache.size()); menu_cache.write[p_menu].disabled = p_disabled; - _update_menu(); + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_disabled("_main", _find_global_start_index() + p_menu, p_disabled); + } } bool MenuBar::is_menu_disabled(int p_menu) const { @@ -824,7 +846,9 @@ bool MenuBar::is_menu_disabled(int p_menu) const { void MenuBar::set_menu_hidden(int p_menu, bool p_hidden) { ERR_FAIL_INDEX(p_menu, menu_cache.size()); menu_cache.write[p_menu].hidden = p_hidden; - _update_menu(); + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_hidden("_main", _find_global_start_index() + p_menu, p_hidden); + } } bool MenuBar::is_menu_hidden(int p_menu) const { diff --git a/scene/gui/menu_bar.h b/scene/gui/menu_bar.h index 4d6e76d9b6..ba4df5f229 100644 --- a/scene/gui/menu_bar.h +++ b/scene/gui/menu_bar.h @@ -66,7 +66,6 @@ class MenuBar : public Control { } }; Vector<Menu> menu_cache; - HashSet<String> global_menus; int focused_menu = -1; int selected_menu = -1; @@ -114,9 +113,23 @@ class MenuBar : public Control { void _open_popup(int p_index, bool p_focus_item = false); void _popup_visibility_changed(bool p_visible); - void _update_submenu(const String &p_menu_name, PopupMenu *p_child); - void _clear_menu(); - void _update_menu(); + + String global_menu_name; + + int _find_global_start_index() { + if (global_menu_name.is_empty()) { + return -1; + } + + DisplayServer *ds = DisplayServer::get_singleton(); + int count = ds->global_menu_get_item_count("_main"); + for (int i = 0; i < count; i++) { + if (ds->global_menu_get_item_tag("_main", i).operator String().begins_with(global_menu_name)) { + return i; + } + } + return -1; + } protected: virtual void shortcut_input(const Ref<InputEvent> &p_event) override; @@ -130,6 +143,9 @@ protected: public: virtual void gui_input(const Ref<InputEvent> &p_event) override; + String bind_global_menu(); + void unbind_global_menu(); + void set_switch_on_hover(bool p_enabled); bool is_switch_on_hover(); void set_disable_shortcuts(bool p_disabled); diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 54fd8b8745..e3b0a18325 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -40,6 +40,86 @@ #include "scene/gui/menu_bar.h" #include "scene/theme/theme_db.h" +String PopupMenu::bind_global_menu() { +#ifdef TOOLS_ENABLED + if (is_part_of_edited_scene()) { + return String(); + } +#endif + if (!DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { + return String(); + } + + if (!global_menu_name.is_empty()) { + return global_menu_name; // Already bound; + } + + DisplayServer *ds = DisplayServer::get_singleton(); + global_menu_name = "__PopupMenu#" + itos(get_instance_id()); + ds->global_menu_set_popup_callbacks(global_menu_name, callable_mp(this, &PopupMenu::_about_to_popup), callable_mp(this, &PopupMenu::_about_to_close)); + for (int i = 0; i < items.size(); i++) { + Item &item = items.write[i]; + if (item.separator) { + ds->global_menu_add_separator(global_menu_name); + } else { + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, 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.checkable_type == Item::CHECKABLE_TYPE_CHECK_BOX) { + ds->global_menu_set_item_checkable(global_menu_name, index, true); + } else if (item.checkable_type == Item::CHECKABLE_TYPE_RADIO_BUTTON) { + ds->global_menu_set_item_radio_checkable(global_menu_name, index, true); + } + ds->global_menu_set_item_checked(global_menu_name, index, item.checked); + ds->global_menu_set_item_disabled(global_menu_name, index, item.disabled); + ds->global_menu_set_item_max_states(global_menu_name, index, item.max_states); + ds->global_menu_set_item_icon(global_menu_name, index, item.icon); + ds->global_menu_set_item_state(global_menu_name, index, item.state); + ds->global_menu_set_item_indentation_level(global_menu_name, index, item.indent); + ds->global_menu_set_item_tooltip(global_menu_name, index, item.tooltip); + if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { + Array events = item.shortcut->get_events(); + for (int j = 0; j < events.size(); j++) { + Ref<InputEventKey> ie = events[j]; + if (ie.is_valid()) { + ds->global_menu_set_item_accelerator(global_menu_name, index, ie->get_keycode_with_modifiers()); + break; + } + } + } else if (item.accel != Key::NONE) { + ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + } + } + } + return global_menu_name; +} + +void PopupMenu::unbind_global_menu() { + if (global_menu_name.is_empty()) { + return; + } + + 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(); + } + item.submenu_bound = false; + } + } + DisplayServer::get_singleton()->global_menu_clear(global_menu_name); + + global_menu_name = String(); +} + String PopupMenu::_get_accel_text(const Item &p_item) const { if (p_item.shortcut.is_valid()) { return p_item.shortcut->get_as_text(); @@ -821,11 +901,17 @@ void PopupMenu::_menu_changed() { void PopupMenu::add_child_notify(Node *p_child) { Window::add_child_notify(p_child); - PopupMenu *pm = Object::cast_to<PopupMenu>(p_child); - if (!pm) { - return; + 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)); + for (int i = 0; i < items.size(); i++) { + if (items[i].submenu == node_name) { + 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; + } + } } - p_child->connect("menu_changed", callable_mp(this, &PopupMenu::_menu_changed)); _menu_changed(); } @@ -836,7 +922,16 @@ void PopupMenu::remove_child_notify(Node *p_child) { if (!pm) { return; } - p_child->disconnect("menu_changed", callable_mp(this, &PopupMenu::_menu_changed)); + 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) { + DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, i, String()); + items.write[i].submenu_bound = false; + } + } + pm->unbind_global_menu(); + } _menu_changed(); } @@ -857,9 +952,15 @@ void PopupMenu::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_TRANSLATION_CHANGED: { + DisplayServer *ds = DisplayServer::get_singleton(); + bool is_global = !global_menu_name.is_empty(); for (int i = 0; i < items.size(); i++) { - items.write[i].xl_text = atr(items[i].text); - items.write[i].dirty = true; + Item &item = items.write[i]; + item.xl_text = atr(item.text); + item.dirty = true; + if (is_global) { + ds->global_menu_set_item_text(global_menu_name, i, item.xl_text); + } _shape_item(i); } @@ -1031,6 +1132,14 @@ void PopupMenu::add_item(const String &p_label, int p_id, Key p_accel) { ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (item.accel != Key::NONE) { + ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + } + } + _shape_item(items.size() - 1); control->queue_redraw(); @@ -1045,6 +1154,15 @@ void PopupMenu::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_labe item.icon = p_icon; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (item.accel != Key::NONE) { + ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + } + ds->global_menu_set_item_icon(global_menu_name, index, item.icon); + } + _shape_item(items.size() - 1); control->queue_redraw(); @@ -1059,10 +1177,20 @@ void PopupMenu::add_check_item(const String &p_label, int p_id, Key p_accel) { item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (item.accel != Key::NONE) { + ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + } + ds->global_menu_set_item_checkable(global_menu_name, index, true); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } @@ -1073,10 +1201,22 @@ void PopupMenu::add_icon_check_item(const Ref<Texture2D> &p_icon, const String & item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (item.accel != Key::NONE) { + ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + } + ds->global_menu_set_item_icon(global_menu_name, index, item.icon); + ds->global_menu_set_item_checkable(global_menu_name, index, true); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); + _menu_changed(); } void PopupMenu::add_radio_check_item(const String &p_label, int p_id, Key p_accel) { @@ -1085,10 +1225,20 @@ void PopupMenu::add_radio_check_item(const String &p_label, int p_id, Key p_acce item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (item.accel != Key::NONE) { + ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + } + ds->global_menu_set_item_radio_checkable(global_menu_name, index, true); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } @@ -1099,10 +1249,21 @@ void PopupMenu::add_icon_radio_check_item(const Ref<Texture2D> &p_icon, const St item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (item.accel != Key::NONE) { + ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + } + ds->global_menu_set_item_icon(global_menu_name, index, item.icon); + ds->global_menu_set_item_radio_checkable(global_menu_name, index, true); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } @@ -1113,11 +1274,22 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int item.state = p_default_state; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (item.accel != Key::NONE) { + ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + } + ds->global_menu_set_item_max_states(global_menu_name, index, item.max_states); + ds->global_menu_set_item_state(global_menu_name, index, item.state); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); _menu_changed(); + notify_property_list_changed(); } #define ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, p_allow_echo) \ @@ -1135,10 +1307,26 @@ void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_g ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, p_allow_echo); items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { + Array events = item.shortcut->get_events(); + for (int j = 0; j < events.size(); j++) { + Ref<InputEventKey> ie = events[j]; + if (ie.is_valid()) { + ds->global_menu_set_item_accelerator(global_menu_name, index, ie->get_keycode_with_modifiers()); + break; + } + } + } + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } @@ -1148,10 +1336,27 @@ void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortc item.icon = p_icon; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { + Array events = item.shortcut->get_events(); + for (int j = 0; j < events.size(); j++) { + Ref<InputEventKey> ie = events[j]; + if (ie.is_valid()) { + ds->global_menu_set_item_accelerator(global_menu_name, index, ie->get_keycode_with_modifiers()); + break; + } + } + } + ds->global_menu_set_item_icon(global_menu_name, index, item.icon); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } @@ -1161,10 +1366,27 @@ void PopupMenu::add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bo item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { + Array events = item.shortcut->get_events(); + for (int j = 0; j < events.size(); j++) { + Ref<InputEventKey> ie = events[j]; + if (ie.is_valid()) { + ds->global_menu_set_item_accelerator(global_menu_name, index, ie->get_keycode_with_modifiers()); + break; + } + } + } + ds->global_menu_set_item_checkable(global_menu_name, index, true); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } @@ -1175,10 +1397,28 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref< item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { + Array events = item.shortcut->get_events(); + for (int j = 0; j < events.size(); j++) { + Ref<InputEventKey> ie = events[j]; + if (ie.is_valid()) { + ds->global_menu_set_item_accelerator(global_menu_name, index, ie->get_keycode_with_modifiers()); + break; + } + } + } + ds->global_menu_set_item_icon(global_menu_name, index, item.icon); + ds->global_menu_set_item_checkable(global_menu_name, index, true); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } @@ -1188,10 +1428,27 @@ void PopupMenu::add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_ item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { + Array events = item.shortcut->get_events(); + for (int j = 0; j < events.size(); j++) { + Ref<InputEventKey> ie = events[j]; + if (ie.is_valid()) { + ds->global_menu_set_item_accelerator(global_menu_name, index, ie->get_keycode_with_modifiers()); + break; + } + } + } + ds->global_menu_set_item_radio_checkable(global_menu_name, index, true); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } @@ -1202,10 +1459,28 @@ void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, cons item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { + Array events = item.shortcut->get_events(); + for (int j = 0; j < events.size(); j++) { + Ref<InputEventKey> ie = events[j]; + if (ie.is_valid()) { + ds->global_menu_set_item_accelerator(global_menu_name, index, ie->get_keycode_with_modifiers()); + break; + } + } + } + ds->global_menu_set_item_icon(global_menu_name, index, item.icon); + ds->global_menu_set_item_radio_checkable(global_menu_name, index, true); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } @@ -1217,10 +1492,22 @@ void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, item.submenu = p_submenu; 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; + } + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } @@ -1240,6 +1527,10 @@ void PopupMenu::set_item_text(int p_idx, const String &p_text) { items.write[p_idx].text = p_text; items.write[p_idx].xl_text = atr(p_text); items.write[p_idx].dirty = true; + + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_text(global_menu_name, p_idx, items[p_idx].xl_text); + } _shape_item(p_idx); control->queue_redraw(); @@ -1284,6 +1575,10 @@ void PopupMenu::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) { items.write[p_idx].icon = p_icon; + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_icon(global_menu_name, p_idx, items[p_idx].icon); + } + control->queue_redraw(); child_controls_changed(); _menu_changed(); @@ -1332,6 +1627,10 @@ void PopupMenu::set_item_checked(int p_idx, bool p_checked) { items.write[p_idx].checked = p_checked; + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_checked(global_menu_name, p_idx, p_checked); + } + control->queue_redraw(); child_controls_changed(); _menu_changed(); @@ -1349,6 +1648,10 @@ void PopupMenu::set_item_id(int p_idx, int p_id) { items.write[p_idx].id = p_id; + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_tag(global_menu_name, p_idx, p_id); + } + control->queue_redraw(); child_controls_changed(); _menu_changed(); @@ -1367,6 +1670,10 @@ void PopupMenu::set_item_accelerator(int p_idx, Key p_accel) { items.write[p_idx].accel = p_accel; items.write[p_idx].dirty = true; + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_accelerator(global_menu_name, p_idx, p_accel); + } + control->queue_redraw(); child_controls_changed(); _menu_changed(); @@ -1383,7 +1690,6 @@ void PopupMenu::set_item_metadata(int p_idx, const Variant &p_meta) { } items.write[p_idx].metadata = p_meta; - control->queue_redraw(); child_controls_changed(); _menu_changed(); } @@ -1399,6 +1705,11 @@ void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) { } items.write[p_idx].disabled = p_disabled; + + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_disabled(global_menu_name, p_idx, p_disabled); + } + control->queue_redraw(); child_controls_changed(); _menu_changed(); @@ -1414,7 +1725,30 @@ void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) { return; } + 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)); + if (pm) { + DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, p_idx, String()); + pm->unbind_global_menu(); + } + items.write[p_idx].submenu_bound = false; + } + } + 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; + } + } + } + control->queue_redraw(); child_controls_changed(); _menu_changed(); @@ -1423,6 +1757,11 @@ void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) { void PopupMenu::toggle_item_checked(int p_idx) { ERR_FAIL_INDEX(p_idx, items.size()); items.write[p_idx].checked = !items[p_idx].checked; + + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_checked(global_menu_name, p_idx, items[p_idx].checked); + } + control->queue_redraw(); child_controls_changed(); _menu_changed(); @@ -1569,6 +1908,11 @@ void PopupMenu::set_item_as_checkable(int p_idx, bool p_checkable) { } items.write[p_idx].checkable_type = p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE; + + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_checkable(global_menu_name, p_idx, p_checkable); + } + control->queue_redraw(); _menu_changed(); } @@ -1585,6 +1929,11 @@ void PopupMenu::set_item_as_radio_checkable(int p_idx, bool p_radio_checkable) { } items.write[p_idx].checkable_type = p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE; + + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_radio_checkable(global_menu_name, p_idx, p_radio_checkable); + } + control->queue_redraw(); _menu_changed(); } @@ -1600,6 +1949,11 @@ void PopupMenu::set_item_tooltip(int p_idx, const String &p_tooltip) { } items.write[p_idx].tooltip = p_tooltip; + + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_tooltip(global_menu_name, p_idx, p_tooltip); + } + control->queue_redraw(); _menu_changed(); } @@ -1625,6 +1979,21 @@ void PopupMenu::set_item_shortcut(int p_idx, const Ref<Shortcut> &p_shortcut, bo _ref_shortcut(items[p_idx].shortcut); } + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + ds->global_menu_set_item_accelerator(global_menu_name, p_idx, Key::NONE); + if (!items[p_idx].shortcut_is_disabled && items[p_idx].shortcut.is_valid() && items[p_idx].shortcut->has_valid_event()) { + Array events = items[p_idx].shortcut->get_events(); + for (int j = 0; j < events.size(); j++) { + Ref<InputEventKey> ie = events[j]; + if (ie.is_valid()) { + ds->global_menu_set_item_accelerator(global_menu_name, p_idx, ie->get_keycode_with_modifiers()); + break; + } + } + } + } + control->queue_redraw(); _menu_changed(); } @@ -1640,6 +2009,10 @@ void PopupMenu::set_item_indent(int p_idx, int p_indent) { } items.write[p_idx].indent = p_indent; + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_indentation_level(global_menu_name, p_idx, p_indent); + } + control->queue_redraw(); child_controls_changed(); _menu_changed(); @@ -1656,6 +2029,11 @@ void PopupMenu::set_item_multistate(int p_idx, int p_state) { } items.write[p_idx].state = p_state; + + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_state(global_menu_name, p_idx, p_state); + } + control->queue_redraw(); _menu_changed(); } @@ -1671,6 +2049,22 @@ void PopupMenu::set_item_shortcut_disabled(int p_idx, bool p_disabled) { } items.write[p_idx].shortcut_is_disabled = p_disabled; + + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + ds->global_menu_set_item_accelerator(global_menu_name, p_idx, Key::NONE); + if (!items[p_idx].shortcut_is_disabled && items[p_idx].shortcut.is_valid() && items[p_idx].shortcut->has_valid_event()) { + Array events = items[p_idx].shortcut->get_events(); + for (int j = 0; j < events.size(); j++) { + Ref<InputEventKey> ie = events[j]; + if (ie.is_valid()) { + ds->global_menu_set_item_accelerator(global_menu_name, p_idx, ie->get_keycode_with_modifiers()); + break; + } + } + } + } + control->queue_redraw(); _menu_changed(); } @@ -1686,6 +2080,10 @@ void PopupMenu::toggle_item_multistate(int p_idx) { items.write[p_idx].state = 0; } + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_state(global_menu_name, p_idx, items[p_idx].state); + } + control->queue_redraw(); _menu_changed(); } @@ -1739,11 +2137,23 @@ void PopupMenu::set_item_count(int p_count) { return; } + DisplayServer *ds = DisplayServer::get_singleton(); + bool is_global = !global_menu_name.is_empty(); + + if (is_global && prev_size > p_count) { + for (int i = prev_size - 1; i >= p_count; i--) { + ds->global_menu_remove_item(global_menu_name, i); + } + } + items.resize(p_count); if (prev_size < p_count) { for (int i = prev_size; i < p_count; i++) { items.write[i].id = i; + if (is_global) { + ds->global_menu_add_item(global_menu_name, String(), callable_mp(this, &PopupMenu::activate_item), Callable(), i); + } } } @@ -1828,6 +2238,16 @@ bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_fo return false; } +void PopupMenu::_about_to_popup() { + ERR_MAIN_THREAD_GUARD; + emit_signal(SNAME("about_to_popup")); +} + +void PopupMenu::_about_to_close() { + ERR_MAIN_THREAD_GUARD; + emit_signal(SNAME("popup_hide")); +} + void PopupMenu::activate_item(int p_idx) { ERR_FAIL_INDEX(p_idx, items.size()); ERR_FAIL_COND(items[p_idx].separator); @@ -1890,6 +2310,11 @@ void PopupMenu::remove_item(int p_idx) { } items.remove_at(p_idx); + + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_remove_item(global_menu_name, p_idx); + } + control->queue_redraw(); child_controls_changed(); _menu_changed(); @@ -1904,6 +2329,11 @@ void PopupMenu::add_separator(const String &p_text, int p_id) { sep.xl_text = atr(p_text); } items.push_back(sep); + + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_add_separator(global_menu_name); + } + control->queue_redraw(); _menu_changed(); } @@ -1922,7 +2352,22 @@ void PopupMenu::clear(bool p_free_submenus) { } } } + + if (!global_menu_name.is_empty()) { + 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(); + } + item.submenu_bound = false; + } + } + DisplayServer::get_singleton()->global_menu_clear(global_menu_name); + } items.clear(); + mouse_over = -1; control->queue_redraw(); child_controls_changed(); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index f123d08400..5d5f4a8322 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -75,6 +75,7 @@ class PopupMenu : public Popup { bool shortcut_is_global = false; bool shortcut_is_disabled = false; bool allow_echo = false; + bool submenu_bound = false; // Returns (0,0) if icon is null. Size2 get_icon_size() const { @@ -88,6 +89,8 @@ class PopupMenu : public Popup { } }; + String global_menu_name; + bool close_allowed = false; bool activated_by_keyboard = false; @@ -213,6 +216,9 @@ public: virtual void _parent_focused() override; + String bind_global_menu(); + void unbind_global_menu(); + void add_item(const String &p_label, int p_id = -1, Key p_accel = Key::NONE); void add_icon_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id = -1, Key p_accel = Key::NONE); void add_check_item(const String &p_label, int p_id = -1, Key p_accel = Key::NONE); @@ -293,6 +299,9 @@ public: bool activate_item_by_event(const Ref<InputEvent> &p_event, bool p_for_global_only = false); void activate_item(int p_idx); + void _about_to_popup(); + void _about_to_close(); + void remove_item(int p_idx); void add_separator(const String &p_text = String(), int p_id = -1); diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp index 851a94b32f..0d33774e20 100644 --- a/scene/gui/subviewport_container.cpp +++ b/scene/gui/subviewport_container.cpp @@ -190,6 +190,13 @@ void SubViewportContainer::_propagate_nonpositional_event(const Ref<InputEvent> return; } + bool send; + if (GDVIRTUAL_CALL(_propagate_input_event, p_event, send)) { + if (!send) { + return; + } + } + _send_event_to_viewports(p_event); } @@ -204,6 +211,13 @@ void SubViewportContainer::gui_input(const Ref<InputEvent> &p_event) { return; } + bool send; + if (GDVIRTUAL_CALL(_propagate_input_event, p_event, send)) { + if (!send) { + return; + } + } + if (stretch && shrink > 1) { Transform2D xform; xform.scale(Vector2(1, 1) / shrink); @@ -275,6 +289,8 @@ void SubViewportContainer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stretch"), "set_stretch", "is_stretch_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_shrink"), "set_stretch_shrink", "get_stretch_shrink"); + + GDVIRTUAL_BIND(_propagate_input_event, "event"); } SubViewportContainer::SubViewportContainer() { diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h index 3c6cd09d66..06420de730 100644 --- a/scene/gui/subviewport_container.h +++ b/scene/gui/subviewport_container.h @@ -50,6 +50,8 @@ protected: virtual void add_child_notify(Node *p_child) override; virtual void remove_child_notify(Node *p_child) override; + GDVIRTUAL1RC(bool, _propagate_input_event, Ref<InputEvent>); + public: void set_stretch(bool p_enable); bool is_stretch_enabled() const; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index f00fdb6896..3d426b8bf3 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -6436,14 +6436,6 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_mode", PROPERTY_HINT_ENUM, "None,Boundary"), "set_line_wrapping_mode", "get_line_wrapping_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Arbitrary:1,Word:2,Word (Smart):3"), "set_autowrap_mode", "get_autowrap_mode"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_current_line"), "set_highlight_current_line", "is_highlight_current_line_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_tabs"), "set_draw_tabs", "is_drawing_tabs"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_spaces"), "set_draw_spaces", "is_drawing_spaces"); - - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "syntax_highlighter", PROPERTY_HINT_RESOURCE_TYPE, "SyntaxHighlighter", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ALWAYS_DUPLICATE), "set_syntax_highlighter", "get_syntax_highlighter"); - ADD_GROUP("Scroll", "scroll_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_smooth"), "set_smooth_scroll_enabled", "is_smooth_scroll_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_v_scroll_speed", PROPERTY_HINT_NONE, "suffix:px/s"), "set_v_scroll_speed", "get_v_scroll_speed"); @@ -6465,6 +6457,16 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_caret_mid_grapheme_enabled", "is_caret_mid_grapheme_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_multiple"), "set_multiple_carets_enabled", "is_multiple_carets_enabled"); + ADD_GROUP("Highlighting", ""); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "syntax_highlighter", PROPERTY_HINT_RESOURCE_TYPE, "SyntaxHighlighter", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ALWAYS_DUPLICATE), "set_syntax_highlighter", "get_syntax_highlighter"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_current_line"), "set_highlight_current_line", "is_highlight_current_line_enabled"); + + ADD_GROUP("Visual Whitespace", "draw_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_tabs"), "set_draw_tabs", "is_drawing_tabs"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_spaces"), "set_draw_spaces", "is_drawing_spaces"); + ADD_GROUP("BiDi", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); |
