You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-12-05 17:15:09 +00:00
Merge pull request #112729 from KoBeWi/multi_group_yoink
Edit groups on multiple nodes
This commit is contained in:
@@ -93,6 +93,41 @@ void GroupsEditor::_set_group_checked(const String &p_name, bool p_checked) {
|
||||
ti->set_checked(0, p_checked);
|
||||
}
|
||||
|
||||
void GroupsEditor::_add_to_group(const StringName &p_name, bool p_persist, const Array &p_nodes) {
|
||||
for (const Variant &v : p_nodes) {
|
||||
Node *node = Object::cast_to<Node>(v.get_validated_object());
|
||||
if (node) {
|
||||
node->add_to_group(p_name, p_persist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GroupsEditor::_remove_from_group(const StringName &p_name, const Array &p_nodes) {
|
||||
for (const Variant &v : p_nodes) {
|
||||
Node *node = Object::cast_to<Node>(v.get_validated_object());
|
||||
if (node) {
|
||||
node->remove_from_group(p_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GroupsEditor::_get_group_mask(const StringName &p_name, Array &r_nodes, bool p_invert) {
|
||||
for (Node *p_node : selection) {
|
||||
if (p_invert != p_node->is_in_group(p_name)) {
|
||||
r_nodes.push_back(p_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GroupsEditor::_can_edit(const StringName &p_group) {
|
||||
for (Node *p_node : selection) {
|
||||
if (!can_edit(p_node, p_group)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GroupsEditor::_has_group(const String &p_name) {
|
||||
return global_groups.has(p_name) || scene_groups.has(p_name);
|
||||
}
|
||||
@@ -102,7 +137,7 @@ void GroupsEditor::_modify_group(Object *p_item, int p_column, int p_id, MouseBu
|
||||
return;
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
if (selection.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -177,7 +212,7 @@ void GroupsEditor::_update_tree() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
if (selection.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -190,7 +225,9 @@ void GroupsEditor::_update_tree() {
|
||||
tree->clear();
|
||||
|
||||
List<Node::GroupInfo> groups;
|
||||
node->get_groups(&groups);
|
||||
for (Node *p_node : selection) {
|
||||
p_node->get_groups(&groups);
|
||||
}
|
||||
groups.sort_custom<_GroupInfoComparator>();
|
||||
|
||||
List<StringName> current_groups;
|
||||
@@ -220,7 +257,7 @@ void GroupsEditor::_update_tree() {
|
||||
|
||||
TreeItem *item = tree->create_item(local_root);
|
||||
item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
|
||||
item->set_editable(0, can_edit(node, E));
|
||||
item->set_editable(0, _can_edit(E));
|
||||
item->set_checked(0, current_groups.find(E) != nullptr);
|
||||
item->set_text(0, E);
|
||||
item->set_meta("__local", true);
|
||||
@@ -252,7 +289,7 @@ void GroupsEditor::_update_tree() {
|
||||
|
||||
TreeItem *item = tree->create_item(global_root);
|
||||
item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
|
||||
item->set_editable(0, can_edit(node, E));
|
||||
item->set_editable(0, _can_edit(E));
|
||||
item->set_checked(0, current_groups.find(E) != nullptr);
|
||||
item->set_text(0, E);
|
||||
item->set_meta("__local", false);
|
||||
@@ -307,15 +344,18 @@ void GroupsEditor::_cache_scene_groups(const ObjectID &p_id) {
|
||||
}
|
||||
}
|
||||
|
||||
void GroupsEditor::set_current(Node *p_node) {
|
||||
if (node == p_node) {
|
||||
void GroupsEditor::set_selection(const Vector<Node *> &p_nodes) {
|
||||
if (p_nodes.is_empty()) {
|
||||
holder->hide();
|
||||
select_a_node->show();
|
||||
selection.clear();
|
||||
return;
|
||||
}
|
||||
node = p_node;
|
||||
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
selection = p_nodes;
|
||||
|
||||
holder->show();
|
||||
select_a_node->hide();
|
||||
|
||||
if (scene_tree->get_edited_scene_root() != scene_root_node) {
|
||||
scene_root_node = scene_tree->get_edited_scene_root();
|
||||
@@ -338,8 +378,10 @@ void GroupsEditor::_item_edited() {
|
||||
if (ti->is_checked(0)) {
|
||||
undo_redo->create_action(TTR("Add to Group"));
|
||||
|
||||
undo_redo->add_do_method(node, "add_to_group", name, true);
|
||||
undo_redo->add_undo_method(node, "remove_from_group", name);
|
||||
Array nodes;
|
||||
_get_group_mask(name, nodes, true);
|
||||
undo_redo->add_do_method(this, "_add_to_group", name, true, nodes);
|
||||
undo_redo->add_undo_method(this, "_remove_from_group", name, nodes);
|
||||
|
||||
undo_redo->add_do_method(this, "_set_group_checked", name, true);
|
||||
undo_redo->add_undo_method(this, "_set_group_checked", name, false);
|
||||
@@ -353,8 +395,10 @@ void GroupsEditor::_item_edited() {
|
||||
} else {
|
||||
undo_redo->create_action(TTR("Remove from Group"));
|
||||
|
||||
undo_redo->add_do_method(node, "remove_from_group", name);
|
||||
undo_redo->add_undo_method(node, "add_to_group", name, true);
|
||||
Array nodes;
|
||||
_get_group_mask(name, nodes, false);
|
||||
undo_redo->add_do_method(this, "_remove_from_group", name, nodes);
|
||||
undo_redo->add_undo_method(this, "_add_to_group", name, true, nodes);
|
||||
|
||||
undo_redo->add_do_method(this, "_set_group_checked", name, false);
|
||||
undo_redo->add_undo_method(this, "_set_group_checked", name, true);
|
||||
@@ -489,8 +533,10 @@ void GroupsEditor::_confirm_add() {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Add to Group"));
|
||||
|
||||
undo_redo->add_do_method(node, "add_to_group", name, true);
|
||||
undo_redo->add_undo_method(node, "remove_from_group", name);
|
||||
Array nodes;
|
||||
_get_group_mask(name, nodes, true);
|
||||
undo_redo->add_do_method(this, "_add_to_group", name, true, nodes);
|
||||
undo_redo->add_undo_method(this, "_remove_from_group", name, nodes);
|
||||
|
||||
bool is_local = !global_group_button->is_pressed();
|
||||
if (is_local) {
|
||||
@@ -819,6 +865,9 @@ void GroupsEditor::_bind_methods() {
|
||||
ClassDB::bind_method("_rename_scene_group", &GroupsEditor::_rename_scene_group);
|
||||
ClassDB::bind_method("_remove_scene_group", &GroupsEditor::_remove_scene_group);
|
||||
ClassDB::bind_method("_set_group_checked", &GroupsEditor::_set_group_checked);
|
||||
|
||||
ClassDB::bind_method("_add_to_group", &GroupsEditor::_add_to_group);
|
||||
ClassDB::bind_method("_remove_from_group", &GroupsEditor::_remove_from_group);
|
||||
}
|
||||
|
||||
void GroupsEditor::_node_removed(Node *p_node) {
|
||||
@@ -834,15 +883,19 @@ void GroupsEditor::_node_removed(Node *p_node) {
|
||||
}
|
||||
|
||||
GroupsEditor::GroupsEditor() {
|
||||
node = nullptr;
|
||||
scene_tree = SceneTree::get_singleton();
|
||||
|
||||
ED_SHORTCUT("groups_editor/delete", TTRC("Delete"), Key::KEY_DELETE);
|
||||
ED_SHORTCUT("groups_editor/rename", TTRC("Rename"), Key::F2);
|
||||
ED_SHORTCUT_OVERRIDE("groups_editor/rename", "macos", Key::ENTER);
|
||||
|
||||
holder = memnew(VBoxContainer);
|
||||
holder->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
holder->hide();
|
||||
add_child(holder);
|
||||
|
||||
HBoxContainer *hbc = memnew(HBoxContainer);
|
||||
add_child(hbc);
|
||||
holder->add_child(hbc);
|
||||
|
||||
add = memnew(Button);
|
||||
add->set_theme_type_variation("FlatMenuButton");
|
||||
@@ -867,11 +920,21 @@ GroupsEditor::GroupsEditor() {
|
||||
tree->connect("button_clicked", callable_mp(this, &GroupsEditor::_modify_group));
|
||||
tree->connect("item_mouse_selected", callable_mp(this, &GroupsEditor::_item_mouse_selected));
|
||||
tree->connect(SceneStringName(gui_input), callable_mp(this, &GroupsEditor::_groups_gui_input));
|
||||
add_child(tree);
|
||||
holder->add_child(tree);
|
||||
|
||||
menu = memnew(PopupMenu);
|
||||
menu->connect(SceneStringName(id_pressed), callable_mp(this, &GroupsEditor::_menu_id_pressed));
|
||||
tree->add_child(menu);
|
||||
|
||||
select_a_node = memnew(Label);
|
||||
select_a_node->set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
select_a_node->set_text(TTRC("Select one or more nodes to edit their groups."));
|
||||
select_a_node->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
|
||||
select_a_node->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
select_a_node->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
|
||||
select_a_node->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
|
||||
select_a_node->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
|
||||
add_child(select_a_node);
|
||||
|
||||
ProjectSettingsEditor::get_singleton()->get_group_settings()->connect("group_changed", callable_mp(this, &GroupsEditor::_update_groups_and_tree));
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class GroupsEditor : public VBoxContainer {
|
||||
bool groups_dirty = false;
|
||||
bool update_groups_and_tree_queued = false;
|
||||
|
||||
Node *node = nullptr;
|
||||
LocalVector<Node *> selection;
|
||||
Node *scene_root_node = nullptr;
|
||||
SceneTree *scene_tree = nullptr;
|
||||
|
||||
@@ -73,9 +73,11 @@ class GroupsEditor : public VBoxContainer {
|
||||
|
||||
PopupMenu *menu = nullptr;
|
||||
|
||||
VBoxContainer *holder = nullptr;
|
||||
LineEdit *filter = nullptr;
|
||||
Button *add = nullptr;
|
||||
Tree *tree = nullptr;
|
||||
Label *select_a_node = nullptr;
|
||||
|
||||
HashMap<ObjectID, HashMap<StringName, bool>> scene_groups_cache;
|
||||
HashMap<StringName, bool> scene_groups_for_caching;
|
||||
@@ -122,6 +124,11 @@ class GroupsEditor : public VBoxContainer {
|
||||
|
||||
void _node_removed(Node *p_node);
|
||||
|
||||
void _add_to_group(const StringName &p_name, bool p_persist, const Array &p_nodes);
|
||||
void _remove_from_group(const StringName &p_name, const Array &p_nodes);
|
||||
void _get_group_mask(const StringName &p_name, Array &r_nodes, bool p_invert);
|
||||
bool _can_edit(const StringName &p_group);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
@@ -134,7 +141,7 @@ public:
|
||||
CONVERT_GROUP,
|
||||
};
|
||||
|
||||
void set_current(Node *p_node);
|
||||
void set_selection(const Vector<Node *> &p_nodes);
|
||||
|
||||
GroupsEditor();
|
||||
};
|
||||
|
||||
@@ -55,15 +55,7 @@ void NodeDock::save_layout_to_config(Ref<ConfigFile> &p_layout, const String &p_
|
||||
|
||||
void NodeDock::load_layout_from_config(const Ref<ConfigFile> &p_layout, const String &p_section) {
|
||||
const int current_tab = p_layout->get_value(p_section, "current_tab", 0);
|
||||
if (select_a_node->is_visible()) {
|
||||
if (current_tab == 0) {
|
||||
groups_button->set_pressed_no_signal(false);
|
||||
connections_button->set_pressed_no_signal(true);
|
||||
} else if (current_tab == 1) {
|
||||
groups_button->set_pressed_no_signal(true);
|
||||
connections_button->set_pressed_no_signal(false);
|
||||
}
|
||||
} else if (current_tab == 0) {
|
||||
if (current_tab == 0) {
|
||||
show_connections();
|
||||
} else if (current_tab == 1) {
|
||||
show_groups();
|
||||
@@ -83,32 +75,17 @@ void NodeDock::update_lists() {
|
||||
connections->update_tree();
|
||||
}
|
||||
|
||||
void NodeDock::set_object(Object *p_object) {
|
||||
connections->set_object(p_object);
|
||||
groups->set_current(Object::cast_to<Node>(p_object));
|
||||
void NodeDock::set_selection(const Vector<Object *> &p_objects) {
|
||||
connections->set_selection(p_objects);
|
||||
|
||||
if (p_object) {
|
||||
if (connections_button->is_pressed()) {
|
||||
connections->show();
|
||||
} else {
|
||||
groups->show();
|
||||
Vector<Node *> nodes;
|
||||
for (Object *obj : p_objects) {
|
||||
Node *n = Object::cast_to<Node>(obj);
|
||||
if (n) {
|
||||
nodes.append(n);
|
||||
}
|
||||
|
||||
if (Object::cast_to<Resource>(p_object)) {
|
||||
show_connections();
|
||||
groups_button->set_disabled(true);
|
||||
} else {
|
||||
groups_button->set_disabled(false);
|
||||
}
|
||||
|
||||
mode_hb->show();
|
||||
select_a_node->hide();
|
||||
} else {
|
||||
connections->hide();
|
||||
groups->hide();
|
||||
mode_hb->hide();
|
||||
select_a_node->show();
|
||||
}
|
||||
groups->set_selection(nodes);
|
||||
}
|
||||
|
||||
NodeDock::NodeDock() {
|
||||
@@ -123,13 +100,11 @@ NodeDock::NodeDock() {
|
||||
|
||||
mode_hb = memnew(HBoxContainer);
|
||||
main_vb->add_child(mode_hb);
|
||||
mode_hb->hide();
|
||||
|
||||
connections_button = memnew(Button);
|
||||
connections_button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
connections_button->set_text(TTRC("Signals"));
|
||||
connections_button->set_toggle_mode(true);
|
||||
connections_button->set_pressed(true);
|
||||
connections_button->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
connections_button->set_clip_text(true);
|
||||
mode_hb->add_child(connections_button);
|
||||
@@ -139,7 +114,6 @@ NodeDock::NodeDock() {
|
||||
groups_button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
groups_button->set_text(TTRC("Groups"));
|
||||
groups_button->set_toggle_mode(true);
|
||||
groups_button->set_pressed(false);
|
||||
groups_button->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
groups_button->set_clip_text(true);
|
||||
mode_hb->add_child(groups_button);
|
||||
@@ -155,15 +129,7 @@ NodeDock::NodeDock() {
|
||||
groups->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
groups->hide();
|
||||
|
||||
select_a_node = memnew(Label);
|
||||
select_a_node->set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
select_a_node->set_text(TTRC("Select a single node to edit its signals and groups, or select an independent resource to view its signals."));
|
||||
select_a_node->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
|
||||
select_a_node->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
select_a_node->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
|
||||
select_a_node->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
|
||||
select_a_node->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
|
||||
main_vb->add_child(select_a_node);
|
||||
show_connections();
|
||||
}
|
||||
|
||||
NodeDock::~NodeDock() {
|
||||
|
||||
@@ -47,8 +47,6 @@ class NodeDock : public EditorDock {
|
||||
|
||||
HBoxContainer *mode_hb = nullptr;
|
||||
|
||||
Label *select_a_node = nullptr;
|
||||
|
||||
private:
|
||||
inline static NodeDock *singleton = nullptr;
|
||||
|
||||
@@ -62,7 +60,7 @@ protected:
|
||||
virtual void load_layout_from_config(const Ref<ConfigFile> &p_layout, const String &p_section) override;
|
||||
|
||||
public:
|
||||
void set_object(Object *p_object);
|
||||
void set_selection(const Vector<Object *> &p_objects);
|
||||
|
||||
void show_groups();
|
||||
void show_connections();
|
||||
|
||||
@@ -1351,7 +1351,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
|
||||
undo_redo->add_undo_method(node, "set_scene_file_path", node->get_scene_file_path());
|
||||
_node_replace_owner(node, node, root);
|
||||
_node_strip_signal_inheritance(node);
|
||||
NodeDock::get_singleton()->set_object(node); // Refresh.
|
||||
NodeDock::get_singleton()->set_selection(Vector<Object *>{ node }); // Refresh.
|
||||
undo_redo->add_do_method(scene_tree, "update_tree");
|
||||
undo_redo->add_undo_method(scene_tree, "update_tree");
|
||||
undo_redo->commit_action();
|
||||
@@ -2855,7 +2855,7 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
|
||||
editor_history->cleanup_history();
|
||||
InspectorDock::get_singleton()->call("_prepare_history");
|
||||
InspectorDock::get_singleton()->update(nullptr);
|
||||
NodeDock::get_singleton()->set_object(nullptr);
|
||||
NodeDock::get_singleton()->set_selection(Vector<Object *>{});
|
||||
}
|
||||
|
||||
void SceneTreeDock::_update_script_button() {
|
||||
|
||||
Reference in New Issue
Block a user