diff options
Diffstat (limited to 'editor/scene_tree_dock.cpp')
-rw-r--r-- | editor/scene_tree_dock.cpp | 817 |
1 files changed, 601 insertions, 216 deletions
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 6c79fad82c..f0114b393d 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -31,6 +31,7 @@ #include "scene_tree_dock.h" #include "core/io/resource_saver.h" +#include "core/os/input.h" #include "core/os/keyboard.h" #include "core/project_settings.h" @@ -88,6 +89,8 @@ void SceneTreeDock::_unhandled_key_input(Ref<InputEvent> p_event) { _tool_selected(TOOL_NEW); } else if (ED_IS_SHORTCUT("scene_tree/instance_scene", p_event)) { _tool_selected(TOOL_INSTANCE); + } else if (ED_IS_SHORTCUT("scene_tree/expand_collapse_all", p_event)) { + _tool_selected(TOOL_EXPAND_COLLAPSE); } else if (ED_IS_SHORTCUT("scene_tree/change_node_type", p_event)) { _tool_selected(TOOL_REPLACE); } else if (ED_IS_SHORTCUT("scene_tree/duplicate", p_event)) { @@ -268,7 +271,7 @@ void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base) bool SceneTreeDock::_cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) { int childCount = p_desired_node->get_child_count(); - if (p_desired_node->get_filename() == p_target_scene_path) { + if (_track_inherit(p_target_scene_path, p_desired_node)) { return true; } @@ -283,6 +286,35 @@ bool SceneTreeDock::_cyclical_dependency_exists(const String &p_target_scene_pat return false; } +bool SceneTreeDock::_track_inherit(const String &p_target_scene_path, Node *p_desired_node) { + Node *p = p_desired_node; + bool result = false; + Vector<Node *> instances; + while (true) { + if (p->get_filename() == p_target_scene_path) { + result = true; + break; + } + Ref<SceneState> ss = p->get_scene_inherited_state(); + if (ss.is_valid()) { + String path = ss->get_path(); + Ref<PackedScene> data = ResourceLoader::load(path); + if (data.is_valid()) { + p = data->instance(PackedScene::GEN_EDIT_STATE_INSTANCE); + if (!p) + continue; + instances.push_back(p); + } else + break; + } else + break; + } + for (int i = 0; i < instances.size(); i++) { + memdelete(instances[i]); + } + return result; +} + void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { current_option = p_tool; @@ -290,20 +322,30 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { switch (p_tool) { case TOOL_BATCH_RENAME: { + if (!profile_allow_editing) { + break; + } Tree *tree = scene_tree->get_scene_tree(); if (tree->is_anything_selected()) { rename_dialog->popup_centered(); } } break; case TOOL_RENAME: { + if (!profile_allow_editing) { + break; + } Tree *tree = scene_tree->get_scene_tree(); if (tree->is_anything_selected()) { tree->grab_focus(); tree->edit_selected(); } } break; - case TOOL_NEW: { + case TOOL_NEW: + case TOOL_REPARENT_TO_NEW_NODE: { + if (!profile_allow_editing) { + break; + } String preferred = ""; Node *current_edited_scene_root = EditorNode::get_singleton()->get_edited_scene(); @@ -319,6 +361,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } break; case TOOL_INSTANCE: { + if (!profile_allow_editing) { + break; + } Node *scene = edited_scene; if (!scene) { @@ -330,15 +375,50 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { quick_open->set_title(TTR("Instance Child Scene")); } break; + case TOOL_EXPAND_COLLAPSE: { + + if (!scene_tree->get_selected()) + break; + + Tree *tree = scene_tree->get_scene_tree(); + TreeItem *selected_item = tree->get_selected(); + + if (!selected_item) + selected_item = tree->get_root(); + + bool collapsed = _is_collapsed_recursive(selected_item); + _set_collapsed_recursive(selected_item, !collapsed); + + tree->ensure_cursor_is_visible(); + + } break; case TOOL_REPLACE: { - create_dialog->popup_create(false, true); + if (!profile_allow_editing) { + break; + } + + Node *selected = scene_tree->get_selected(); + if (!selected && !editor_selection->get_selected_node_list().empty()) + selected = editor_selection->get_selected_node_list().front()->get(); + + if (selected) + create_dialog->popup_create(false, true, selected->get_class()); + } break; case TOOL_ATTACH_SCRIPT: { + if (!profile_allow_script_editing) { + break; + } + + List<Node *> selection = editor_selection->get_selected_node_list(); + if (selection.empty()) + break; + Node *selected = scene_tree->get_selected(); if (!selected) - break; + selected = selection.front()->get(); Ref<Script> existing = selected->get_script(); @@ -367,13 +447,20 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } } } + script_create_dialog->connect("script_created", this, "_script_created"); + script_create_dialog->connect("popup_hide", this, "_script_creation_closed"); + script_create_dialog->set_inheritance_base_type("Node"); script_create_dialog->config(inherits, path); script_create_dialog->popup_centered(); } break; case TOOL_CLEAR_SCRIPT: { - List<Node *> selection = editor_selection->get_selected_node_list(); + if (!profile_allow_script_editing) { + break; + } + + Array selection = editor_selection->get_selected_nodes(); if (selection.empty()) return; @@ -381,13 +468,14 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { editor_data->get_undo_redo().create_action(TTR("Clear Script")); editor_data->get_undo_redo().add_do_method(editor, "push_item", (Script *)NULL); - for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + for (int i = 0; i < selection.size(); i++) { - Ref<Script> existing = E->get()->get_script(); - if (existing.is_valid()) { - const RefPtr empty; - editor_data->get_undo_redo().add_do_method(E->get(), "set_script", empty); - editor_data->get_undo_redo().add_undo_method(E->get(), "set_script", existing); + Node *n = Object::cast_to<Node>(selection[i]); + Ref<Script> existing = n->get_script(); + Ref<Script> empty = EditorNode::get_singleton()->get_object_custom_type_base(n); + if (existing != empty) { + editor_data->get_undo_redo().add_do_method(n, "set_script", empty); + editor_data->get_undo_redo().add_undo_method(n, "set_script", existing); } } @@ -399,6 +487,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { case TOOL_MOVE_UP: case TOOL_MOVE_DOWN: { + if (!profile_allow_editing) { + break; + } + if (!scene_tree->get_selected()) break; @@ -459,6 +551,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } break; case TOOL_DUPLICATE: { + if (!profile_allow_editing) { + break; + } + if (!edited_scene) break; @@ -481,11 +577,15 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { editor_data->get_undo_redo().add_do_method(editor_selection, "clear"); Node *dupsingle = NULL; + List<Node *> editable_children; - for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + selection.sort_custom<Node::Comparator>(); + + for (List<Node *>::Element *E = selection.back(); E; E = E->prev()) { Node *node = E->get(); Node *parent = node->get_parent(); + Node *selection_tail = _get_selection_group_tail(node, selection); List<Node *> owned; node->get_owned_by(node->get_owner(), &owned); @@ -493,6 +593,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { Map<const Node *, Node *> duplimap; Node *dup = node->duplicate_from_editor(duplimap); + if (EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(node)) + editable_children.push_back(dup); + ERR_CONTINUE(!dup); if (selection.size() == 1) @@ -500,7 +603,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { dup->set_name(parent->validate_child_name(dup)); - editor_data->get_undo_redo().add_do_method(parent, "add_child_below_node", node, dup); + editor_data->get_undo_redo().add_do_method(parent, "add_child_below_node", selection_tail, dup); for (List<Node *>::Element *F = owned.front(); F; F = F->next()) { if (!duplimap.has(F->get())) { @@ -508,7 +611,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { continue; } Node *d = duplimap[F->get()]; - editor_data->get_undo_redo().add_do_method(d, "set_owner", node->get_owner()); + editor_data->get_undo_redo().add_do_method(d, "set_owner", selection_tail->get_owner()); } editor_data->get_undo_redo().add_do_method(editor_selection, "add_node", dup); editor_data->get_undo_redo().add_undo_method(parent, "remove_child", dup); @@ -525,9 +628,16 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (dupsingle) editor->push_item(dupsingle); + for (List<Node *>::Element *E = editable_children.front(); E; E = E->next()) + _toggle_editable_children(E->get()); + } break; case TOOL_REPARENT: { + if (!profile_allow_editing) { + break; + } + if (!scene_tree->get_selected()) break; @@ -554,6 +664,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } break; case TOOL_MAKE_ROOT: { + if (!profile_allow_editing) { + break; + } + List<Node *> nodes = editor_selection->get_selected_node_list(); ERR_FAIL_COND(nodes.size() != 1); @@ -563,7 +677,27 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (node == root) return; - editor_data->get_undo_redo().create_action("Make node as Root"); + //check that from node to root, all owners are right + + if (root->get_scene_inherited_state().is_valid()) { + accept->set_text(TTR("Can't reparent nodes in inherited scenes, order of nodes can't change.")); + accept->popup_centered_minsize(); + return; + } + + if (node->get_owner() != root) { + accept->set_text(TTR("Node must belong to the edited scene to become root.")); + accept->popup_centered_minsize(); + return; + } + + if (node->get_filename() != String()) { + accept->set_text(TTR("Instantiated scenes can't become root")); + accept->popup_centered_minsize(); + return; + } + + editor_data->get_undo_redo().create_action(TTR("Make node as Root")); editor_data->get_undo_redo().add_do_method(node->get_parent(), "remove_child", node); editor_data->get_undo_redo().add_do_method(root->get_parent(), "remove_child", root); editor_data->get_undo_redo().add_do_method(node, "add_child", root); @@ -591,6 +725,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } break; case TOOL_MULTI_EDIT: { + if (!profile_allow_editing) { + break; + } + Node *root = EditorNode::get_singleton()->get_edited_scene(); if (!root) break; @@ -605,6 +743,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { case TOOL_ERASE: { + if (!profile_allow_editing) { + break; + } + List<Node *> remove_list = editor_selection->get_selected_node_list(); if (remove_list.empty()) @@ -624,10 +766,18 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } break; case TOOL_MERGE_FROM_SCENE: { + if (!profile_allow_editing) { + break; + } + EditorNode::get_singleton()->merge_from_scene(); } break; case TOOL_NEW_SCENE_FROM: { + if (!profile_allow_editing) { + break; + } + Node *scene = editor_data->get_edited_scene_root(); if (!scene) { @@ -679,6 +829,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { new_scene_from_dialog->set_title(TTR("Save New Scene As...")); } break; case TOOL_COPY_NODE_PATH: { + List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); if (e) { @@ -698,6 +849,11 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); } break; case TOOL_SCENE_EDITABLE_CHILDREN: { + + if (!profile_allow_editing) { + break; + } + List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); if (e) { @@ -710,11 +866,16 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { editable_instance_remove_dialog->popup_centered_minsize(); break; } - _toggle_editable_children(); + _toggle_editable_children(node); } } } break; case TOOL_SCENE_USE_PLACEHOLDER: { + + if (!profile_allow_editing) { + break; + } + List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); if (e) { @@ -735,6 +896,11 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } } break; case TOOL_SCENE_MAKE_LOCAL: { + + if (!profile_allow_editing) { + break; + } + List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); if (e) { @@ -757,6 +923,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } } break; case TOOL_SCENE_OPEN: { + List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); if (e) { @@ -767,9 +934,17 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } } break; case TOOL_SCENE_CLEAR_INHERITANCE: { + if (!profile_allow_editing) { + break; + } + clear_inherit_confirm->popup_centered_minsize(); } break; case TOOL_SCENE_CLEAR_INHERITANCE_CONFIRM: { + if (!profile_allow_editing) { + break; + } + List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); if (e) { @@ -782,14 +957,13 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } } break; case TOOL_SCENE_OPEN_INHERITED: { + List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); if (e) { Node *node = e->get(); - if (node) { - if (node && node->get_scene_inherited_state().is_valid()) { - scene_tree->emit_signal("open", node->get_scene_inherited_state()->get_path()); - } + if (node && node->get_scene_inherited_state().is_valid()) { + scene_tree->emit_signal("open", node->get_scene_inherited_state()->get_path()); } } } break; @@ -803,7 +977,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (TOOL_CREATE_FAVORITE == p_tool) { String name = selected_favorite_root.get_slicec(' ', 0); if (ScriptServer::is_global_class(name)) { - new_node = Object::cast_to<Node>(ClassDB::instance(ScriptServer::get_global_class_base(name))); + new_node = Object::cast_to<Node>(ClassDB::instance(ScriptServer::get_global_class_native_base(name))); Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(name), "Script"); if (new_node && script.is_valid()) { new_node->set_script(script.get_ref_ptr()); @@ -812,9 +986,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } else { new_node = Object::cast_to<Node>(ClassDB::instance(selected_favorite_root)); } + if (!new_node) { - ERR_EXPLAIN("Creating root from favorite '" + selected_favorite_root + "' failed. Creating 'Node' instead."); new_node = memnew(Node); + ERR_PRINTS("Creating root from favorite '" + selected_favorite_root + "' failed. Creating 'Node' instead."); } } else { switch (p_tool) { @@ -829,7 +1004,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } } - editor_data->get_undo_redo().create_action("New Scene Root"); + editor_data->get_undo_redo().create_action(TTR("New Scene Root")); editor_data->get_undo_redo().add_do_method(editor, "set_edited_scene", new_node); editor_data->get_undo_redo().add_do_method(scene_tree, "update_tree"); editor_data->get_undo_redo().add_do_reference(new_node); @@ -859,6 +1034,17 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } } +void SceneTreeDock::_node_collapsed(Object *p_obj) { + + TreeItem *ti = Object::cast_to<TreeItem>(p_obj); + if (!ti) + return; + + if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { + _set_collapsed_recursive(ti, ti->is_collapsed()); + } +} + void SceneTreeDock::_notification(int p_what) { switch (p_what) { @@ -869,6 +1055,8 @@ void SceneTreeDock::_notification(int p_what) { break; first_enter = false; + EditorFeatureProfileManager::get_singleton()->connect("current_feature_profile_changed", this, "_feature_profile_changed"); + CanvasItemEditorPlugin *canvas_item_plugin = Object::cast_to<CanvasItemEditorPlugin>(editor_data->get_editor("2D")); if (canvas_item_plugin) { canvas_item_plugin->get_canvas_item_editor()->connect("item_lock_status_changed", scene_tree, "_update_tree"); @@ -878,6 +1066,7 @@ void SceneTreeDock::_notification(int p_what) { SpatialEditorPlugin *spatial_editor_plugin = Object::cast_to<SpatialEditorPlugin>(editor_data->get_editor("3D")); spatial_editor_plugin->get_spatial_editor()->connect("item_lock_status_changed", scene_tree, "_update_tree"); + spatial_editor_plugin->get_spatial_editor()->connect("item_group_status_changed", scene_tree, "_update_tree"); button_add->set_icon(get_icon("Add", "EditorIcons")); button_instance->set_icon(get_icon("Instance", "EditorIcons")); @@ -888,6 +1077,7 @@ void SceneTreeDock::_notification(int p_what) { filter->set_clear_button_enabled(true); EditorNode::get_singleton()->get_editor_selection()->connect("selection_changed", this, "_selection_changed"); + scene_tree->get_scene_tree()->connect("item_collapsed", this, "_node_collapsed"); // create_root_dialog HBoxContainer *top_row = memnew(HBoxContainer); @@ -938,7 +1128,7 @@ void SceneTreeDock::_notification(int p_what) { Button *button_custom = memnew(Button); node_shortcuts->add_child(button_custom); - button_custom->set_text(TTR("Custom Node")); + button_custom->set_text(TTR("Other Node")); button_custom->set_icon(get_icon("Add", "EditorIcons")); button_custom->connect("pressed", this, "_tool_selected", make_binds(TOOL_NEW, false)); @@ -967,7 +1157,7 @@ void SceneTreeDock::_notification(int p_what) { bool show_create_root = bool(EDITOR_GET("interface/editors/show_scene_tree_root_selection")) && get_tree()->get_edited_scene_root() == NULL; - if (show_create_root != create_root_dialog->is_visible_in_tree()) { + if (show_create_root != create_root_dialog->is_visible_in_tree() && !remote_tree->is_visible()) { if (show_create_root) { create_root_dialog->show(); scene_tree->hide(); @@ -1132,17 +1322,17 @@ void SceneTreeDock::perform_node_renames(Node *p_base, List<Pair<NodePath, NodeP if (p.get_type() == Variant::NODE_PATH) { // Goes through all paths to check if its matching - for (List<Pair<NodePath, NodePath> >::Element *E = p_renames->front(); E; E = E->next()) { + for (List<Pair<NodePath, NodePath> >::Element *F = p_renames->front(); F; F = F->next()) { NodePath root_path = p_base->get_path(); - NodePath rel_path_old = root_path.rel_path_to(E->get().first); + NodePath rel_path_old = root_path.rel_path_to(F->get().first); - NodePath rel_path_new = E->get().second; + NodePath rel_path_new = F->get().second; // if not empty, get new relative path - if (E->get().second != NodePath()) { - rel_path_new = root_path.rel_path_to(E->get().second); + if (F->get().second != NodePath()) { + rel_path_new = root_path.rel_path_to(F->get().second); } // if old path detected, then it needs to be replaced with the new one @@ -1213,11 +1403,11 @@ void SceneTreeDock::perform_node_renames(Node *p_base, List<Pair<NodePath, NodeP if (!ran.has(i)) continue; //channel was removed - for (List<Pair<NodePath, NodePath> >::Element *E = p_renames->front(); E; E = E->next()) { + for (List<Pair<NodePath, NodePath> >::Element *F = p_renames->front(); F; F = F->next()) { - if (E->get().first == old_np) { + if (F->get().first == old_np) { - if (E->get().second == NodePath()) { + if (F->get().second == NodePath()) { //will be erased int idx = 0; @@ -1242,7 +1432,7 @@ void SceneTreeDock::perform_node_renames(Node *p_base, List<Pair<NodePath, NodeP } else { //will be renamed - NodePath rel_path = new_root_path.rel_path_to(E->get().second); + NodePath rel_path = new_root_path.rel_path_to(F->get().second); NodePath new_path = NodePath(rel_path.get_names(), track_np.get_subnames(), false); if (new_path == track_np) @@ -1348,11 +1538,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V Node *validate = new_parent; while (validate) { - if (p_nodes.find(validate) != -1) { - ERR_EXPLAIN("Selection changed at some point.. can't reparent"); - ERR_FAIL(); - return; - } + ERR_FAIL_COND_MSG(p_nodes.find(validate) != -1, "Selection changed at some point.. can't reparent."); validate = validate->get_parent(); } //ok all valid @@ -1482,6 +1668,52 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V editor_data->get_undo_redo().commit_action(); } +bool SceneTreeDock::_is_collapsed_recursive(TreeItem *p_item) const { + + bool is_branch_collapsed = false; + + List<TreeItem *> needs_check; + needs_check.push_back(p_item); + + while (!needs_check.empty()) { + + TreeItem *item = needs_check.back()->get(); + needs_check.pop_back(); + + TreeItem *child = item->get_children(); + is_branch_collapsed = item->is_collapsed() && child; + + if (is_branch_collapsed) { + break; + } + while (child) { + needs_check.push_back(child); + child = child->get_next(); + } + } + return is_branch_collapsed; +} + +void SceneTreeDock::_set_collapsed_recursive(TreeItem *p_item, bool p_collapsed) { + + List<TreeItem *> to_collapse; + to_collapse.push_back(p_item); + + while (!to_collapse.empty()) { + + TreeItem *item = to_collapse.back()->get(); + to_collapse.pop_back(); + + item->set_collapsed(p_collapsed); + + TreeItem *child = item->get_children(); + while (child) { + to_collapse.push_back(child); + child = child->get_next(); + } + } +} + void SceneTreeDock::_script_created(Ref<Script> p_script) { List<Node *> selected = editor_selection->get_selected_node_list(); @@ -1495,6 +1727,8 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) { Ref<Script> existing = E->get()->get_script(); editor_data->get_undo_redo().add_do_method(E->get(), "set_script", p_script.get_ref_ptr()); editor_data->get_undo_redo().add_undo_method(E->get(), "set_script", existing); + editor_data->get_undo_redo().add_do_method(this, "_update_script_button"); + editor_data->get_undo_redo().add_undo_method(this, "_update_script_button"); } editor_data->get_undo_redo().commit_action(); @@ -1503,30 +1737,32 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) { _update_script_button(); } -void SceneTreeDock::_toggle_editable_children() { +void SceneTreeDock::_script_creation_closed() { + script_create_dialog->disconnect("script_created", this, "_script_created"); + script_create_dialog->disconnect("popup_hide", this, "_script_creation_closed"); +} + +void SceneTreeDock::_toggle_editable_children_from_selection() { + List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); - if (e) { - Node *node = e->get(); - if (node) { - bool editable = EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(node); - int editable_item_idx = menu->get_item_idx_from_text(TTR("Editable Children")); - int placeholder_item_idx = menu->get_item_idx_from_text(TTR("Load As Placeholder")); - editable = !editable; + if (e) { + _toggle_editable_children(e->get()); + } +} - EditorNode::get_singleton()->get_edited_scene()->set_editable_instance(node, editable); +void SceneTreeDock::_toggle_editable_children(Node *p_node) { - menu->set_item_checked(editable_item_idx, editable); - if (editable) { - node->set_scene_instance_load_placeholder(false); - menu->set_item_checked(placeholder_item_idx, false); - } + if (p_node) { + bool editable = !EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(p_node); + EditorNode::get_singleton()->get_edited_scene()->set_editable_instance(p_node, editable); + if (editable) + p_node->set_scene_instance_load_placeholder(false); - SpatialEditor::get_singleton()->update_all_gizmos(node); + SpatialEditor::get_singleton()->update_all_gizmos(p_node); - scene_tree->update_tree(); - } + scene_tree->update_tree(); } } @@ -1582,9 +1818,9 @@ void SceneTreeDock::_delete_confirm() { List<Node *> owned; n->get_owned_by(n->get_owner(), &owned); Array owners; - for (List<Node *>::Element *E = owned.front(); E; E = E->next()) { + for (List<Node *>::Element *F = owned.front(); F; F = F->next()) { - owners.push_back(E->get()); + owners.push_back(F->get()); } editor_data->get_undo_redo().add_do_method(n->get_parent(), "remove_child", n); @@ -1615,7 +1851,12 @@ void SceneTreeDock::_delete_confirm() { } void SceneTreeDock::_update_script_button() { - if (EditorNode::get_singleton()->get_editor_selection()->get_selection().size() == 0) { + + if (!profile_allow_script_editing) { + + button_create_script->hide(); + button_clear_script->hide(); + } else if (EditorNode::get_singleton()->get_editor_selection()->get_selection().size() == 0) { button_create_script->hide(); button_clear_script->hide(); } else if (EditorNode::get_singleton()->get_editor_selection()->get_selection().size() == 1) { @@ -1628,10 +1869,10 @@ void SceneTreeDock::_update_script_button() { button_clear_script->show(); } } else { - button_create_script->show(); - List<Node *> selection = EditorNode::get_singleton()->get_editor_selection()->get_selected_node_list(); - for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { - Node *n = E->get(); + button_create_script->hide(); + Array selection = editor_selection->get_selected_nodes(); + for (int i = 0; i < selection.size(); i++) { + Node *n = Object::cast_to<Node>(selection[i]); if (!n->get_script().is_null()) { button_clear_script->show(); return; @@ -1655,6 +1896,71 @@ void SceneTreeDock::_selection_changed() { _update_script_button(); } +Node *SceneTreeDock::_get_selection_group_tail(Node *p_node, List<Node *> p_list) { + + Node *tail = p_node; + Node *parent = tail->get_parent(); + + for (int i = p_node->get_position_in_parent(); i < parent->get_child_count(); i++) { + Node *sibling = parent->get_child(i); + + if (p_list.find(sibling)) + tail = sibling; + else + break; + } + + return tail; +} + +void SceneTreeDock::_do_create(Node *p_parent) { + Object *c = create_dialog->instance_selected(); + + ERR_FAIL_COND(!c); + Node *child = Object::cast_to<Node>(c); + ERR_FAIL_COND(!child); + + editor_data->get_undo_redo().create_action(TTR("Create Node")); + + if (edited_scene) { + + editor_data->get_undo_redo().add_do_method(p_parent, "add_child", child); + editor_data->get_undo_redo().add_do_method(child, "set_owner", edited_scene); + editor_data->get_undo_redo().add_do_method(editor_selection, "clear"); + editor_data->get_undo_redo().add_do_method(editor_selection, "add_node", child); + editor_data->get_undo_redo().add_do_reference(child); + editor_data->get_undo_redo().add_undo_method(p_parent, "remove_child", child); + + String new_name = p_parent->validate_child_name(child); + ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); + editor_data->get_undo_redo().add_do_method(sed, "live_debug_create_node", edited_scene->get_path_to(p_parent), child->get_class(), new_name); + editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)).plus_file(new_name))); + + } else { + + editor_data->get_undo_redo().add_do_method(editor, "set_edited_scene", child); + editor_data->get_undo_redo().add_do_method(scene_tree, "update_tree"); + editor_data->get_undo_redo().add_do_reference(child); + editor_data->get_undo_redo().add_undo_method(editor, "set_edited_scene", (Object *)NULL); + } + + editor_data->get_undo_redo().commit_action(); + editor->push_item(c); + editor_selection->clear(); + editor_selection->add_node(child); + if (Object::cast_to<Control>(c)) { + //make editor more comfortable, so some controls don't appear super shrunk + Control *ct = Object::cast_to<Control>(c); + + Size2 ms = ct->get_minimum_size(); + if (ms.width < 4) + ms.width = 40; + if (ms.height < 4) + ms.height = 40; + ct->set_size(ms); + } +} + void SceneTreeDock::_create() { if (current_option == TOOL_NEW) { @@ -1673,88 +1979,105 @@ void SceneTreeDock::_create() { ERR_FAIL_COND(!parent); } - Object *c = create_dialog->instance_selected(); - - ERR_FAIL_COND(!c); - Node *child = Object::cast_to<Node>(c); - ERR_FAIL_COND(!child); - - editor_data->get_undo_redo().create_action(TTR("Create Node")); + _do_create(parent); - if (edited_scene) { + } else if (current_option == TOOL_REPLACE) { + List<Node *> selection = editor_selection->get_selected_node_list(); + ERR_FAIL_COND(selection.size() <= 0); - editor_data->get_undo_redo().add_do_method(parent, "add_child", child); - editor_data->get_undo_redo().add_do_method(child, "set_owner", edited_scene); - editor_data->get_undo_redo().add_do_method(editor_selection, "clear"); - editor_data->get_undo_redo().add_do_method(editor_selection, "add_node", child); - editor_data->get_undo_redo().add_do_reference(child); - editor_data->get_undo_redo().add_undo_method(parent, "remove_child", child); + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change type of node(s)")); - String new_name = parent->validate_child_name(child); - ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger(); - editor_data->get_undo_redo().add_do_method(sed, "live_debug_create_node", edited_scene->get_path_to(parent), child->get_class(), new_name); - editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(new_name))); + for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + Node *n = E->get(); + ERR_FAIL_COND(!n); - } else { + Object *c = create_dialog->instance_selected(); - editor_data->get_undo_redo().add_do_method(editor, "set_edited_scene", child); - editor_data->get_undo_redo().add_do_method(scene_tree, "update_tree"); - editor_data->get_undo_redo().add_do_reference(child); - editor_data->get_undo_redo().add_undo_method(editor, "set_edited_scene", (Object *)NULL); - } + ERR_FAIL_COND(!c); + Node *newnode = Object::cast_to<Node>(c); + ERR_FAIL_COND(!newnode); - editor_data->get_undo_redo().commit_action(); - editor->push_item(c); - editor_selection->clear(); - editor_selection->add_node(child); - if (Object::cast_to<Control>(c)) { - //make editor more comfortable, so some controls don't appear super shrunk - Control *ct = Object::cast_to<Control>(c); - - Size2 ms = ct->get_minimum_size(); - if (ms.width < 4) - ms.width = 40; - if (ms.height < 4) - ms.height = 40; - ct->set_size(ms); + ur->add_do_method(this, "replace_node", n, newnode, true, false); + ur->add_do_reference(newnode); + ur->add_undo_method(this, "replace_node", newnode, n, false, false); + ur->add_undo_reference(n); } - } else if (current_option == TOOL_REPLACE) { + ur->commit_action(); + } else if (current_option == TOOL_REPARENT_TO_NEW_NODE) { List<Node *> selection = editor_selection->get_selected_node_list(); ERR_FAIL_COND(selection.size() <= 0); - for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + + // Find top level node in selection + bool only_one_top_node = true; + + Node *first = selection.front()->get(); + ERR_FAIL_COND(!first); + int smaller_path_to_top = first->get_path_to(scene_root).get_name_count(); + Node *top_node = first; + + for (List<Node *>::Element *E = selection.front()->next(); E; E = E->next()) { Node *n = E->get(); ERR_FAIL_COND(!n); - Object *c = create_dialog->instance_selected(); + int path_length = n->get_path_to(scene_root).get_name_count(); - ERR_FAIL_COND(!c); - Node *newnode = Object::cast_to<Node>(c); - ERR_FAIL_COND(!newnode); + if (top_node != n) { + if (smaller_path_to_top > path_length) { + top_node = n; + smaller_path_to_top = path_length; + only_one_top_node = true; + } else if (smaller_path_to_top == path_length) { + if (only_one_top_node && top_node->get_parent() != n->get_parent()) + only_one_top_node = false; + } + } + } - replace_node(n, newnode); + Node *parent = NULL; + if (only_one_top_node) + parent = top_node->get_parent(); + else + parent = top_node->get_parent()->get_parent(); + + _do_create(parent); + + Vector<Node *> nodes; + for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + nodes.push_back(E->get()); } + + // This works because editor_selection was cleared and populated with last created node in _do_create() + Node *last_created = editor_selection->get_selected_node_list().front()->get(); + _do_reparent(last_created, -1, nodes, true); } + + scene_tree->get_scene_tree()->call_deferred("grab_focus"); } -void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node) { +void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties, bool p_remove_old) { Node *n = p_node; Node *newnode = p_by_node; - Node *default_oldnode = Object::cast_to<Node>(ClassDB::instance(n->get_class())); - List<PropertyInfo> pinfo; - n->get_property_list(&pinfo); - for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) - continue; - if (E->get().name == "__meta__") - continue; - if (default_oldnode->get(E->get().name) != n->get(E->get().name)) { - newnode->set(E->get().name, n->get(E->get().name)); + if (p_keep_properties) { + Node *default_oldnode = Object::cast_to<Node>(ClassDB::instance(n->get_class())); + List<PropertyInfo> pinfo; + n->get_property_list(&pinfo); + + for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { + if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) + continue; + if (E->get().name == "__meta__") + continue; + if (default_oldnode->get(E->get().name) != n->get(E->get().name)) { + newnode->set(E->get().name, n->get(E->get().name)); + } } + + memdelete(default_oldnode); } - memdelete(default_oldnode); editor->push_item(NULL); @@ -1772,7 +2095,7 @@ void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node) { Object::Connection &c = F->get(); if (!(c.flags & Object::CONNECT_PERSIST)) continue; - newnode->connect(c.signal, c.target, c.method, varray(), Object::CONNECT_PERSIST); + newnode->connect(c.signal, c.target, c.method, c.binds, Object::CONNECT_PERSIST); } } @@ -1797,16 +2120,20 @@ void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node) { Node *c = newnode->get_child(i); c->call("set_transform", c->call("get_transform")); } - editor_data->get_undo_redo().clear_history(); + //p_remove_old was added to support undo + if (p_remove_old) + editor_data->get_undo_redo().clear_history(); newnode->set_name(newname); editor->push_item(newnode); - memdelete(n); + if (p_remove_old) { + memdelete(n); - while (to_erase.front()) { - memdelete(to_erase.front()->get()); - to_erase.pop_front(); + while (to_erase.front()) { + memdelete(to_erase.front()->get()); + to_erase.pop_front(); + } } } @@ -1822,7 +2149,7 @@ void SceneTreeDock::set_selected(Node *p_node, bool p_emit_selected) { void SceneTreeDock::import_subscene() { - import_subscene_dialog->popup_centered_ratio(); + import_subscene_dialog->popup_centered_clamped(Size2(500, 800) * EDSCALE, 0.8); } void SceneTreeDock::_import_subscene() { @@ -1924,8 +2251,7 @@ void SceneTreeDock::_normalize_drop(Node *&to_node, int &to_pos, int p_type) { //drop at above selected node if (to_node == EditorNode::get_singleton()->get_edited_scene()) { to_node = NULL; - ERR_EXPLAIN("Cannot perform drop above the root node!"); - ERR_FAIL(); + ERR_FAIL_MSG("Cannot perform drop above the root node!"); } to_pos = to_node->get_index(); @@ -1986,27 +2312,24 @@ void SceneTreeDock::_script_dropped(String p_file, NodePath p_to) { void SceneTreeDock::_nodes_dragged(Array p_nodes, NodePath p_to, int p_type) { - Vector<Node *> nodes; - Node *to_node; - - for (int i = 0; i < p_nodes.size(); i++) { - Node *n = get_node((p_nodes[i])); - if (n) { - nodes.push_back(n); - } - } + List<Node *> selection = editor_selection->get_selected_node_list(); - if (nodes.size() == 0) - return; + if (selection.empty()) + return; //nothing to reparent - to_node = get_node(p_to); + Node *to_node = get_node(p_to); if (!to_node) return; + Vector<Node *> nodes; + for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + nodes.push_back(E->get()); + } + int to_pos = -1; _normalize_drop(to_node, to_pos, p_type); - _do_reparent(to_node, to_pos, nodes, true); + _do_reparent(to_node, to_pos, nodes, !Input::get_singleton()->is_key_pressed(KEY_SHIFT)); } void SceneTreeDock::_add_children_to_popup(Object *p_obj, int p_depth) { @@ -2045,11 +2368,14 @@ void SceneTreeDock::_add_children_to_popup(Object *p_obj, int p_depth) { } void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { + if (!EditorNode::get_singleton()->get_edited_scene()) { menu->clear(); - menu->add_icon_shortcut(get_icon("Add", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/add_child_node"), TOOL_NEW); - menu->add_icon_shortcut(get_icon("Instance", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/instance_scene"), TOOL_INSTANCE); + if (profile_allow_editing) { + menu->add_icon_shortcut(get_icon("Add", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/add_child_node"), TOOL_NEW); + menu->add_icon_shortcut(get_icon("Instance", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/instance_scene"), TOOL_INSTANCE); + } menu->set_size(Size2(1, 1)); menu->set_position(p_menu_pos); @@ -2065,84 +2391,113 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { menu->clear(); Ref<Script> existing_script; + bool exisiting_script_removable = true; if (selection.size() == 1) { Node *selected = selection[0]; - subresources.clear(); - menu_subresources->clear(); - menu_subresources->set_size(Size2(1, 1)); - _add_children_to_popup(selection.front()->get(), 0); - if (menu->get_item_count() > 0) - menu->add_separator(); + if (profile_allow_editing) { + subresources.clear(); + menu_subresources->clear(); + menu_subresources->set_size(Size2(1, 1)); + _add_children_to_popup(selection.front()->get(), 0); + if (menu->get_item_count() > 0) + menu->add_separator(); - menu->add_icon_shortcut(get_icon("Add", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/add_child_node"), TOOL_NEW); - menu->add_icon_shortcut(get_icon("Instance", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/instance_scene"), TOOL_INSTANCE); + menu->add_icon_shortcut(get_icon("Add", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/add_child_node"), TOOL_NEW); + menu->add_icon_shortcut(get_icon("Instance", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/instance_scene"), TOOL_INSTANCE); + } + menu->add_icon_shortcut(get_icon("Collapse", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/expand_collapse_all"), TOOL_EXPAND_COLLAPSE); menu->add_separator(); + existing_script = selected->get_script(); - } - if (!existing_script.is_valid()) { - menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT); - } - if (selection.size() > 1 || existing_script.is_valid()) { - menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/clear_script"), TOOL_CLEAR_SCRIPT); - menu->add_icon_shortcut(get_icon("ScriptExtend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/extend_script"), TOOL_ATTACH_SCRIPT); + if (EditorNode::get_singleton()->get_object_custom_type_base(selected) == existing_script) { + exisiting_script_removable = false; + } } - menu->add_separator(); - if (selection.size() == 1) { - menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/rename"), TOOL_RENAME); - } - menu->add_icon_shortcut(get_icon("Reload", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/change_node_type"), TOOL_REPLACE); + if (profile_allow_script_editing) { - if (scene_tree->get_selected() != edited_scene) { + if (selection.size() == 1) { + if (!existing_script.is_valid()) { + menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT); + } else { + menu->add_icon_shortcut(get_icon("ScriptExtend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/extend_script"), TOOL_ATTACH_SCRIPT); + } + } + if (selection.size() > 1 || (existing_script.is_valid() && exisiting_script_removable)) { + menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/clear_script"), TOOL_CLEAR_SCRIPT); + } menu->add_separator(); - menu->add_icon_shortcut(get_icon("MoveUp", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/move_up"), TOOL_MOVE_UP); - menu->add_icon_shortcut(get_icon("MoveDown", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/move_down"), TOOL_MOVE_DOWN); - menu->add_icon_shortcut(get_icon("Duplicate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/duplicate"), TOOL_DUPLICATE); - menu->add_icon_shortcut(get_icon("Reparent", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/reparent"), TOOL_REPARENT); + } + + if (profile_allow_editing) { + if (selection.size() == 1) { + menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/rename"), TOOL_RENAME); + } + menu->add_icon_shortcut(get_icon("Reload", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/change_node_type"), TOOL_REPLACE); + + if (scene_tree->get_selected() != edited_scene) { + menu->add_separator(); + menu->add_icon_shortcut(get_icon("MoveUp", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/move_up"), TOOL_MOVE_UP); + menu->add_icon_shortcut(get_icon("MoveDown", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/move_down"), TOOL_MOVE_DOWN); + menu->add_icon_shortcut(get_icon("Duplicate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/duplicate"), TOOL_DUPLICATE); + menu->add_icon_shortcut(get_icon("Reparent", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/reparent"), TOOL_REPARENT); + menu->add_icon_shortcut(get_icon("ReparentToNewNode", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/reparent_to_new_node"), TOOL_REPARENT_TO_NEW_NODE); + menu->add_icon_shortcut(get_icon("NewRoot", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/make_root"), TOOL_MAKE_ROOT); + } } if (selection.size() == 1) { - menu->add_icon_shortcut(get_icon("NewRoot", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/make_root"), TOOL_MAKE_ROOT); - menu->add_separator(); - menu->add_icon_shortcut(get_icon("Blend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/merge_from_scene"), TOOL_MERGE_FROM_SCENE); - menu->add_icon_shortcut(get_icon("CreateNewSceneFrom", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/save_branch_as_scene"), TOOL_NEW_SCENE_FROM); - menu->add_separator(); + if (profile_allow_editing) { + menu->add_separator(); + menu->add_icon_shortcut(get_icon("Blend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/merge_from_scene"), TOOL_MERGE_FROM_SCENE); + menu->add_icon_shortcut(get_icon("CreateNewSceneFrom", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/save_branch_as_scene"), TOOL_NEW_SCENE_FROM); + menu->add_separator(); + } menu->add_icon_shortcut(get_icon("CopyNodePath", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/copy_node_path"), TOOL_COPY_NODE_PATH); + bool is_external = (selection[0]->get_filename() != ""); if (is_external) { bool is_inherited = selection[0]->get_scene_inherited_state() != NULL; bool is_top_level = selection[0]->get_owner() == NULL; if (is_inherited && is_top_level) { menu->add_separator(); - menu->add_item(TTR("Clear Inheritance"), TOOL_SCENE_CLEAR_INHERITANCE); + if (profile_allow_editing) { + menu->add_item(TTR("Clear Inheritance"), TOOL_SCENE_CLEAR_INHERITANCE); + } menu->add_icon_item(get_icon("Load", "EditorIcons"), TTR("Open in Editor"), TOOL_SCENE_OPEN_INHERITED); } else if (!is_top_level) { menu->add_separator(); bool editable = EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(selection[0]); bool placeholder = selection[0]->get_scene_instance_load_placeholder(); - menu->add_check_item(TTR("Editable Children"), TOOL_SCENE_EDITABLE_CHILDREN); - menu->add_check_item(TTR("Load As Placeholder"), TOOL_SCENE_USE_PLACEHOLDER); - menu->add_item(TTR("Make Local"), TOOL_SCENE_MAKE_LOCAL); + if (profile_allow_editing) { + menu->add_check_item(TTR("Editable Children"), TOOL_SCENE_EDITABLE_CHILDREN); + menu->add_check_item(TTR("Load As Placeholder"), TOOL_SCENE_USE_PLACEHOLDER); + menu->add_item(TTR("Make Local"), TOOL_SCENE_MAKE_LOCAL); + } menu->add_icon_item(get_icon("Load", "EditorIcons"), TTR("Open in Editor"), TOOL_SCENE_OPEN); - menu->set_item_checked(menu->get_item_idx_from_text(TTR("Editable Children")), editable); - menu->set_item_checked(menu->get_item_idx_from_text(TTR("Load As Placeholder")), placeholder); + if (profile_allow_editing) { + menu->set_item_checked(menu->get_item_idx_from_text(TTR("Editable Children")), editable); + menu->set_item_checked(menu->get_item_idx_from_text(TTR("Load As Placeholder")), placeholder); + } } } } - if (selection.size() > 1) { + if (profile_allow_editing && selection.size() > 1) { //this is not a commonly used action, it makes no sense for it to be where it was nor always present. menu->add_separator(); menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/batch_rename"), TOOL_BATCH_RENAME); } menu->add_separator(); - menu->add_icon_item(get_icon("Help", "EditorIcons"), TTR("Open documentation"), TOOL_OPEN_DOCUMENTATION); + menu->add_icon_item(get_icon("Help", "EditorIcons"), TTR("Open Documentation"), TOOL_OPEN_DOCUMENTATION); - menu->add_separator(); - menu->add_icon_shortcut(get_icon("Remove", "EditorIcons"), ED_SHORTCUT("scene_tree/delete", TTR("Delete Node(s)"), KEY_DELETE), TOOL_ERASE); + if (profile_allow_editing) { + menu->add_separator(); + menu->add_icon_shortcut(get_icon("Remove", "EditorIcons"), ED_SHORTCUT("scene_tree/delete", TTR("Delete Node(s)"), KEY_DELETE), TOOL_ERASE); + } menu->set_size(Size2(1, 1)); menu->set_position(p_menu_pos); menu->popup(); @@ -2214,6 +2569,7 @@ void SceneTreeDock::hide_tab_buttons() { void SceneTreeDock::_remote_tree_selected() { scene_tree->hide(); + create_root_dialog->hide(); if (remote_tree) remote_tree->show(); edit_remote->set_pressed(true); @@ -2266,7 +2622,7 @@ void SceneTreeDock::_update_create_root_dialog() { button->set_text(TTR(l)); String name = l.get_slicec(' ', 0); if (ScriptServer::is_global_class(name)) - name = ScriptServer::get_global_class_base(name); + name = ScriptServer::get_global_class_native_base(name); button->set_icon(EditorNode::get_singleton()->get_class_icon(name)); button->connect("pressed", this, "_favorite_root_selected", make_binds(l)); } @@ -2292,6 +2648,30 @@ void SceneTreeDock::_favorite_root_selected(const String &p_class) { _tool_selected(TOOL_CREATE_FAVORITE, false); } +void SceneTreeDock::_feature_profile_changed() { + + Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile(); + + if (profile.is_valid()) { + + profile_allow_editing = !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCENE_TREE); + profile_allow_script_editing = !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCRIPT); + + button_add->set_visible(profile_allow_editing); + button_instance->set_visible(profile_allow_editing); + scene_tree->set_can_rename(profile_allow_editing); + + } else { + button_add->set_visible(true); + button_instance->set_visible(true); + scene_tree->set_can_rename(true); + profile_allow_editing = true; + profile_allow_script_editing = true; + } + + _update_script_button(); +} + void SceneTreeDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_tool_selected"), &SceneTreeDock::_tool_selected, DEFVAL(false)); @@ -2301,16 +2681,18 @@ void SceneTreeDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_node_selected"), &SceneTreeDock::_node_selected); ClassDB::bind_method(D_METHOD("_node_renamed"), &SceneTreeDock::_node_renamed); ClassDB::bind_method(D_METHOD("_script_created"), &SceneTreeDock::_script_created); + ClassDB::bind_method(D_METHOD("_script_creation_closed"), &SceneTreeDock::_script_creation_closed); ClassDB::bind_method(D_METHOD("_load_request"), &SceneTreeDock::_load_request); ClassDB::bind_method(D_METHOD("_script_open_request"), &SceneTreeDock::_script_open_request); ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &SceneTreeDock::_unhandled_key_input); ClassDB::bind_method(D_METHOD("_input"), &SceneTreeDock::_input); ClassDB::bind_method(D_METHOD("_nodes_drag_begin"), &SceneTreeDock::_nodes_drag_begin); ClassDB::bind_method(D_METHOD("_delete_confirm"), &SceneTreeDock::_delete_confirm); - ClassDB::bind_method(D_METHOD("_toggle_editable_children"), &SceneTreeDock::_toggle_editable_children); + ClassDB::bind_method(D_METHOD("_toggle_editable_children_from_selection"), &SceneTreeDock::_toggle_editable_children_from_selection); ClassDB::bind_method(D_METHOD("_node_prerenamed"), &SceneTreeDock::_node_prerenamed); ClassDB::bind_method(D_METHOD("_import_subscene"), &SceneTreeDock::_import_subscene); ClassDB::bind_method(D_METHOD("_selection_changed"), &SceneTreeDock::_selection_changed); + ClassDB::bind_method(D_METHOD("_node_collapsed"), &SceneTreeDock::_node_collapsed); ClassDB::bind_method(D_METHOD("_new_scene_from"), &SceneTreeDock::_new_scene_from); ClassDB::bind_method(D_METHOD("_nodes_dragged"), &SceneTreeDock::_nodes_dragged); ClassDB::bind_method(D_METHOD("_files_dropped"), &SceneTreeDock::_files_dropped); @@ -2324,8 +2706,11 @@ void SceneTreeDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_script_button"), &SceneTreeDock::_update_script_button); ClassDB::bind_method(D_METHOD("_favorite_root_selected"), &SceneTreeDock::_favorite_root_selected); ClassDB::bind_method(D_METHOD("_update_create_root_dialog"), &SceneTreeDock::_update_create_root_dialog); + ClassDB::bind_method(D_METHOD("_feature_profile_changed"), &SceneTreeDock::_feature_profile_changed); ClassDB::bind_method(D_METHOD("instance"), &SceneTreeDock::instance); + ClassDB::bind_method(D_METHOD("get_tree_editor"), &SceneTreeDock::get_tree_editor); + ClassDB::bind_method(D_METHOD("replace_node"), &SceneTreeDock::replace_node); ADD_SIGNAL(MethodInfo("remote_tree_selected")); } @@ -2343,12 +2728,12 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel HBoxContainer *filter_hbc = memnew(HBoxContainer); filter_hbc->add_constant_override("separate", 0); - ToolButton *tb; ED_SHORTCUT("scene_tree/rename", TTR("Rename")); ED_SHORTCUT("scene_tree/batch_rename", TTR("Batch Rename"), KEY_MASK_CMD | KEY_F2); ED_SHORTCUT("scene_tree/add_child_node", TTR("Add Child Node"), KEY_MASK_CMD | KEY_A); ED_SHORTCUT("scene_tree/instance_scene", TTR("Instance Child Scene")); + ED_SHORTCUT("scene_tree/expand_collapse_all", TTR("Expand/Collapse All")); ED_SHORTCUT("scene_tree/change_node_type", TTR("Change Type")); ED_SHORTCUT("scene_tree/attach_script", TTR("Attach Script")); ED_SHORTCUT("scene_tree/extend_script", TTR("Extend Script")); @@ -2357,6 +2742,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel ED_SHORTCUT("scene_tree/move_down", TTR("Move Down"), KEY_MASK_CMD | KEY_DOWN); ED_SHORTCUT("scene_tree/duplicate", TTR("Duplicate"), KEY_MASK_CMD | KEY_D); ED_SHORTCUT("scene_tree/reparent", TTR("Reparent")); + ED_SHORTCUT("scene_tree/reparent_to_new_node", TTR("Reparent to New Node")); ED_SHORTCUT("scene_tree/make_root", TTR("Make Scene Root")); ED_SHORTCUT("scene_tree/merge_from_scene", TTR("Merge From Scene")); ED_SHORTCUT("scene_tree/save_branch_as_scene", TTR("Save Branch as Scene")); @@ -2364,19 +2750,17 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel ED_SHORTCUT("scene_tree/delete_no_confirm", TTR("Delete (No Confirm)"), KEY_MASK_SHIFT | KEY_DELETE); ED_SHORTCUT("scene_tree/delete", TTR("Delete"), KEY_DELETE); - tb = memnew(ToolButton); - tb->connect("pressed", this, "_tool_selected", make_binds(TOOL_NEW, false)); - tb->set_tooltip(TTR("Add/Create a New Node")); - tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/add_child_node")); - filter_hbc->add_child(tb); - button_add = tb; + button_add = memnew(ToolButton); + button_add->connect("pressed", this, "_tool_selected", make_binds(TOOL_NEW, false)); + button_add->set_tooltip(TTR("Add/Create a New Node.")); + button_add->set_shortcut(ED_GET_SHORTCUT("scene_tree/add_child_node")); + filter_hbc->add_child(button_add); - tb = memnew(ToolButton); - tb->connect("pressed", this, "_tool_selected", make_binds(TOOL_INSTANCE, false)); - tb->set_tooltip(TTR("Instance a scene file as a Node. Creates an inherited scene if no root node exists.")); - tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/instance_scene")); - filter_hbc->add_child(tb); - button_instance = tb; + button_instance = memnew(ToolButton); + button_instance->connect("pressed", this, "_tool_selected", make_binds(TOOL_INSTANCE, false)); + button_instance->set_tooltip(TTR("Instance a scene file as a Node. Creates an inherited scene if no root node exists.")); + button_instance->set_shortcut(ED_GET_SHORTCUT("scene_tree/instance_scene")); + filter_hbc->add_child(button_instance); vbc->add_child(filter_hbc); filter = memnew(LineEdit); @@ -2386,21 +2770,19 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel filter->add_constant_override("minimum_spaces", 0); filter->connect("text_changed", this, "_filter_changed"); - tb = memnew(ToolButton); - tb->connect("pressed", this, "_tool_selected", make_binds(TOOL_ATTACH_SCRIPT, false)); - tb->set_tooltip(TTR("Attach a new or existing script for the selected node.")); - tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/attach_script")); - filter_hbc->add_child(tb); - tb->hide(); - button_create_script = tb; - - tb = memnew(ToolButton); - tb->connect("pressed", this, "_tool_selected", make_binds(TOOL_CLEAR_SCRIPT, false)); - tb->set_tooltip(TTR("Clear a script for the selected node.")); - tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/clear_script")); - filter_hbc->add_child(tb); - button_clear_script = tb; - tb->hide(); + button_create_script = memnew(ToolButton); + button_create_script->connect("pressed", this, "_tool_selected", make_binds(TOOL_ATTACH_SCRIPT, false)); + button_create_script->set_tooltip(TTR("Attach a new or existing script for the selected node.")); + button_create_script->set_shortcut(ED_GET_SHORTCUT("scene_tree/attach_script")); + filter_hbc->add_child(button_create_script); + button_create_script->hide(); + + button_clear_script = memnew(ToolButton); + button_clear_script->connect("pressed", this, "_tool_selected", make_binds(TOOL_CLEAR_SCRIPT, false)); + button_clear_script->set_tooltip(TTR("Clear a script for the selected node.")); + button_clear_script->set_shortcut(ED_GET_SHORTCUT("scene_tree/clear_script")); + filter_hbc->add_child(button_clear_script); + button_clear_script->hide(); button_hb = memnew(HBoxContainer); vbc->add_child(button_hb); @@ -2457,8 +2839,8 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel add_child(rename_dialog); script_create_dialog = memnew(ScriptCreateDialog); + script_create_dialog->set_inheritance_base_type("Node"); add_child(script_create_dialog); - script_create_dialog->connect("script_created", this, "_script_created"); reparent_dialog = memnew(ReparentDialog); add_child(reparent_dialog); @@ -2478,7 +2860,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel editable_instance_remove_dialog = memnew(ConfirmationDialog); add_child(editable_instance_remove_dialog); - editable_instance_remove_dialog->connect("confirmed", this, "_toggle_editable_children"); + editable_instance_remove_dialog->connect("confirmed", this, "_toggle_editable_children_from_selection"); import_subscene_dialog = memnew(EditorSubScene); add_child(import_subscene_dialog); @@ -2508,6 +2890,9 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel set_process_input(true); set_process(true); + profile_allow_editing = true; + profile_allow_script_editing = true; + EDITOR_DEF("interface/editors/show_scene_tree_root_selection", true); EDITOR_DEF("_use_favorites_root_selection", false); } |