/**************************************************************************/ /* scene_tree_editor.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /**************************************************************************/ /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ #include "scene_tree_editor.h" #include "core/config/engine.h" #include "core/config/project_settings.h" #include "core/io/resource_loader.h" #include "core/object/callable_mp.h" #include "core/object/class_db.h" #include "core/object/script_language.h" #include "editor/animation/animation_player_editor_plugin.h" #include "editor/docks/editor_dock_manager.h" #include "editor/docks/groups_dock.h" #include "editor/docks/signals_dock.h" #include "editor/editor_node.h" #include "editor/editor_string_names.h" #include "editor/editor_undo_redo_manager.h" #include "editor/file_system/editor_file_system.h" #include "editor/gui/filter_line_edit.h" #include "editor/scene/3d/node_3d_editor_plugin.h" #include "editor/scene/canvas_item_editor_plugin.h" #include "editor/script/script_editor_plugin.h" #include "editor/settings/editor_settings.h" #include "editor/themes/editor_scale.h" #include "scene/2d/node_2d.h" #include "scene/gui/check_box.h" #include "scene/gui/check_button.h" #include "scene/gui/flow_container.h" #include "scene/gui/label.h" #include "scene/gui/texture_rect.h" #include "scene/main/scene_tree.h" #include "scene/main/window.h" #include "scene/resources/packed_scene.h" Node *SceneTreeEditor::get_scene_node() const { ERR_FAIL_COND_V(!is_inside_tree(), nullptr); return get_tree()->get_edited_scene_root(); } PackedStringArray SceneTreeEditor::_get_node_configuration_warnings(Node *p_node) { PackedStringArray warnings = p_node->get_configuration_warnings(); if (p_node == get_scene_node()) { Node2D *node_2d = Object::cast_to(p_node); if (node_2d) { // Note: Warn for Node2D but not all CanvasItems, don't warn for Control nodes. // Control nodes may have reasons to use a transformed root node like anchors. if (!node_2d->get_position().is_zero_approx()) { warnings.append(TTR("The root node of a scene is recommended to not be transformed, since instances of the scene will usually override this. Reset the transform and reload the scene to remove this warning.")); } } Node3D *node_3d = Object::cast_to(p_node); if (node_3d) { if (!node_3d->get_position().is_zero_approx()) { warnings.append(TTR("The root node of a scene is recommended to not be transformed, since instances of the scene will usually override this. Reset the transform and reload the scene to remove this warning.")); } } } return warnings; } PackedStringArray SceneTreeEditor::_get_node_accessibility_configuration_warnings(Node *p_node) { PackedStringArray warnings = p_node->get_accessibility_configuration_warnings(); return warnings; } void SceneTreeEditor::_gui_input(const Ref &p_event) { if (connect_to_script_mode) { return; // Don't do anything in this mode. } Ref mb = p_event; if (mb.is_valid()) { if (mb->get_button_index() == MouseButton::LEFT) { if (mb->is_pressed()) { Vector2 tree_mouse_pos = tree->get_transform().xform_inv(mb->get_position()); int tree_button_id = tree->get_button_id_at_position(tree_mouse_pos); if (tree_button_id == BUTTON_VISIBILITY) { TreeItem *tree_item = tree->get_item_at_position(tree_mouse_pos); ERR_FAIL_NULL(tree_item); NodePath node_path = tree_item->get_metadata(0); Node *node = get_node_or_null(node_path); if (node != nullptr) { visibility_drag_value = !node->call("is_visible"); visibility_drag_start_pos = tree_mouse_pos; visibility_drag_start_node = node->get_instance_id(); } } } else if (mb->is_released() && visibility_drag_start_node.is_valid()) { if (!visibility_drag_nodes.is_empty()) { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(vformat(TTR("Toggle Visibility of %d Nodes"), visibility_drag_nodes.size())); for (ObjectID id : visibility_drag_nodes) { Node *node = ObjectDB::get_instance(id); if (node != nullptr) { undo_redo->add_do_method(node, "set_visible", visibility_drag_value); undo_redo->add_undo_method(node, "set_visible", !visibility_drag_value); } } undo_redo->commit_action(false); } // Defer this so that `_cell_button_pressed` can still react to the dragging. callable_mp(this, &SceneTreeEditor::_reset_visibility_drag).call_deferred(); } } else if (mb->get_button_index() == MouseButton::RIGHT && visibility_drag_start_node.is_valid()) { _reset_visibility_drag(); accept_event(); } return; } Ref mm = p_event; if (mm.is_valid()) { if (visibility_drag_start_node.is_valid()) { Vector2 tree_mouse_pos = tree->get_transform().xform_inv(mm->get_position()); int tree_button_id = tree->get_button_id_at_position(tree_mouse_pos); bool crossed_drag_threshold = visibility_drag_start_pos.distance_to(tree_mouse_pos) > get_viewport()->get_drag_threshold(); if (tree_button_id == BUTTON_VISIBILITY && (crossed_drag_threshold || !visibility_drag_nodes.is_empty())) { Node *start_node = ObjectDB::get_instance(visibility_drag_start_node); if (start_node != nullptr && (bool)start_node->call("is_visible") != visibility_drag_value) { start_node->call("set_visible", visibility_drag_value); visibility_drag_nodes.push_back(visibility_drag_start_node); } TreeItem *tree_item = tree->get_item_at_position(tree_mouse_pos); ERR_FAIL_NULL(tree_item); NodePath node_path = tree_item->get_metadata(0); Node *node = get_node_or_null(node_path); if (node != nullptr && (bool)node->call("is_visible") != visibility_drag_value) { node->call("set_visible", visibility_drag_value); visibility_drag_nodes.push_back(node->get_instance_id()); } } } return; } if (p_event->is_action_type()) { if (p_event->is_action_pressed(SNAME("ui_cancel")) && visibility_drag_start_node.is_valid()) { _reset_visibility_drag(); accept_event(); } } } void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) { if (p_button != MouseButton::LEFT) { return; } if (connect_to_script_mode) { return; // Don't do anything in this mode. } TreeItem *item = Object::cast_to(p_item); ERR_FAIL_NULL(item); NodePath np = item->get_metadata(0); Node *n = get_node(np); ERR_FAIL_NULL(n); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); if (p_id == BUTTON_SUBSCENE) { if (n == get_scene_node()) { if (n && n->get_scene_inherited_state().is_valid()) { emit_signal(SNAME("open"), n->get_scene_inherited_state()->get_path()); } } else { emit_signal(SNAME("open"), n->get_scene_file_path()); } } else if (p_id == BUTTON_SCRIPT) { Ref