From bca0d36fe662477eb787b4ddc9b0b69ab4603393 Mon Sep 17 00:00:00 2001 From: trollodel <33117082+trollodel@users.noreply.github.com> Date: Sun, 7 Mar 2021 21:07:30 +0100 Subject: [PATCH] Improve TreeItem API and allow to move nodes --- doc/classes/Tree.xml | 2 +- doc/classes/TreeItem.xml | 66 ++- editor/action_map_editor.cpp | 4 +- editor/animation_track_editor.cpp | 8 +- editor/connections_dialog.cpp | 2 +- editor/debugger/script_editor_debugger.cpp | 12 +- editor/dependency_editor.cpp | 4 +- editor/editor_asset_installer.cpp | 6 +- editor/editor_sectioned_inspector.cpp | 6 +- editor/filesystem_dock.cpp | 26 +- editor/find_in_files.cpp | 2 +- editor/groups_editor.cpp | 8 +- editor/plugins/script_editor_plugin.cpp | 4 +- editor/plugins/theme_editor_plugin.cpp | 8 +- editor/plugins/tiles/tile_map_editor.cpp | 2 +- .../plugins/version_control_editor_plugin.cpp | 4 +- .../plugins/visual_shader_editor_plugin.cpp | 10 +- editor/project_export.cpp | 4 +- editor/property_selector.cpp | 12 +- editor/quick_open.cpp | 4 +- editor/scene_tree_dock.cpp | 4 +- editor/scene_tree_editor.cpp | 4 +- editor/settings_config_dialog.cpp | 6 +- .../visual_script/visual_script_editor.cpp | 36 +- .../visual_script_property_selector.cpp | 6 +- scene/gui/file_dialog.cpp | 4 +- scene/gui/tree.cpp | 457 +++++++++++------- scene/gui/tree.h | 99 +++- 28 files changed, 524 insertions(+), 286 deletions(-) diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index a09f1bf470ed..27b26801f3ec 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -30,7 +30,7 @@ } [/csharp] [/codeblocks] - To iterate over all the [TreeItem] objects in a [Tree] object, use [method TreeItem.get_next] and [method TreeItem.get_children] after getting the root through [method get_root]. You can use [method Object.free] on a [TreeItem] to remove it from the [Tree]. + To iterate over all the [TreeItem] objects in a [Tree] object, use [method TreeItem.get_next] and [method TreeItem.get_first_child] after getting the root through [method get_root]. You can use [method Object.free] on a [TreeItem] to remove it from the [Tree]. diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml index add23c2ce6c1..7d375805044b 100644 --- a/doc/classes/TreeItem.xml +++ b/doc/classes/TreeItem.xml @@ -63,6 +63,16 @@ Removes all OpenType features. + + + + + + + Creates an item and adds it as a child. + The new item will be inserted as position [code]idx[/code] (the default value [code]-1[/code] means the last position), or it will be the last child if [code]idx[/code] is higher than the child count. + + @@ -123,11 +133,28 @@ Returns the column's cell mode. - + + + + + Returns a child item by its index (see [method get_child_count]). This method is often used for iterating all children of an item. + Negative indices access the children from the last one. + + + + + - Returns the TreeItem's first child item or a null object if there is none. + Returns the number of child items. + + + + + + + Returns an array of references to the item's children. @@ -157,6 +184,13 @@ Returns [code]true[/code] if [code]expand_right[/code] is set. + + + + + Returns the TreeItem's first child. + + @@ -193,6 +227,13 @@ Returns the icon [Texture2D] region as [Rect2]. + + + + + Returns the node's order in the tree. For example, if called on the first child item the position is [code]0[/code]. + + @@ -342,6 +383,13 @@ Returns the given column's tooltip. + + + + + Returns the [Tree] that owns this TreeItem. + + @@ -397,18 +445,24 @@ Returns [code]true[/code] if column [code]column[/code] is selected. - + + + - Moves this TreeItem to the bottom in the [Tree] hierarchy. + Moves this TreeItem right after the given [code]item[/code]. + [b]Note:[/b] You can't move to the root or move the root. - + + + - Moves this TreeItem to the top in the [Tree] hierarchy. + Moves this TreeItem right before the given [code]item[/code]. + [b]Note:[/b] You can't move to the root or move the root. diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp index cfc5a2a8db3a..957e370a2794 100644 --- a/editor/action_map_editor.cpp +++ b/editor/action_map_editor.cpp @@ -122,9 +122,9 @@ void InputEventConfigurationDialog::_set_event(const Ref &p_event) { // Update selected item in input list for keys, joybuttons and joyaxis only (since the mouse cannot be "listened" for). if (k.is_valid() || joyb.is_valid() || joym.is_valid()) { - TreeItem *category = input_list_tree->get_root()->get_children(); + TreeItem *category = input_list_tree->get_root()->get_first_child(); while (category) { - TreeItem *input_item = category->get_children(); + TreeItem *input_item = category->get_first_child(); // has_type this should be always true, unless the tree structure has been misconfigured. bool has_type = input_item->get_parent()->has_meta("__type"); diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 00915e4c2171..9d5811dcbdad 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -5216,7 +5216,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { track_clipboard.clear(); TreeItem *root = track_copy_select->get_root(); if (root) { - TreeItem *it = root->get_children(); + TreeItem *it = root->get_first_child(); while (it) { Dictionary md = it->get_metadata(0); int idx = md["track_idx"]; @@ -5602,7 +5602,7 @@ void AnimationTrackEditor::_show_imported_anim_warning() { } void AnimationTrackEditor::_select_all_tracks_for_copy() { - TreeItem *track = track_copy_select->get_root()->get_children(); + TreeItem *track = track_copy_select->get_root()->get_first_child(); if (!track) { return; } @@ -5616,7 +5616,7 @@ void AnimationTrackEditor::_select_all_tracks_for_copy() { track = track->get_next(); } - track = track_copy_select->get_root()->get_children(); + track = track_copy_select->get_root()->get_first_child(); while (track) { track->set_checked(0, !all_selected); track = track->get_next(); @@ -5681,7 +5681,7 @@ void AnimationTrackEditor::_pick_track_select_recursive(TreeItem *p_item, const p_select_candidates.push_back(node); } - TreeItem *c = p_item->get_children(); + TreeItem *c = p_item->get_first_child(); while (c) { _pick_track_select_recursive(c, p_filter, p_select_candidates); diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 0c1fb6fe4d1c..511c2a48cca1 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -655,7 +655,7 @@ void ConnectionsDock::_disconnect_all() { return; } - TreeItem *child = item->get_children(); + TreeItem *child = item->get_first_child(); String signalName = item->get_metadata(0).operator Dictionary()["name"]; undo_redo->create_action(vformat(TTR("Disconnect all from signal: '%s'"), signalName)); diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 1d95161e6c68..7493cc2a8d7e 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -220,7 +220,7 @@ void ScriptEditorDebugger::_file_selected(const String &p_file) { file->store_csv_line(headers); if (vmem_tree->get_root()) { - TreeItem *ti = vmem_tree->get_root()->get_children(); + TreeItem *ti = vmem_tree->get_root()->get_first_child(); while (ti) { Vector values; values.resize(vmem_tree->get_columns()); @@ -1319,7 +1319,7 @@ bool ScriptEditorDebugger::is_skip_breakpoints() { void ScriptEditorDebugger::_error_activated() { TreeItem *selected = error_tree->get_selected(); - TreeItem *ci = selected->get_children(); + TreeItem *ci = selected->get_first_child(); if (ci) { selected->set_collapsed(!selected->is_collapsed()); } @@ -1341,7 +1341,7 @@ void ScriptEditorDebugger::_expand_errors_list() { return; } - TreeItem *item = root->get_children(); + TreeItem *item = root->get_first_child(); while (item) { item->set_collapsed(false); item = item->get_next(); @@ -1354,7 +1354,7 @@ void ScriptEditorDebugger::_collapse_errors_list() { return; } - TreeItem *item = root->get_children(); + TreeItem *item = root->get_first_child(); while (item) { item->set_collapsed(true); item = item->get_next(); @@ -1403,7 +1403,7 @@ void ScriptEditorDebugger::_item_menu_id_pressed(int p_option) { int rpad_len = text.length(); text = type + text + ti->get_text(1) + "\n"; - TreeItem *ci = ti->get_children(); + TreeItem *ci = ti->get_first_child(); while (ci) { text += " " + ci->get_text(0).rpad(rpad_len) + ci->get_text(1) + "\n"; ci = ci->get_next(); @@ -1419,7 +1419,7 @@ void ScriptEditorDebugger::_item_menu_id_pressed(int p_option) { } // We only need the first child here (C++ source stack trace). - TreeItem *ci = ti->get_children(); + TreeItem *ci = ti->get_first_child(); // Parse back the `file:line @ method()` string. const Vector file_line_number = ci->get_text(1).split("@")[0].strip_edges().split(":"); ERR_FAIL_COND_MSG(file_line_number.size() < 2, "Incorrect C++ source stack trace file:line format (please report)."); diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index 57d44ca56cfd..c085205f6320 100644 --- a/editor/dependency_editor.cpp +++ b/editor/dependency_editor.cpp @@ -725,8 +725,8 @@ void OrphanResourcesDialog::_find_to_delete(TreeItem *p_item, List &path paths.push_back(p_item->get_metadata(0)); } - if (p_item->get_children()) { - _find_to_delete(p_item->get_children(), paths); + if (p_item->get_first_child()) { + _find_to_delete(p_item->get_first_child(), paths); } p_item = p_item->get_next(); diff --git a/editor/editor_asset_installer.cpp b/editor/editor_asset_installer.cpp index 2d29076476cb..9dcd550f5c36 100644 --- a/editor/editor_asset_installer.cpp +++ b/editor/editor_asset_installer.cpp @@ -45,8 +45,8 @@ void EditorAssetInstaller::_update_subitems(TreeItem *p_item, bool p_check, bool p_item->set_checked(0, false); } - if (p_item->get_children()) { - _update_subitems(p_item->get_children(), p_check); + if (p_item->get_first_child()) { + _update_subitems(p_item->get_first_child(), p_check); } if (!p_first && p_item->get_next()) { @@ -60,7 +60,7 @@ void EditorAssetInstaller::_uncheck_parent(TreeItem *p_item) { } bool any_checked = false; - TreeItem *item = p_item->get_children(); + TreeItem *item = p_item->get_first_child(); while (item) { if (item->is_checked(0)) { any_checked = true; diff --git a/editor/editor_sectioned_inspector.cpp b/editor/editor_sectioned_inspector.cpp index f81c87be9ee0..7532f1c79612 100644 --- a/editor/editor_sectioned_inspector.cpp +++ b/editor/editor_sectioned_inspector.cpp @@ -135,7 +135,7 @@ void SectionedInspector::_section_selected() { } selected_category = sections->get_selected()->get_metadata(0); - filter->set_section(selected_category, sections->get_selected()->get_children() == nullptr); + filter->set_section(selected_category, sections->get_selected()->get_first_child() == nullptr); inspector->set_property_prefix(selected_category + "/"); } @@ -187,8 +187,8 @@ void SectionedInspector::edit(Object *p_object) { TreeItem *first_item = sections->get_root(); if (first_item) { - while (first_item->get_children()) { - first_item = first_item->get_children(); + while (first_item->get_first_child()) { + first_item = first_item->get_first_child(); } first_item->select(0); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 3168710e90ba..ec2358b02fbb 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -180,12 +180,12 @@ Vector FileSystemDock::_compute_uncollapsed_paths() { Vector uncollapsed_paths; TreeItem *root = tree->get_root(); if (root) { - TreeItem *favorites_item = root->get_children(); + TreeItem *favorites_item = root->get_first_child(); if (!favorites_item->is_collapsed()) { uncollapsed_paths.push_back(favorites_item->get_metadata(0)); } - TreeItem *resTree = root->get_children()->get_next(); + TreeItem *resTree = root->get_first_child()->get_next(); if (resTree) { Vector needs_check; needs_check.push_back(resTree); @@ -193,7 +193,7 @@ Vector FileSystemDock::_compute_uncollapsed_paths() { while (needs_check.size()) { if (!needs_check[0]->is_collapsed()) { uncollapsed_paths.push_back(needs_check[0]->get_metadata(0)); - TreeItem *child = needs_check[0]->get_children(); + TreeItem *child = needs_check[0]->get_first_child(); while (child) { needs_check.push_back(child); child = child->get_next(); @@ -464,7 +464,7 @@ void FileSystemDock::_tree_multi_selected(Object *p_item, int p_column, bool p_s return; } - TreeItem *favorites_item = tree->get_root()->get_children(); + TreeItem *favorites_item = tree->get_root()->get_first_child(); if (selected->get_parent() == favorites_item && !String(selected->get_metadata(0)).ends_with("/")) { // Go to the favorites if we click in the favorites and the path has changed. path = "Favorites"; @@ -1644,7 +1644,7 @@ Vector FileSystemDock::_tree_get_selected(bool remove_self_inclusion) { // Build a list of selected items with the active one at the first position. Vector selected_strings; - TreeItem *favorites_item = tree->get_root()->get_children(); + TreeItem *favorites_item = tree->get_root()->get_first_child(); TreeItem *active_selected = tree->get_selected(); if (active_selected && active_selected != favorites_item) { selected_strings.push_back(active_selected->get_metadata(0)); @@ -1700,7 +1700,7 @@ void FileSystemDock::_tree_rmb_option(int p_option) { while (needs_check.size()) { needs_check[0]->set_collapsed(is_collapsed); - TreeItem *child = needs_check[0]->get_children(); + TreeItem *child = needs_check[0]->get_first_child(); while (child) { needs_check.push_back(child); child = child->get_next(); @@ -2062,13 +2062,13 @@ Variant FileSystemDock::get_drag_data_fw(const Point2 &p_point, Control *p_from) // Check if the first selected is in favorite. TreeItem *selected = tree->get_next_selected(tree->get_root()); while (selected) { - TreeItem *favorites_item = tree->get_root()->get_children(); + TreeItem *favorites_item = tree->get_root()->get_first_child(); if (selected == favorites_item) { // The "Favorites" item is not draggable. return Variant(); } - bool is_favorite = selected->get_parent() != nullptr && tree->get_root()->get_children() == selected->get_parent(); + bool is_favorite = selected->get_parent() != nullptr && tree->get_root()->get_first_child() == selected->get_parent(); all_favorites &= is_favorite; all_not_favorites &= !is_favorite; selected = tree->get_next_selected(selected); @@ -2114,7 +2114,7 @@ bool FileSystemDock::can_drop_data_fw(const Point2 &p_point, const Variant &p_da } int drop_section = tree->get_drop_section_at_position(p_point); - TreeItem *favorites_item = tree->get_root()->get_children(); + TreeItem *favorites_item = tree->get_root()->get_first_child(); TreeItem *resources_item = favorites_item->get_next(); @@ -2190,7 +2190,7 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data, int drop_position; Vector files = drag_data["files"]; - TreeItem *favorites_item = tree->get_root()->get_children(); + TreeItem *favorites_item = tree->get_root()->get_first_child(); TreeItem *resources_item = favorites_item->get_next(); if (ti == favorites_item) { @@ -2328,10 +2328,10 @@ void FileSystemDock::_get_drag_target_folder(String &target, bool &target_favori int section = tree->get_drop_section_at_position(p_point); if (ti) { // Check the favorites first. - if (ti == tree->get_root()->get_children() && section >= 0) { + if (ti == tree->get_root()->get_first_child() && section >= 0) { target_favorites = true; return; - } else if (ti->get_parent() == tree->get_root()->get_children()) { + } else if (ti->get_parent() == tree->get_root()->get_first_child()) { target_favorites = true; return; } else { @@ -2347,7 +2347,7 @@ void FileSystemDock::_get_drag_target_folder(String &target, bool &target_favori return; } } else { - if (ti->get_parent() != tree->get_root()->get_children()) { + if (ti->get_parent() != tree->get_root()->get_first_child()) { // Not in the favorite section. if (fpath != "res://") { // We drop between two files diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index 47079a92b75f..4fa131e99191 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -838,7 +838,7 @@ void FindInFilesPanel::_on_replace_all_clicked() { String fpath = file_item->get_metadata(0); Vector locations; - for (TreeItem *item = file_item->get_children(); item; item = item->get_next()) { + for (TreeItem *item = file_item->get_first_child(); item; item = item->get_next()) { if (!item->is_checked(0)) { continue; } diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp index f2a110ca03dd..d8e5a05c5d45 100644 --- a/editor/groups_editor.cpp +++ b/editor/groups_editor.cpp @@ -52,7 +52,7 @@ void GroupDialog::_group_selected() { selected_group = groups->get_selected()->get_text(0); _load_nodes(scene_tree->get_edited_scene_root()); - group_empty->set_visible(!remove_node_root->get_children()); + group_empty->set_visible(!remove_node_root->get_first_child()); } void GroupDialog::_load_nodes(Node *p_current) { @@ -217,7 +217,7 @@ void GroupDialog::_group_renamed() { } const String name = renamed_group->get_text(0).strip_edges(); - for (TreeItem *E = groups_root->get_children(); E; E = E->get_next()) { + for (TreeItem *E = groups_root->get_first_child(); E; E = E->get_next()) { if (E != renamed_group && E->get_text(0) == name) { renamed_group->set_text(0, selected_group); error->set_text(TTR("Group name already exists.")); @@ -274,7 +274,7 @@ void GroupDialog::_rename_group_item(const String &p_old_name, const String &p_n selected_group = p_new_name; - for (TreeItem *E = groups_root->get_children(); E; E = E->get_next()) { + for (TreeItem *E = groups_root->get_first_child(); E; E = E->get_next()) { if (E->get_text(0) == p_old_name) { E->set_text(0, p_new_name); return; @@ -351,7 +351,7 @@ void GroupDialog::_delete_group_item(const String &p_name) { selected_group = ""; } - for (TreeItem *E = groups_root->get_children(); E; E = E->get_next()) { + for (TreeItem *E = groups_root->get_first_child(); E; E = E->get_next()) { if (E->get_text(0) == p_name) { groups_root->remove_child(E); return; diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 893a9356a2ab..4bc64c92710d 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -339,13 +339,13 @@ void ScriptEditorQuickOpen::_update_search() { if ((search_box->get_text() == "" || file.findn(search_box->get_text()) != -1)) { TreeItem *ti = search_options->create_item(root); ti->set_text(0, file); - if (root->get_children() == ti) { + if (root->get_first_child() == ti) { ti->select(0); } } } - get_ok_button()->set_disabled(root->get_children() == nullptr); + get_ok_button()->set_disabled(root->get_first_child() == nullptr); } void ScriptEditorQuickOpen::_confirmed() { diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 0f7468beade1..f1c73f99dc94 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -321,7 +321,7 @@ void ThemeItemImportTree::_toggle_type_items(bool p_collapse) { return; } - TreeItem *type_node = root->get_children(); + TreeItem *type_node = root->get_first_child(); while (type_node) { type_node->set_collapsed(p_collapse); type_node = type_node->get_next(); @@ -491,7 +491,7 @@ void ThemeItemImportTree::_tree_item_edited() { } void ThemeItemImportTree::_select_all_subitems(TreeItem *p_root_item, bool p_select_with_data) { - TreeItem *child_item = p_root_item->get_children(); + TreeItem *child_item = p_root_item->get_first_child(); while (child_item) { child_item->set_checked(IMPORT_ITEM, true); if (p_select_with_data) { @@ -505,7 +505,7 @@ void ThemeItemImportTree::_select_all_subitems(TreeItem *p_root_item, bool p_sel } void ThemeItemImportTree::_deselect_all_subitems(TreeItem *p_root_item, bool p_deselect_completely) { - TreeItem *child_item = p_root_item->get_children(); + TreeItem *child_item = p_root_item->get_first_child(); while (child_item) { child_item->set_checked(IMPORT_ITEM_DATA, false); if (p_deselect_completely) { @@ -527,7 +527,7 @@ void ThemeItemImportTree::_update_parent_items(TreeItem *p_root_item) { bool any_checked = false; bool any_checked_with_data = false; - TreeItem *child_item = parent_item->get_children(); + TreeItem *child_item = parent_item->get_first_child(); while (child_item) { if (child_item->is_checked(IMPORT_ITEM)) { any_checked = true; diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 91bbd8c0804e..d4fa575905df 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -2587,7 +2587,7 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref } if (need_tree_item_switch) { - for (tree_item = terrains_tree->get_root()->get_children(); tree_item; tree_item = tree_item->get_next_visible()) { + for (tree_item = terrains_tree->get_root()->get_first_child(); tree_item; tree_item = tree_item->get_next_visible()) { Dictionary metadata_dict = tree_item->get_metadata(0); if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) { int terrain_set = metadata_dict["terrain_set"]; diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp index 0af3b936cb33..75a944e91040 100644 --- a/editor/plugins/version_control_editor_plugin.cpp +++ b/editor/plugins/version_control_editor_plugin.cpp @@ -180,7 +180,7 @@ void VersionControlEditorPlugin::_stage_selected() { staged_files_count = 0; TreeItem *root = stage_files->get_root(); if (root) { - TreeItem *file_entry = root->get_children(); + TreeItem *file_entry = root->get_first_child(); while (file_entry) { if (file_entry->is_checked(0)) { EditorVCSInterface::get_singleton()->stage_file(file_entry->get_metadata(0)); @@ -207,7 +207,7 @@ void VersionControlEditorPlugin::_stage_all() { staged_files_count = 0; TreeItem *root = stage_files->get_root(); if (root) { - TreeItem *file_entry = root->get_children(); + TreeItem *file_entry = root->get_first_child(); while (file_entry) { EditorVCSInterface::get_singleton()->stage_file(file_entry->get_metadata(0)); file_entry->set_icon_modulate(0, EditorNode::get_singleton()->get_gui_base()->get_theme_color("success_color", "Editor")); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 9ed8f6c1fcc2..e7816002dcae 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -2762,10 +2762,10 @@ void VisualShaderEditor::_notification(int p_what) { // collapse tree by default - TreeItem *category = members->get_root()->get_children(); + TreeItem *category = members->get_root()->get_first_child(); while (category) { category->set_collapsed(true); - TreeItem *sub_category = category->get_children(); + TreeItem *sub_category = category->get_first_child(); while (sub_category) { sub_category->set_collapsed(true); sub_category = sub_category->get_next(); @@ -3210,14 +3210,14 @@ void VisualShaderEditor::_member_cancel() { } void VisualShaderEditor::_tools_menu_option(int p_idx) { - TreeItem *category = members->get_root()->get_children(); + TreeItem *category = members->get_root()->get_first_child(); switch (p_idx) { case EXPAND_ALL: while (category) { category->set_collapsed(false); - TreeItem *sub_category = category->get_children(); + TreeItem *sub_category = category->get_first_child(); while (sub_category) { sub_category->set_collapsed(false); sub_category = sub_category->get_next(); @@ -3231,7 +3231,7 @@ void VisualShaderEditor::_tools_menu_option(int p_idx) { while (category) { category->set_collapsed(true); - TreeItem *sub_category = category->get_children(); + TreeItem *sub_category = category->get_first_child(); while (sub_category) { sub_category->set_collapsed(true); sub_category = sub_category->get_next(); diff --git a/editor/project_export.cpp b/editor/project_export.cpp index 3ede50320af6..9b9937273566 100644 --- a/editor/project_export.cpp +++ b/editor/project_export.cpp @@ -795,7 +795,7 @@ void ProjectExportDialog::_tree_changed() { } void ProjectExportDialog::_check_dir_recursive(TreeItem *p_dir, bool p_checked) { - for (TreeItem *child = p_dir->get_children(); child; child = child->get_next()) { + for (TreeItem *child = p_dir->get_first_child(); child; child = child->get_next()) { String path = child->get_metadata(0); child->set_checked(0, p_checked); @@ -818,7 +818,7 @@ void ProjectExportDialog::_refresh_parent_checks(TreeItem *p_item) { } bool checked = true; - for (TreeItem *child = parent->get_children(); child; child = child->get_next()) { + for (TreeItem *child = parent->get_first_child(); child; child = child->get_next()) { checked = checked && child->is_checked(0); if (!checked) { break; diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp index da798962e58c..bf31be536c0a 100644 --- a/editor/property_selector.cpp +++ b/editor/property_selector.cpp @@ -52,7 +52,7 @@ void PropertySelector::_sbox_input(const Ref &p_ie) { search_box->accept_event(); TreeItem *root = search_options->get_root(); - if (!root->get_children()) { + if (!root->get_first_child()) { break; } @@ -150,7 +150,7 @@ void PropertySelector::_update_search() { for (List::Element *E = props.front(); E; E = E->next()) { if (E->get().usage == PROPERTY_USAGE_CATEGORY) { - if (category && category->get_children() == nullptr) { + if (category && category->get_first_child() == nullptr) { memdelete(category); //old category was unused } category = search_options->create_item(root); @@ -192,7 +192,7 @@ void PropertySelector::_update_search() { item->set_selectable(0, true); } - if (category && category->get_children() == nullptr) { + if (category && category->get_first_child() == nullptr) { memdelete(category); //old category was unused } } else { @@ -225,7 +225,7 @@ void PropertySelector::_update_search() { for (List::Element *E = methods.front(); E; E = E->next()) { if (E->get().name.begins_with("*")) { - if (category && category->get_children() == nullptr) { + if (category && category->get_first_child() == nullptr) { memdelete(category); //old category was unused } category = search_options->create_item(root); @@ -316,12 +316,12 @@ void PropertySelector::_update_search() { } } - if (category && category->get_children() == nullptr) { + if (category && category->get_first_child() == nullptr) { memdelete(category); //old category was unused } } - get_ok_button()->set_disabled(root->get_children() == nullptr); + get_ok_button()->set_disabled(root->get_first_child() == nullptr); } void PropertySelector::_confirmed() { diff --git a/editor/quick_open.cpp b/editor/quick_open.cpp index 7f720d65d0b3..e8e13bab215d 100644 --- a/editor/quick_open.cpp +++ b/editor/quick_open.cpp @@ -102,7 +102,7 @@ void EditorQuickOpen::_update_search() { ti->set_icon(0, *icons.lookup_ptr(entries[i].path.get_extension())); } - TreeItem *to_select = root->get_children(); + TreeItem *to_select = root->get_first_child(); to_select->select(0); to_select->set_as_cursor(0); search_options->scroll_to_item(to_select); @@ -170,7 +170,7 @@ void EditorQuickOpen::_sbox_input(const Ref &p_ie) { if (allow_multi_select) { TreeItem *root = search_options->get_root(); - if (!root->get_children()) { + if (!root->get_first_child()) { break; } diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 4068a102df7e..349e05b47b01 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -1833,7 +1833,7 @@ bool SceneTreeDock::_is_collapsed_recursive(TreeItem *p_item) const { TreeItem *item = needs_check.back()->get(); needs_check.pop_back(); - TreeItem *child = item->get_children(); + TreeItem *child = item->get_first_child(); is_branch_collapsed = item->is_collapsed() && child; if (is_branch_collapsed) { @@ -1857,7 +1857,7 @@ void SceneTreeDock::_set_collapsed_recursive(TreeItem *p_item, bool p_collapsed) item->set_collapsed(p_collapsed); - TreeItem *child = item->get_children(); + TreeItem *child = item->get_first_child(); while (child) { to_collapse.push_back(child); child = child->get_next(); diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index d0bf83eb2d0d..6f9b0ae87308 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -699,7 +699,7 @@ TreeItem *SceneTreeEditor::_find(TreeItem *p_node, const NodePath &p_path) { return p_node; } - TreeItem *children = p_node->get_children(); + TreeItem *children = p_node->get_first_child(); while (children) { TreeItem *n = _find(children, p_path); if (n) { @@ -883,7 +883,7 @@ void SceneTreeEditor::_update_selection(TreeItem *item) { item->deselect(0); } - TreeItem *c = item->get_children(); + TreeItem *c = item->get_first_child(); while (c) { _update_selection(c); diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp index 541ec224b90d..4cdf8208778b 100644 --- a/editor/settings_config_dialog.cpp +++ b/editor/settings_config_dialog.cpp @@ -235,8 +235,8 @@ void EditorSettingsDialog::_update_shortcuts() { // Before clearing the tree, take note of which categories are collapsed so that this state can be maintained when the tree is repopulated. Map collapsed; - if (shortcuts->get_root() && shortcuts->get_root()->get_children()) { - for (TreeItem *item = shortcuts->get_root()->get_children(); item; item = item->get_next()) { + if (shortcuts->get_root() && shortcuts->get_root()->get_first_child()) { + for (TreeItem *item = shortcuts->get_root()->get_first_child(); item; item = item->get_next()) { collapsed[item->get_text(0)] = item->is_collapsed(); } } @@ -380,7 +380,7 @@ void EditorSettingsDialog::_update_shortcuts() { // remove sections with no shortcuts for (Map::Element *E = sections.front(); E; E = E->next()) { TreeItem *section = E->get(); - if (section->get_children() == nullptr) { + if (section->get_first_child() == nullptr) { root->remove_child(section); } } diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index a51369c5999e..743244060312 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -1168,7 +1168,7 @@ void VisualScriptEditor::_member_selected() { selected = ti->get_metadata(0); - if (ti->get_parent() == members->get_root()->get_children()) { + if (ti->get_parent() == members->get_root()->get_first_child()) { #ifdef OSX_ENABLED bool held_ctrl = Input::get_singleton()->is_key_pressed(KEY_META); #else @@ -1214,7 +1214,7 @@ void VisualScriptEditor::_member_edited() { TreeItem *root = members->get_root(); - if (ti->get_parent() == root->get_children()) { + if (ti->get_parent() == root->get_first_child()) { selected = new_name; int node_id = script->get_function_node_id(name); @@ -1255,7 +1255,7 @@ void VisualScriptEditor::_member_edited() { return; // Or crash because it will become invalid. } - if (ti->get_parent() == root->get_children()->get_next()) { + if (ti->get_parent() == root->get_first_child()->get_next()) { selected = new_name; undo_redo->create_action(TTR("Rename Variable")); undo_redo->add_do_method(script.ptr(), "rename_variable", name, new_name); @@ -1271,7 +1271,7 @@ void VisualScriptEditor::_member_edited() { return; // Or crash because it will become invalid. } - if (ti->get_parent() == root->get_children()->get_next()->get_next()) { + if (ti->get_parent() == root->get_first_child()->get_next()->get_next()) { selected = new_name; undo_redo->create_action(TTR("Rename Signal")); undo_redo->add_do_method(script.ptr(), "rename_custom_signal", name, new_name); @@ -1405,7 +1405,7 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt if (ti->get_parent() == root) { //main buttons - if (ti == root->get_children()) { + if (ti == root->get_first_child()) { // Add function, this one uses menu. if (p_button == 1) { @@ -1442,7 +1442,7 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt return; // Or crash because it will become invalid. } - if (ti == root->get_children()->get_next()) { + if (ti == root->get_first_child()->get_next()) { // Add variable. String name = _validate_name("new_variable"); selected = name; @@ -1458,7 +1458,7 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt return; // Or crash because it will become invalid. } - if (ti == root->get_children()->get_next()->get_next()) { + if (ti == root->get_first_child()->get_next()->get_next()) { // Add variable. String name = _validate_name("new_signal"); selected = name; @@ -1473,7 +1473,7 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt undo_redo->commit_action(); return; // Or crash because it will become invalid. } - } else if (ti->get_parent() == root->get_children()) { + } else if (ti->get_parent() == root->get_first_child()) { selected = ti->get_text(0); function_name_edit->set_position(Input::get_singleton()->get_mouse_position() - Vector2(60, -10)); function_name_edit->popup(); @@ -1841,13 +1841,13 @@ void VisualScriptEditor::_members_gui_input(const Ref &p_event) { TreeItem *ti = members->get_selected(); if (ti) { TreeItem *root = members->get_root(); - if (ti->get_parent() == root->get_children()) { + if (ti->get_parent() == root->get_first_child()) { member_type = MEMBER_FUNCTION; } - if (ti->get_parent() == root->get_children()->get_next()) { + if (ti->get_parent() == root->get_first_child()->get_next()) { member_type = MEMBER_VARIABLE; } - if (ti->get_parent() == root->get_children()->get_next()->get_next()) { + if (ti->get_parent() == root->get_first_child()->get_next()->get_next()) { member_type = MEMBER_SIGNAL; } member_name = ti->get_text(0); @@ -1864,7 +1864,7 @@ void VisualScriptEditor::_members_gui_input(const Ref &p_event) { Ref btn = p_event; if (btn.is_valid() && btn->is_double_click()) { TreeItem *ti = members->get_selected(); - if (ti && ti->get_parent() == members->get_root()->get_children()) { // to check if it's a function + if (ti && ti->get_parent() == members->get_root()->get_first_child()) { // to check if it's a function _center_on_node(script->get_function_node_id(ti->get_metadata(0))); } } @@ -1946,13 +1946,13 @@ Variant VisualScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_f Dictionary dd; TreeItem *root = members->get_root(); - if (it->get_parent() == root->get_children()) { + if (it->get_parent() == root->get_first_child()) { dd["type"] = "visual_script_function_drag"; dd["function"] = type; - } else if (it->get_parent() == root->get_children()->get_next()) { + } else if (it->get_parent() == root->get_first_child()->get_next()) { dd["type"] = "visual_script_variable_drag"; dd["variable"] = type; - } else if (it->get_parent() == root->get_children()->get_next()->get_next()) { + } else if (it->get_parent() == root->get_first_child()->get_next()->get_next()) { dd["type"] = "visual_script_signal_drag"; dd["signal"] = type; @@ -4115,7 +4115,7 @@ void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos) { Ref edit_icon = Control::get_theme_icon("Edit", "EditorIcons"); - if (ti->get_parent() == root->get_children()) { + if (ti->get_parent() == root->get_first_child()) { member_type = MEMBER_FUNCTION; member_name = ti->get_text(0); member_popup->add_icon_shortcut(edit_icon, ED_GET_SHORTCUT("visual_script_editor/edit_member"), MEMBER_EDIT); @@ -4125,7 +4125,7 @@ void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos) { return; } - if (ti->get_parent() == root->get_children()->get_next()) { + if (ti->get_parent() == root->get_first_child()->get_next()) { member_type = MEMBER_VARIABLE; member_name = ti->get_text(0); member_popup->add_icon_shortcut(edit_icon, ED_GET_SHORTCUT("visual_script_editor/edit_member"), MEMBER_EDIT); @@ -4135,7 +4135,7 @@ void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos) { return; } - if (ti->get_parent() == root->get_children()->get_next()->get_next()) { + if (ti->get_parent() == root->get_first_child()->get_next()->get_next()) { member_type = MEMBER_SIGNAL; member_name = ti->get_text(0); member_popup->add_icon_shortcut(edit_icon, ED_GET_SHORTCUT("visual_script_editor/edit_member"), MEMBER_EDIT); diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp index 862cac5c679f..93e14f60d00f 100644 --- a/modules/visual_script/visual_script_property_selector.cpp +++ b/modules/visual_script/visual_script_property_selector.cpp @@ -59,7 +59,7 @@ void VisualScriptPropertySelector::_sbox_input(const Ref &p_ie) { search_box->accept_event(); TreeItem *root = search_options->get_root(); - if (!root->get_children()) { + if (!root->get_first_child()) { break; } @@ -265,7 +265,7 @@ void VisualScriptPropertySelector::_update_search() { item->set_metadata(2, connecting); } - if (category && category->get_children() == nullptr) { + if (category && category->get_first_child() == nullptr) { memdelete(category); //old category was unused } } @@ -310,7 +310,7 @@ void VisualScriptPropertySelector::_update_search() { found = true; } - get_ok_button()->set_disabled(root->get_children() == nullptr); + get_ok_button()->set_disabled(root->get_first_child() == nullptr); } void VisualScriptPropertySelector::create_visualscript_item(const String &name, TreeItem *const root, const String &search_input, const String &text) { diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 350c77039f1c..806039d7acc7 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -589,8 +589,8 @@ void FileDialog::update_file_list() { files.pop_front(); } - if (tree->get_root() && tree->get_root()->get_children() && tree->get_selected() == nullptr) { - tree->get_root()->get_children()->select(0); + if (tree->get_root() && tree->get_root()->get_first_child() && tree->get_selected() == nullptr) { + tree->get_root()->get_first_child()->select(0); } } diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 9e4c73679e31..2f78736a122b 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -47,36 +47,6 @@ #include -void TreeItem::move_to_top() { - if (!parent || parent->children == this) { - return; //already on top - } - TreeItem *prev = get_prev(); - prev->next = next; - next = parent->children; - parent->children = this; -} - -void TreeItem::move_to_bottom() { - if (!parent || !next) { - return; - } - - TreeItem *prev = get_prev(); - TreeItem *last = next; - while (last->next) { - last = last->next; - } - - if (prev) { - prev->next = next; - } else { - parent->children = next; - } - last->next = this; - next = nullptr; -} - Size2 TreeItem::Cell::get_icon_size() const { if (icon.is_null()) { return Size2(); @@ -118,6 +88,54 @@ void TreeItem::_cell_deselected(int p_cell) { tree->item_deselected(p_cell, this); } +void TreeItem::_change_tree(Tree *p_tree) { + if (p_tree == tree) { + return; + } + + TreeItem *c = first_child; + while (c) { + c->_change_tree(p_tree); + c = c->next; + } + + if (tree && tree->root == this) { + tree->root = nullptr; + } + + if (tree && tree->popup_edited_item == this) { + tree->popup_edited_item = nullptr; + tree->pressing_for_editor = false; + } + + if (tree && tree->cache.hover_item == this) { + tree->cache.hover_item = nullptr; + } + + if (tree && tree->selected_item == this) { + tree->selected_item = nullptr; + } + + if (tree && tree->drop_mode_over == this) { + tree->drop_mode_over = nullptr; + } + + if (tree && tree->single_select_defer == this) { + tree->single_select_defer = nullptr; + } + + if (tree && tree->edited_item == this) { + tree->edited_item = nullptr; + tree->pressing_for_editor = false; + } + + tree = p_tree; + + if (tree) { + cells.resize(tree->columns.size()); + } +} + /* cell mode */ void TreeItem::set_cell_mode(int p_column, TreeCellMode p_mode) { ERR_FAIL_INDEX(p_column, cells.size()); @@ -427,20 +445,74 @@ int TreeItem::get_custom_minimum_height() const { return custom_min_height; } +/* Item manipulation */ + +TreeItem *TreeItem::create_child(int p_idx) { + TreeItem *ti = memnew(TreeItem(tree)); + if (tree) { + ti->cells.resize(tree->columns.size()); + } + + TreeItem *l_prev = nullptr; + TreeItem *c = first_child; + int idx = 0; + + while (c) { + if (idx++ == p_idx) { + c->prev = ti; + ti->next = c; + break; + } + l_prev = c; + c = c->next; + } + + if (l_prev) { + l_prev->next = ti; + ti->prev = l_prev; + if (!children_cache.is_empty()) { + if (ti->next) { + children_cache.insert(p_idx, ti); + } else { + children_cache.append(ti); + } + } + } else { + first_child = ti; + if (!children_cache.is_empty()) { + children_cache.insert(0, ti); + } + } + + ti->parent = this; + + return ti; +} + +Tree *TreeItem::get_tree() { + return tree; +} + TreeItem *TreeItem::get_next() { return next; } TreeItem *TreeItem::get_prev() { - if (!parent || parent->children == this) { - return nullptr; + if (prev) { + return prev; } - TreeItem *prev = parent->children; - while (prev && prev->next != this) { - prev = prev->next; + if (!parent || parent->first_child == this) { + return nullptr; + } + // This is an edge case + TreeItem *l_prev = parent->first_child; + while (l_prev && l_prev->next != this) { + l_prev = l_prev->next; } + prev = l_prev; + return prev; } @@ -448,8 +520,8 @@ TreeItem *TreeItem::get_parent() { return parent; } -TreeItem *TreeItem::get_children() { - return children; +TreeItem *TreeItem::get_first_child() { + return first_child; } TreeItem *TreeItem::get_prev_visible(bool p_wrap) { @@ -475,10 +547,10 @@ TreeItem *TreeItem::get_prev_visible(bool p_wrap) { } } else { current = prev; - while (!current->collapsed && current->children) { + while (!current->collapsed && current->first_child) { //go to the very end - current = current->children; + current = current->first_child; while (current->next) { current = current->next; } @@ -491,8 +563,8 @@ TreeItem *TreeItem::get_prev_visible(bool p_wrap) { TreeItem *TreeItem::get_next_visible(bool p_wrap) { TreeItem *current = this; - if (!current->collapsed && current->children) { - current = current->children; + if (!current->collapsed && current->first_child) { + current = current->first_child; } else if (current->next) { current = current->next; @@ -515,24 +587,136 @@ TreeItem *TreeItem::get_next_visible(bool p_wrap) { return current; } -void TreeItem::remove_child(TreeItem *p_item) { +TreeItem *TreeItem::get_child(int p_idx) { + _create_children_cache(); + ERR_FAIL_INDEX_V(p_idx, children_cache.size(), nullptr); + return children_cache.get(p_idx); +} + +int TreeItem::get_child_count() { + _create_children_cache(); + return children_cache.size(); +} + +Array TreeItem::get_children() { + int size = get_child_count(); + Array arr; + arr.resize(size); + for (int i = 0; i < size; i++) { + arr[i] = children_cache[i]; + } + + return arr; +} + +int TreeItem::get_index() { + int idx = 0; + TreeItem *c = this; + + while (c) { + c = c->get_prev(); + idx++; + } + return idx - 1; +} + +void TreeItem::move_before(TreeItem *p_item) { ERR_FAIL_NULL(p_item); - TreeItem **c = &children; + ERR_FAIL_COND(is_root); + ERR_FAIL_COND(!p_item->parent); - while (*c) { - if ((*c) == p_item) { - TreeItem *aux = *c; + if (p_item == this) { + return; + } - *c = (*c)->next; + TreeItem *p = p_item->parent; + while (p) { + ERR_FAIL_COND_MSG(p == this, "Can't move to a descendant"); + p = p->parent; + } - aux->parent = nullptr; - return; - } + Tree *old_tree = tree; + _unlink_from_tree(); + _change_tree(p_item->tree); + + parent = p_item->parent; + + TreeItem *item_prev = p_item->get_prev(); + if (item_prev) { + item_prev->next = this; + parent->children_cache.clear(); + } else { + parent->first_child = this; + parent->children_cache.insert(0, this); + } + + prev = item_prev; + next = p_item; + p_item->prev = this; + + if (old_tree && old_tree != tree) { + old_tree->update(); + } + + if (tree) { + tree->update(); + } +} + +void TreeItem::move_after(TreeItem *p_item) { + ERR_FAIL_NULL(p_item); + ERR_FAIL_COND(is_root); + ERR_FAIL_COND(!p_item->parent); + + if (p_item == this) { + return; + } - c = &(*c)->next; + TreeItem *p = p_item->parent; + while (p) { + ERR_FAIL_COND_MSG(p == this, "Can't move to a descendant"); + p = p->parent; } - ERR_FAIL(); + Tree *old_tree = tree; + _unlink_from_tree(); + _change_tree(p_item->tree); + + if (p_item->next) { + p_item->next->prev = this; + } + parent = p_item->parent; + prev = p_item; + next = p_item->next; + p_item->next = this; + + if (next) { + parent->children_cache.clear(); + } else { + parent->children_cache.append(this); + } + + if (old_tree && old_tree != tree) { + old_tree->update(); + } + + if (tree) { + tree->update(); + } +} + +void TreeItem::remove_child(TreeItem *p_item) { + ERR_FAIL_NULL(p_item); + ERR_FAIL_COND(p_item->parent != this); + + p_item->_unlink_from_tree(); + p_item->prev = nullptr; + p_item->next = nullptr; + p_item->parent = nullptr; + + if (tree) { + tree->update(); + } } void TreeItem::set_selectable(int p_column, bool p_selectable) { @@ -785,7 +969,7 @@ void recursive_call_aux(TreeItem *p_item, const StringName &p_method, const Vari return; } p_item->call(p_method, p_args, p_argcount, r_error); - TreeItem *c = p_item->get_children(); + TreeItem *c = p_item->get_first_child(); while (c) { recursive_call_aux(c, p_method, p_args, p_argcount, r_error); c = c->get_next(); @@ -855,16 +1039,6 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_custom_minimum_height", "height"), &TreeItem::set_custom_minimum_height); ClassDB::bind_method(D_METHOD("get_custom_minimum_height"), &TreeItem::get_custom_minimum_height); - ClassDB::bind_method(D_METHOD("get_next"), &TreeItem::get_next); - ClassDB::bind_method(D_METHOD("get_prev"), &TreeItem::get_prev); - ClassDB::bind_method(D_METHOD("get_parent"), &TreeItem::get_parent); - ClassDB::bind_method(D_METHOD("get_children"), &TreeItem::get_children); - - ClassDB::bind_method(D_METHOD("get_next_visible", "wrap"), &TreeItem::get_next_visible, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("get_prev_visible", "wrap"), &TreeItem::get_prev_visible, DEFVAL(false)); - - ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::_remove_child); - ClassDB::bind_method(D_METHOD("set_selectable", "column", "selectable"), &TreeItem::set_selectable); ClassDB::bind_method(D_METHOD("is_selectable", "column"), &TreeItem::is_selectable); @@ -895,19 +1069,38 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_button_disabled", "column", "button_idx", "disabled"), &TreeItem::set_button_disabled); ClassDB::bind_method(D_METHOD("is_button_disabled", "column", "button_idx"), &TreeItem::is_button_disabled); - ClassDB::bind_method(D_METHOD("set_expand_right", "column", "enable"), &TreeItem::set_expand_right); - ClassDB::bind_method(D_METHOD("get_expand_right", "column"), &TreeItem::get_expand_right); - ClassDB::bind_method(D_METHOD("set_tooltip", "column", "tooltip"), &TreeItem::set_tooltip); ClassDB::bind_method(D_METHOD("get_tooltip", "column"), &TreeItem::get_tooltip); ClassDB::bind_method(D_METHOD("set_text_align", "column", "text_align"), &TreeItem::set_text_align); ClassDB::bind_method(D_METHOD("get_text_align", "column"), &TreeItem::get_text_align); - ClassDB::bind_method(D_METHOD("move_to_top"), &TreeItem::move_to_top); - ClassDB::bind_method(D_METHOD("move_to_bottom"), &TreeItem::move_to_bottom); + + ClassDB::bind_method(D_METHOD("set_expand_right", "column", "enable"), &TreeItem::set_expand_right); + ClassDB::bind_method(D_METHOD("get_expand_right", "column"), &TreeItem::get_expand_right); ClassDB::bind_method(D_METHOD("set_disable_folding", "disable"), &TreeItem::set_disable_folding); ClassDB::bind_method(D_METHOD("is_folding_disabled"), &TreeItem::is_folding_disabled); + ClassDB::bind_method(D_METHOD("create_child", "idx"), &TreeItem::create_child, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_tree"), &TreeItem::get_tree); + + ClassDB::bind_method(D_METHOD("get_next"), &TreeItem::get_next); + ClassDB::bind_method(D_METHOD("get_prev"), &TreeItem::get_prev); + ClassDB::bind_method(D_METHOD("get_parent"), &TreeItem::get_parent); + ClassDB::bind_method(D_METHOD("get_first_child"), &TreeItem::get_first_child); + + ClassDB::bind_method(D_METHOD("get_next_visible", "wrap"), &TreeItem::get_next_visible, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_prev_visible", "wrap"), &TreeItem::get_prev_visible, DEFVAL(false)); + + ClassDB::bind_method(D_METHOD("get_child", "idx"), &TreeItem::get_child); + ClassDB::bind_method(D_METHOD("get_child_count"), &TreeItem::get_child_count); + ClassDB::bind_method(D_METHOD("get_children"), &TreeItem::get_children); + ClassDB::bind_method(D_METHOD("get_index"), &TreeItem::get_index); + + ClassDB::bind_method(D_METHOD("move_before", "item"), &TreeItem::_move_before); + ClassDB::bind_method(D_METHOD("move_after", "item"), &TreeItem::_move_after); + + ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::_remove_child); + { MethodInfo mi; mi.name = "call_recursive"; @@ -932,7 +1125,7 @@ void TreeItem::_bind_methods() { } void TreeItem::clear_children() { - TreeItem *c = children; + TreeItem *c = first_child; while (c) { TreeItem *aux = c; c = c->get_next(); @@ -940,56 +1133,18 @@ void TreeItem::clear_children() { memdelete(aux); } - children = nullptr; + first_child = nullptr; }; TreeItem::TreeItem(Tree *p_tree) { tree = p_tree; - collapsed = false; - disable_folding = false; - custom_min_height = 0; - - parent = nullptr; // parent item - next = nullptr; // next in list - children = nullptr; //child items } TreeItem::~TreeItem() { + _unlink_from_tree(); + prev = nullptr; clear_children(); - - if (parent) { - parent->remove_child(this); - } - - if (tree && tree->root == this) { - tree->root = nullptr; - } - - if (tree && tree->popup_edited_item == this) { - tree->popup_edited_item = nullptr; - tree->pressing_for_editor = false; - } - - if (tree && tree->cache.hover_item == this) { - tree->cache.hover_item = nullptr; - } - - if (tree && tree->selected_item == this) { - tree->selected_item = nullptr; - } - - if (tree && tree->drop_mode_over == this) { - tree->drop_mode_over = nullptr; - } - - if (tree && tree->single_select_defer == this) { - tree->single_select_defer = nullptr; - } - - if (tree && tree->edited_item == this) { - tree->edited_item = nullptr; - tree->pressing_for_editor = false; - } + _change_tree(nullptr); } /**********************************************/ @@ -1116,7 +1271,7 @@ int Tree::get_item_height(TreeItem *p_item) const { if (!p_item->collapsed) { /* if not collapsed, check the children */ - TreeItem *c = p_item->children; + TreeItem *c = p_item->first_child; while (c) { height += get_item_height(c); @@ -1263,7 +1418,7 @@ void Tree::update_item_cache(TreeItem *p_item) { update_item_cell(p_item, i); } - TreeItem *c = p_item->children; + TreeItem *c = p_item->first_child; while (c) { update_item_cache(c); c = c->next; @@ -1430,7 +1585,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (drop_mode_flags && drop_mode_over == p_item) { Rect2 r = cell_rect; - bool has_parent = p_item->get_children() != nullptr; + bool has_parent = p_item->get_first_child() != nullptr; if (rtl) { r.position.x = get_size().width - r.position.x - r.size.x; } @@ -1626,7 +1781,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } } - if (!p_item->disable_folding && !hide_folding && p_item->children) { //has children, draw the guide box + if (!p_item->disable_folding && !hide_folding && p_item->first_child) { //has children, draw the guide box Ref arrow; @@ -1656,7 +1811,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (!p_item->collapsed) { /* if not collapsed, check the children */ - TreeItem *c = p_item->children; + TreeItem *c = p_item->first_child; int prev_ofs = children_pos.y - cache.offset.y + p_draw_ofs.y; @@ -1666,7 +1821,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 int parent_ofs = p_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin); Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - cache.offset + p_draw_ofs; - if (c->get_children() != nullptr) { + if (c->get_first_child() != nullptr) { root_pos -= Point2i(cache.arrow->get_width(), 0); } @@ -1723,8 +1878,8 @@ int Tree::_count_selected_items(TreeItem *p_from) const { } } - if (p_from->get_children()) { - count += _count_selected_items(p_from->get_children()); + if (p_from->get_first_child()) { + count += _count_selected_items(p_from->get_first_child()); } if (p_from->get_next()) { @@ -1812,7 +1967,7 @@ void Tree::select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_c *r_in_range = false; } - TreeItem *c = p_current->children; + TreeItem *c = p_current->first_child; while (c) { select_single_item(p_selected, c, p_col, p_prev, r_in_range, p_current->is_collapsed() || p_force_deselect); @@ -1839,7 +1994,6 @@ void Tree::_range_click_timeout() { click_handled = false; Ref mb; mb.instance(); - ; propagate_mouse_activated = false; // done from outside, so signal handler can't clear the tree in the middle of emit (which is a common case) blocked++; @@ -1879,7 +2033,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool } if (!p_item->disable_folding && !hide_folding && (p_pos.x >= x_ofs && p_pos.x < (x_ofs + cache.item_margin))) { - if (p_item->children) { + if (p_item->first_child) { p_item->set_collapsed(!p_item->is_collapsed()); } @@ -1926,7 +2080,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool x -= cache.hseparation; } - if (!p_item->disable_folding && !hide_folding && !p_item->cells[col].editable && !p_item->cells[col].selectable && p_item->get_children()) { + if (!p_item->disable_folding && !hide_folding && !p_item->cells[col].editable && !p_item->cells[col].selectable && p_item->get_first_child()) { p_item->set_collapsed(!p_item->is_collapsed()); return -1; //collapse/uncollapse because nothing can be done with item } @@ -2164,7 +2318,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool if (!p_item->collapsed) { /* if not collapsed, check the children */ - TreeItem *c = p_item->children; + TreeItem *c = p_item->first_child; while (c) { int child_h = propagate_mouse_event(new_pos, x_ofs, y_ofs, p_double_click, c, p_button, p_mod); @@ -2270,7 +2424,7 @@ void Tree::popup_select(int p_option) { void Tree::_go_left() { if (selected_col == 0) { - if (selected_item->get_children() != nullptr && !selected_item->is_collapsed()) { + if (selected_item->get_first_child() != nullptr && !selected_item->is_collapsed()) { selected_item->set_collapsed(true); } else { if (columns.size() == 1) { // goto parent with one column @@ -2298,7 +2452,7 @@ void Tree::_go_left() { void Tree::_go_right() { if (selected_col == (columns.size() - 1)) { - if (selected_item->get_children() != nullptr && selected_item->is_collapsed()) { + if (selected_item->get_first_child() != nullptr && selected_item->is_collapsed()) { selected_item->set_collapsed(false); } else if (selected_item->get_next_visible()) { selected_col = 0; @@ -2417,7 +2571,7 @@ void Tree::_gui_input(Ref p_event) { } if (k.is_valid() && k->is_alt_pressed()) { selected_item->set_collapsed(false); - TreeItem *next = selected_item->get_children(); + TreeItem *next = selected_item->get_first_child(); while (next && next != selected_item->next) { next->set_collapsed(false); next = next->get_next_visible(); @@ -2436,7 +2590,7 @@ void Tree::_gui_input(Ref p_event) { if (k.is_valid() && k->is_alt_pressed()) { selected_item->set_collapsed(true); - TreeItem *next = selected_item->get_children(); + TreeItem *next = selected_item->get_first_child(); while (next && next != selected_item->next) { next->set_collapsed(true); next = next->get_next_visible(); @@ -2834,7 +2988,7 @@ void Tree::_gui_input(Ref p_event) { break; } } - if (!root || (!root->get_children() && hide_root)) { + if (!root || (!root->get_first_child() && hide_root)) { if (b->get_button_index() == MOUSE_BUTTON_RIGHT && allow_rmb_select) { emit_signal("empty_tree_rmb_selected", get_local_mouse_position()); } @@ -3269,38 +3423,15 @@ TreeItem *Tree::create_item(TreeItem *p_parent, int p_idx) { TreeItem *ti = nullptr; if (p_parent) { - // Append or insert a new item to the given parent. - ti = memnew(TreeItem(this)); - ERR_FAIL_COND_V(!ti, nullptr); - ti->cells.resize(columns.size()); - - TreeItem *prev = nullptr; - TreeItem *c = p_parent->children; - int idx = 0; - - while (c) { - if (idx++ == p_idx) { - ti->next = c; - break; - } - prev = c; - c = c->next; - } - - if (prev) { - prev->next = ti; - } else { - p_parent->children = ti; - } - ti->parent = p_parent; - + ERR_FAIL_COND_V_MSG(p_parent->tree != this, nullptr, "A different tree owns the given parent"); + ti = p_parent->create_child(p_idx); } else { if (!root) { // No root exists, make the given item the new root. ti = memnew(TreeItem(this)); ERR_FAIL_COND_V(!ti, nullptr); ti->cells.resize(columns.size()); - + ti->is_root = true; root = ti; } else { // Root exists, append or insert to root. @@ -3321,8 +3452,8 @@ TreeItem *Tree::get_last_item() { while (last) { if (last->next) { last = last->next; - } else if (last->children) { - last = last->children; + } else if (last->first_child) { + last = last->first_child; } else { break; } @@ -3491,8 +3622,8 @@ TreeItem *Tree::get_next_selected(TreeItem *p_item) { if (!p_item) { p_item = root; } else { - if (p_item->children) { - p_item = p_item->children; + if (p_item->first_child) { + p_item = p_item->first_child; } else if (p_item->next) { p_item = p_item->next; @@ -3561,7 +3692,7 @@ int Tree::get_column_width(int p_column) const { void Tree::propagate_set_columns(TreeItem *p_item) { p_item->cells.resize(columns.size()); - TreeItem *c = p_item->get_children(); + TreeItem *c = p_item->get_first_child(); while (c) { propagate_set_columns(c); c = c->next; @@ -3611,8 +3742,8 @@ int Tree::get_item_offset(TreeItem *p_item) const { ofs += cache.vseparation; } - if (it->children && !it->collapsed) { - it = it->children; + if (it->first_child && !it->collapsed) { + it = it->first_child; } else if (it->next) { it = it->next; @@ -3935,7 +4066,7 @@ TreeItem *Tree::_find_item_at_pos(TreeItem *p_item, const Point2 &p_pos, int &r_ return nullptr; // do not try children, it's collapsed } - TreeItem *n = p_item->get_children(); + TreeItem *n = p_item->get_first_child(); while (n) { int ch; TreeItem *r = _find_item_at_pos(n, pos, r_column, ch, section); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 6d36f0df7f4c..9dbfd93082df 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -122,14 +122,18 @@ class TreeItem : public Object { Vector cells; - bool collapsed; // won't show children - bool disable_folding; - int custom_min_height; + bool collapsed = false; // won't show children + bool disable_folding = false; + int custom_min_height = 0; - TreeItem *parent; // parent item - TreeItem *next; // next in list - TreeItem *children; //child items - Tree *tree; //tree (for reference) + TreeItem *parent = nullptr; // parent item + TreeItem *prev = nullptr; // previous in list + TreeItem *next = nullptr; // next in list + TreeItem *first_child = nullptr; + + Vector children_cache; + bool is_root = false; // for tree root + Tree *tree; // tree (for reference) TreeItem(Tree *p_tree); @@ -138,9 +142,40 @@ class TreeItem : public Object { void _cell_selected(int p_cell); void _cell_deselected(int p_cell); + void _change_tree(Tree *p_tree); + + _FORCE_INLINE_ void _create_children_cache() { + if (children_cache.is_empty()) { + TreeItem *c = first_child; + while (c) { + children_cache.append(c); + c = c->next; + } + } + } + + _FORCE_INLINE_ void _unlink_from_tree() { + TreeItem *p = get_prev(); + if (p) { + p->next = next; + } + if (next) { + next->prev = p; + } + if (parent) { + if (!parent->children_cache.is_empty()) { + parent->children_cache.remove(get_index()); + } + if (parent->first_child == this) { + parent->first_child = next; + } + } + } + protected: static void _bind_methods(); - //bind helpers + + // Bind helpers Dictionary _get_range_config(int p_column) { Dictionary d; double min = 0.0, max = 0.0, step = 0.0; @@ -156,6 +191,13 @@ class TreeItem : public Object { remove_child(Object::cast_to(p_child)); } + void _move_before(Object *p_item) { + move_before(Object::cast_to(p_item)); + } + void _move_after(Object *p_item) { + move_after(Object::cast_to(p_item)); + } + Variant _call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); public: @@ -234,16 +276,6 @@ class TreeItem : public Object { void set_custom_minimum_height(int p_height); int get_custom_minimum_height() const; - TreeItem *get_prev(); - TreeItem *get_next(); - TreeItem *get_parent(); - TreeItem *get_children(); - - TreeItem *get_prev_visible(bool p_wrap = false); - TreeItem *get_next_visible(bool p_wrap = false); - - void remove_child(TreeItem *p_item); - void set_selectable(int p_column, bool p_selectable); bool is_selectable(int p_column) const; @@ -269,22 +301,43 @@ class TreeItem : public Object { void set_tooltip(int p_column, const String &p_tooltip); String get_tooltip(int p_column) const; - void clear_children(); - void set_text_align(int p_column, TextAlign p_align); TextAlign get_text_align(int p_column) const; void set_expand_right(int p_column, bool p_enable); bool get_expand_right(int p_column) const; - void move_to_top(); - void move_to_bottom(); - void set_disable_folding(bool p_disable); bool is_folding_disabled() const; + /* Item manipulation */ + + TreeItem *create_child(int p_idx = -1); + + Tree *get_tree(); + + TreeItem *get_prev(); + TreeItem *get_next(); + TreeItem *get_parent(); + TreeItem *get_first_child(); + + TreeItem *get_prev_visible(bool p_wrap = false); + TreeItem *get_next_visible(bool p_wrap = false); + + TreeItem *get_child(int p_idx); + int get_child_count(); + Array get_children(); + int get_index(); + + void move_before(TreeItem *p_item); + void move_after(TreeItem *p_item); + + void remove_child(TreeItem *p_item); + void call_recursive(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); + void clear_children(); + ~TreeItem(); };