Add ability to drag-toggle visibility in scene tree editor

Co-authored-by: Kasper Arnklit Frandsen <kasper.arnklit@gmail.com>
This commit is contained in:
Mikael Hermansson
2026-04-15 15:34:12 +02:00
parent 3f63a400fa
commit 96a57910cb
2 changed files with 108 additions and 0 deletions
+101
View File
@@ -92,6 +92,90 @@ PackedStringArray SceneTreeEditor::_get_node_accessibility_configuration_warning
return warnings;
}
void SceneTreeEditor::_gui_input(const Ref<InputEvent> &p_event) {
if (connect_to_script_mode) {
return; // Don't do anything in this mode.
}
Ref<InputEventMouseButton> 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<Node>(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<InputEventMouseMotion> 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<Node>(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;
@@ -125,6 +209,10 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i
}
} else if (p_id == BUTTON_VISIBILITY) {
if (!visibility_drag_nodes.is_empty()) {
return;
}
undo_redo->create_action(TTR("Toggle Visible"));
_toggle_visible(n);
List<Node *> selection = editor_selection->get_top_selected_node_list();
@@ -1367,6 +1455,13 @@ void SceneTreeEditor::_tree_scroll_to_item(ObjectID p_item_id) {
}
}
void SceneTreeEditor::_reset_visibility_drag() {
visibility_drag_value = false;
visibility_drag_start_pos = Vector2();
visibility_drag_start_node = ObjectID();
visibility_drag_nodes.clear();
}
void SceneTreeEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
@@ -1430,6 +1525,11 @@ void SceneTreeEditor::_notification(int p_what) {
}
}
} break;
case NOTIFICATION_APPLICATION_FOCUS_OUT:
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
_reset_visibility_drag();
} break;
}
}
@@ -2194,6 +2294,7 @@ SceneTreeEditor::SceneTreeEditor(bool p_label, bool p_can_rename, bool p_can_ope
tree->connect("multi_selected", callable_mp(this, &SceneTreeEditor::_cell_multi_selected));
tree->connect("button_clicked", callable_mp(this, &SceneTreeEditor::_cell_button_pressed));
tree->connect("nothing_selected", callable_mp(this, &SceneTreeEditor::_deselect_items));
tree->connect(SceneStringName(gui_input), callable_mp(this, &SceneTreeEditor::_gui_input));
error = memnew(AcceptDialog);
add_child(error);
+7
View File
@@ -189,8 +189,14 @@ class SceneTreeEditor : public Control {
bool pending_selection_update = false;
Timer *update_node_tooltip_delay = nullptr;
bool visibility_drag_value = false;
Vector2 visibility_drag_start_pos;
ObjectID visibility_drag_start_node;
LocalVector<ObjectID> visibility_drag_nodes;
static void _bind_methods();
void _gui_input(const Ref<InputEvent> &p_event);
void _cell_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
void _toggle_visible(Node *p_node);
void _cell_multi_selected(Object *p_object, int p_cell, bool p_selected);
@@ -203,6 +209,7 @@ class SceneTreeEditor : public Control {
void _update_node_tooltip(Node *p_node, TreeItem *p_item);
void _queue_update_node_tooltip(Node *p_node, TreeItem *p_item);
void _tree_scroll_to_item(ObjectID p_item_id);
void _reset_visibility_drag();
void _selection_changed();
Node *get_scene_node() const;