Implement project-wide node groups
This commit is contained in:
parent
1f5d4a62e9
commit
958699a0c4
|
@ -281,6 +281,11 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
|
|||
if (autoloads.has(node_name)) {
|
||||
remove_autoload(node_name);
|
||||
}
|
||||
} else if (p_name.operator String().begins_with("global_group/")) {
|
||||
String group_name = p_name.operator String().get_slice("/", 1);
|
||||
if (global_groups.has(group_name)) {
|
||||
remove_global_group(group_name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (p_name == CoreStringNames::get_singleton()->_custom_features) {
|
||||
|
@ -327,6 +332,9 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
|
|||
autoload.path = path;
|
||||
}
|
||||
add_autoload(autoload);
|
||||
} else if (p_name.operator String().begins_with("global_group/")) {
|
||||
String group_name = p_name.operator String().get_slice("/", 1);
|
||||
add_global_group(group_name, p_value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -674,6 +682,8 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo
|
|||
|
||||
Compression::gzip_level = GLOBAL_GET("compression/formats/gzip/compression_level");
|
||||
|
||||
load_scene_groups_cache();
|
||||
|
||||
project_loaded = err == OK;
|
||||
return err;
|
||||
}
|
||||
|
@ -1241,6 +1251,73 @@ ProjectSettings::AutoloadInfo ProjectSettings::get_autoload(const StringName &p_
|
|||
return autoloads[p_name];
|
||||
}
|
||||
|
||||
const HashMap<StringName, String> &ProjectSettings::get_global_groups_list() const {
|
||||
return global_groups;
|
||||
}
|
||||
|
||||
void ProjectSettings::add_global_group(const StringName &p_name, const String &p_description) {
|
||||
ERR_FAIL_COND_MSG(p_name == StringName(), "Trying to add global group with no name.");
|
||||
global_groups[p_name] = p_description;
|
||||
}
|
||||
|
||||
void ProjectSettings::remove_global_group(const StringName &p_name) {
|
||||
ERR_FAIL_COND_MSG(!global_groups.has(p_name), "Trying to remove non-existent global group.");
|
||||
global_groups.erase(p_name);
|
||||
}
|
||||
|
||||
bool ProjectSettings::has_global_group(const StringName &p_name) const {
|
||||
return global_groups.has(p_name);
|
||||
}
|
||||
|
||||
void ProjectSettings::remove_scene_groups_cache(const StringName &p_path) {
|
||||
scene_groups_cache.erase(p_path);
|
||||
}
|
||||
|
||||
void ProjectSettings::add_scene_groups_cache(const StringName &p_path, const HashSet<StringName> &p_cache) {
|
||||
scene_groups_cache[p_path] = p_cache;
|
||||
}
|
||||
|
||||
void ProjectSettings::save_scene_groups_cache() {
|
||||
Ref<ConfigFile> cf;
|
||||
cf.instantiate();
|
||||
for (const KeyValue<StringName, HashSet<StringName>> &E : scene_groups_cache) {
|
||||
if (E.value.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
Array list;
|
||||
for (const StringName &group : E.value) {
|
||||
list.push_back(group);
|
||||
}
|
||||
cf->set_value(E.key, "groups", list);
|
||||
}
|
||||
cf->save(get_scene_groups_cache_path());
|
||||
}
|
||||
|
||||
String ProjectSettings::get_scene_groups_cache_path() const {
|
||||
return get_project_data_path().path_join("scene_groups_cache.cfg");
|
||||
}
|
||||
|
||||
void ProjectSettings::load_scene_groups_cache() {
|
||||
Ref<ConfigFile> cf;
|
||||
cf.instantiate();
|
||||
if (cf->load(get_scene_groups_cache_path()) == OK) {
|
||||
List<String> scene_paths;
|
||||
cf->get_sections(&scene_paths);
|
||||
for (const String &E : scene_paths) {
|
||||
Array scene_groups = cf->get_value(E, "groups", Array());
|
||||
HashSet<StringName> cache;
|
||||
for (int i = 0; i < scene_groups.size(); ++i) {
|
||||
cache.insert(scene_groups[i]);
|
||||
}
|
||||
add_scene_groups_cache(E, cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const HashMap<StringName, HashSet<StringName>> &ProjectSettings::get_scene_groups_cache() const {
|
||||
return scene_groups_cache;
|
||||
}
|
||||
|
||||
void ProjectSettings::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("has_setting", "name"), &ProjectSettings::has_setting);
|
||||
ClassDB::bind_method(D_METHOD("set_setting", "name", "value"), &ProjectSettings::set_setting);
|
||||
|
|
|
@ -106,6 +106,8 @@ protected:
|
|||
|
||||
LocalVector<String> hidden_prefixes;
|
||||
HashMap<StringName, AutoloadInfo> autoloads;
|
||||
HashMap<StringName, String> global_groups;
|
||||
HashMap<StringName, HashSet<StringName>> scene_groups_cache;
|
||||
|
||||
Array global_class_list;
|
||||
bool is_global_class_list_loaded = false;
|
||||
|
@ -208,6 +210,18 @@ public:
|
|||
bool has_autoload(const StringName &p_autoload) const;
|
||||
AutoloadInfo get_autoload(const StringName &p_name) const;
|
||||
|
||||
const HashMap<StringName, String> &get_global_groups_list() const;
|
||||
void add_global_group(const StringName &p_name, const String &p_description);
|
||||
void remove_global_group(const StringName &p_name);
|
||||
bool has_global_group(const StringName &p_name) const;
|
||||
|
||||
const HashMap<StringName, HashSet<StringName>> &get_scene_groups_cache() const;
|
||||
void add_scene_groups_cache(const StringName &p_path, const HashSet<StringName> &p_cache);
|
||||
void remove_scene_groups_cache(const StringName &p_path);
|
||||
void save_scene_groups_cache();
|
||||
String get_scene_groups_cache_path() const;
|
||||
void load_scene_groups_cache();
|
||||
|
||||
ProjectSettings();
|
||||
~ProjectSettings();
|
||||
};
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "editor/editor_paths.h"
|
||||
#include "editor/editor_resource_preview.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
|
||||
EditorFileSystem *EditorFileSystem::singleton = nullptr;
|
||||
//the name is the version, to keep compatibility with different versions of Godot
|
||||
|
@ -615,6 +616,9 @@ bool EditorFileSystem::_update_scan_actions() {
|
|||
if (ClassDB::is_parent_class(ia.new_file->type, SNAME("Script"))) {
|
||||
_queue_update_script_class(ia.dir->get_file_path(idx));
|
||||
}
|
||||
if (ia.new_file->type == SNAME("PackedScene")) {
|
||||
_queue_update_scene_groups(ia.dir->get_file_path(idx));
|
||||
}
|
||||
|
||||
} break;
|
||||
case ItemAction::ACTION_FILE_REMOVE: {
|
||||
|
@ -624,6 +628,9 @@ bool EditorFileSystem::_update_scan_actions() {
|
|||
if (ClassDB::is_parent_class(ia.dir->files[idx]->type, SNAME("Script"))) {
|
||||
_queue_update_script_class(ia.dir->get_file_path(idx));
|
||||
}
|
||||
if (ia.dir->files[idx]->type == SNAME("PackedScene")) {
|
||||
_queue_update_scene_groups(ia.dir->get_file_path(idx));
|
||||
}
|
||||
|
||||
_delete_internal_files(ia.dir->files[idx]->file);
|
||||
memdelete(ia.dir->files[idx]);
|
||||
|
@ -662,6 +669,9 @@ bool EditorFileSystem::_update_scan_actions() {
|
|||
if (ClassDB::is_parent_class(ia.dir->files[idx]->type, SNAME("Script"))) {
|
||||
_queue_update_script_class(full_path);
|
||||
}
|
||||
if (ia.dir->files[idx]->type == SNAME("PackedScene")) {
|
||||
_queue_update_scene_groups(full_path);
|
||||
}
|
||||
|
||||
reloads.push_back(full_path);
|
||||
|
||||
|
@ -732,6 +742,7 @@ void EditorFileSystem::scan() {
|
|||
_update_scan_actions();
|
||||
scanning = false;
|
||||
_update_pending_script_classes();
|
||||
_update_pending_scene_groups();
|
||||
emit_signal(SNAME("filesystem_changed"));
|
||||
emit_signal(SNAME("sources_changed"), sources_changed.size() > 0);
|
||||
first_scan = false;
|
||||
|
@ -942,6 +953,9 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, Ref<DirAc
|
|||
if (ClassDB::is_parent_class(fi->type, SNAME("Script"))) {
|
||||
_queue_update_script_class(path);
|
||||
}
|
||||
if (fi->type == SNAME("PackedScene")) {
|
||||
_queue_update_scene_groups(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1196,6 +1210,7 @@ void EditorFileSystem::scan_changes() {
|
|||
_scan_fs_changes(filesystem, sp);
|
||||
bool changed = _update_scan_actions();
|
||||
_update_pending_script_classes();
|
||||
_update_pending_scene_groups();
|
||||
if (changed) {
|
||||
emit_signal(SNAME("filesystem_changed"));
|
||||
}
|
||||
|
@ -1262,6 +1277,7 @@ void EditorFileSystem::_notification(int p_what) {
|
|||
}
|
||||
bool changed = _update_scan_actions();
|
||||
_update_pending_script_classes();
|
||||
_update_pending_scene_groups();
|
||||
if (changed) {
|
||||
emit_signal(SNAME("filesystem_changed"));
|
||||
}
|
||||
|
@ -1281,6 +1297,7 @@ void EditorFileSystem::_notification(int p_what) {
|
|||
thread.wait_to_finish();
|
||||
_update_scan_actions();
|
||||
_update_pending_script_classes();
|
||||
_update_pending_scene_groups();
|
||||
emit_signal(SNAME("filesystem_changed"));
|
||||
emit_signal(SNAME("sources_changed"), sources_changed.size() > 0);
|
||||
first_scan = false;
|
||||
|
@ -1635,6 +1652,65 @@ void EditorFileSystem::_queue_update_script_class(const String &p_path) {
|
|||
update_script_mutex.unlock();
|
||||
}
|
||||
|
||||
void EditorFileSystem::_update_scene_groups() {
|
||||
update_scene_mutex.lock();
|
||||
|
||||
for (const String &path : update_scene_paths) {
|
||||
ProjectSettings::get_singleton()->remove_scene_groups_cache(path);
|
||||
|
||||
int index = -1;
|
||||
EditorFileSystemDirectory *efd = find_file(path, &index);
|
||||
|
||||
if (!efd || index < 0) {
|
||||
// The file was removed.
|
||||
continue;
|
||||
}
|
||||
|
||||
const HashSet<StringName> scene_groups = _get_scene_groups(path);
|
||||
if (!scene_groups.is_empty()) {
|
||||
ProjectSettings::get_singleton()->add_scene_groups_cache(path, scene_groups);
|
||||
}
|
||||
}
|
||||
|
||||
update_scene_paths.clear();
|
||||
update_scene_mutex.unlock();
|
||||
|
||||
ProjectSettings::get_singleton()->save_scene_groups_cache();
|
||||
}
|
||||
|
||||
void EditorFileSystem::_update_pending_scene_groups() {
|
||||
if (!FileAccess::exists(ProjectSettings::get_singleton()->get_scene_groups_cache_path())) {
|
||||
_get_all_scenes(get_filesystem(), update_scene_paths);
|
||||
_update_scene_groups();
|
||||
} else if (!update_scene_paths.is_empty()) {
|
||||
_update_scene_groups();
|
||||
}
|
||||
}
|
||||
|
||||
void EditorFileSystem::_queue_update_scene_groups(const String &p_path) {
|
||||
update_scene_mutex.lock();
|
||||
update_scene_paths.insert(p_path);
|
||||
update_scene_mutex.unlock();
|
||||
}
|
||||
|
||||
void EditorFileSystem::_get_all_scenes(EditorFileSystemDirectory *p_dir, HashSet<String> &r_list) {
|
||||
for (int i = 0; i < p_dir->get_file_count(); i++) {
|
||||
if (p_dir->get_file_type(i) == SNAME("PackedScene")) {
|
||||
r_list.insert(p_dir->get_file_path(i));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < p_dir->get_subdir_count(); i++) {
|
||||
_get_all_scenes(p_dir->get_subdir(i), r_list);
|
||||
}
|
||||
}
|
||||
|
||||
HashSet<StringName> EditorFileSystem::_get_scene_groups(const String &p_path) {
|
||||
Ref<PackedScene> packed_scene = ResourceLoader::load(p_path);
|
||||
ERR_FAIL_COND_V(packed_scene.is_null(), HashSet<StringName>());
|
||||
return packed_scene->get_state()->get_all_groups();
|
||||
}
|
||||
|
||||
void EditorFileSystem::update_file(const String &p_file) {
|
||||
ERR_FAIL_COND(p_file.is_empty());
|
||||
EditorFileSystemDirectory *fs = nullptr;
|
||||
|
@ -1658,12 +1734,16 @@ void EditorFileSystem::update_file(const String &p_file) {
|
|||
if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) {
|
||||
_queue_update_script_class(p_file);
|
||||
}
|
||||
if (fs->files[cpos]->type == SNAME("PackedScene")) {
|
||||
_queue_update_scene_groups(p_file);
|
||||
}
|
||||
|
||||
memdelete(fs->files[cpos]);
|
||||
fs->files.remove_at(cpos);
|
||||
}
|
||||
|
||||
_update_pending_script_classes();
|
||||
_update_pending_scene_groups();
|
||||
call_deferred(SNAME("emit_signal"), "filesystem_changed"); //update later
|
||||
return;
|
||||
}
|
||||
|
@ -1730,8 +1810,12 @@ void EditorFileSystem::update_file(const String &p_file) {
|
|||
if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) {
|
||||
_queue_update_script_class(p_file);
|
||||
}
|
||||
if (fs->files[cpos]->type == SNAME("PackedScene")) {
|
||||
_queue_update_scene_groups(p_file);
|
||||
}
|
||||
|
||||
_update_pending_script_classes();
|
||||
_update_pending_scene_groups();
|
||||
call_deferred(SNAME("emit_signal"), "filesystem_changed"); //update later
|
||||
}
|
||||
|
||||
|
@ -2341,6 +2425,7 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) {
|
|||
|
||||
_save_filesystem_cache();
|
||||
_update_pending_script_classes();
|
||||
_update_pending_scene_groups();
|
||||
importing = false;
|
||||
if (!is_scanning()) {
|
||||
emit_signal(SNAME("filesystem_changed"));
|
||||
|
|
|
@ -267,6 +267,14 @@ class EditorFileSystem : public Node {
|
|||
void _update_script_classes();
|
||||
void _update_pending_script_classes();
|
||||
|
||||
Mutex update_scene_mutex;
|
||||
HashSet<String> update_scene_paths;
|
||||
void _queue_update_scene_groups(const String &p_path);
|
||||
void _update_scene_groups();
|
||||
void _update_pending_scene_groups();
|
||||
HashSet<StringName> _get_scene_groups(const String &p_path);
|
||||
void _get_all_scenes(EditorFileSystemDirectory *p_dir, HashSet<String> &r_list);
|
||||
|
||||
String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const;
|
||||
|
||||
static Error _resource_import(const String &p_path);
|
||||
|
|
|
@ -0,0 +1,537 @@
|
|||
/**************************************************************************/
|
||||
/* group_settings_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 "group_settings_editor.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "editor/editor_scale.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/filesystem_dock.h"
|
||||
#include "editor/gui/editor_validation_panel.h"
|
||||
#include "editor/scene_tree_dock.h"
|
||||
#include "editor_file_system.h"
|
||||
#include "editor_node.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
|
||||
void GroupSettingsEditor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
update_groups();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void GroupSettingsEditor::_item_edited() {
|
||||
if (updating_groups) {
|
||||
return;
|
||||
}
|
||||
|
||||
TreeItem *ti = tree->get_edited();
|
||||
int column = tree->get_edited_column();
|
||||
|
||||
if (!ti) {
|
||||
return;
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
if (column == 1) {
|
||||
// Description Edited.
|
||||
String name = ti->get_text(0);
|
||||
String new_description = ti->get_text(1);
|
||||
String old_description = ti->get_meta("__description");
|
||||
|
||||
if (new_description == old_description) {
|
||||
return;
|
||||
}
|
||||
|
||||
name = GLOBAL_GROUP_PREFIX + name;
|
||||
|
||||
undo_redo->create_action(TTR("Set Group Description"));
|
||||
|
||||
undo_redo->add_do_property(ProjectSettings::get_singleton(), name, new_description);
|
||||
undo_redo->add_undo_property(ProjectSettings::get_singleton(), name, old_description);
|
||||
|
||||
undo_redo->add_do_method(this, "call_deferred", "update_groups");
|
||||
undo_redo->add_undo_method(this, "call_deferred", "update_groups");
|
||||
|
||||
undo_redo->add_do_method(this, "emit_signal", group_changed);
|
||||
undo_redo->add_undo_method(this, "emit_signal", group_changed);
|
||||
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
}
|
||||
|
||||
void GroupSettingsEditor::_item_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) {
|
||||
if (p_button != MouseButton::LEFT) {
|
||||
return;
|
||||
}
|
||||
|
||||
TreeItem *ti = Object::cast_to<TreeItem>(p_item);
|
||||
|
||||
if (!ti) {
|
||||
return;
|
||||
}
|
||||
ti->select(0);
|
||||
_show_remove_dialog();
|
||||
}
|
||||
|
||||
String GroupSettingsEditor::_check_new_group_name(const String &p_name) {
|
||||
if (p_name.is_empty()) {
|
||||
return TTR("Invalid group name. It cannot be empty.");
|
||||
}
|
||||
|
||||
if (ProjectSettings::get_singleton()->has_global_group(p_name)) {
|
||||
return vformat(TTR("A group with the name '%s' already exists."), p_name);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void GroupSettingsEditor::_check_rename() {
|
||||
String new_name = rename_group->get_text().strip_edges();
|
||||
String old_name = rename_group_dialog->get_meta("__name");
|
||||
|
||||
if (new_name == old_name) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (new_name.is_empty()) {
|
||||
rename_validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Group can't be empty."), EditorValidationPanel::MSG_ERROR);
|
||||
} else if (ProjectSettings::get_singleton()->has_global_group(new_name)) {
|
||||
rename_validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Group already exists."), EditorValidationPanel::MSG_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
void GroupSettingsEditor::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("remove_references"), &GroupSettingsEditor::remove_references);
|
||||
ClassDB::bind_method(D_METHOD("rename_references"), &GroupSettingsEditor::rename_references);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("update_groups"), &GroupSettingsEditor::update_groups);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("group_changed"));
|
||||
}
|
||||
|
||||
void GroupSettingsEditor::_add_group(const String &p_name, const String &p_description) {
|
||||
String name = p_name.strip_edges();
|
||||
|
||||
String error = _check_new_group_name(name);
|
||||
if (!error.is_empty()) {
|
||||
show_message(error);
|
||||
return;
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Add Group"));
|
||||
|
||||
name = GLOBAL_GROUP_PREFIX + name;
|
||||
|
||||
undo_redo->add_do_property(ProjectSettings::get_singleton(), name, p_description);
|
||||
undo_redo->add_undo_property(ProjectSettings::get_singleton(), name, Variant());
|
||||
|
||||
undo_redo->add_do_method(this, "call_deferred", "update_groups");
|
||||
undo_redo->add_undo_method(this, "call_deferred", "update_groups");
|
||||
|
||||
undo_redo->add_do_method(this, "emit_signal", group_changed);
|
||||
undo_redo->add_undo_method(this, "emit_signal", group_changed);
|
||||
|
||||
undo_redo->commit_action();
|
||||
|
||||
group_name->clear();
|
||||
group_description->clear();
|
||||
}
|
||||
|
||||
void GroupSettingsEditor::_add_group() {
|
||||
_add_group(group_name->get_text(), group_description->get_text());
|
||||
}
|
||||
|
||||
void GroupSettingsEditor::_text_submitted(const String &p_text) {
|
||||
if (!add_button->is_disabled()) {
|
||||
_add_group();
|
||||
}
|
||||
}
|
||||
|
||||
void GroupSettingsEditor::_group_name_text_changed(const String &p_name) {
|
||||
String error = _check_new_group_name(p_name.strip_edges());
|
||||
add_button->set_tooltip_text(error);
|
||||
add_button->set_disabled(!error.is_empty());
|
||||
}
|
||||
|
||||
void GroupSettingsEditor::_modify_references(const StringName &p_name, const StringName &p_new_name, bool p_is_rename) {
|
||||
HashSet<String> scenes;
|
||||
|
||||
HashMap<StringName, HashSet<StringName>> scene_groups_cache = ProjectSettings::get_singleton()->get_scene_groups_cache();
|
||||
for (const KeyValue<StringName, HashSet<StringName>> &E : scene_groups_cache) {
|
||||
if (E.value.has(p_name)) {
|
||||
scenes.insert(E.key);
|
||||
}
|
||||
}
|
||||
|
||||
int steps = scenes.size();
|
||||
Vector<EditorData::EditedScene> edited_scenes = EditorNode::get_editor_data().get_edited_scenes();
|
||||
for (const EditorData::EditedScene &es : edited_scenes) {
|
||||
if (!es.root) {
|
||||
continue;
|
||||
}
|
||||
if (es.path.is_empty()) {
|
||||
++steps;
|
||||
} else if (!scenes.has(es.path)) {
|
||||
++steps;
|
||||
}
|
||||
}
|
||||
|
||||
String progress_task = p_is_rename ? "rename_reference" : "remove_references";
|
||||
String progress_label = p_is_rename ? TTR("Renaming Group References") : TTR("Removing Group References");
|
||||
EditorProgress progress(progress_task, progress_label, steps);
|
||||
|
||||
int step = 0;
|
||||
// Update opened scenes.
|
||||
HashSet<String> edited_scenes_path;
|
||||
for (const EditorData::EditedScene &es : edited_scenes) {
|
||||
if (!es.root) {
|
||||
continue;
|
||||
}
|
||||
progress.step(es.path, step++);
|
||||
bool edited = p_is_rename ? rename_node_references(es.root, p_name, p_new_name) : remove_node_references(es.root, p_name);
|
||||
if (!es.path.is_empty()) {
|
||||
scenes.erase(es.path);
|
||||
if (edited) {
|
||||
edited_scenes_path.insert(es.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!edited_scenes_path.is_empty()) {
|
||||
EditorNode::get_singleton()->save_scene_list(edited_scenes_path);
|
||||
SceneTreeDock::get_singleton()->get_tree_editor()->update_tree();
|
||||
}
|
||||
|
||||
for (const String &E : scenes) {
|
||||
Ref<PackedScene> packed_scene = ResourceLoader::load(E);
|
||||
progress.step(E, step++);
|
||||
ERR_CONTINUE(packed_scene.is_null());
|
||||
if (p_is_rename) {
|
||||
if (packed_scene->get_state()->rename_group_references(p_name, p_new_name)) {
|
||||
ResourceSaver::save(packed_scene, E);
|
||||
}
|
||||
} else {
|
||||
if (packed_scene->get_state()->remove_group_references(p_name)) {
|
||||
ResourceSaver::save(packed_scene, E);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GroupSettingsEditor::remove_references(const StringName &p_name) {
|
||||
_modify_references(p_name, StringName(), false);
|
||||
}
|
||||
|
||||
void GroupSettingsEditor::rename_references(const StringName &p_old_name, const StringName &p_new_name) {
|
||||
_modify_references(p_old_name, p_new_name, true);
|
||||
}
|
||||
|
||||
bool GroupSettingsEditor::remove_node_references(Node *p_node, const StringName &p_name) {
|
||||
bool edited = false;
|
||||
if (p_node->is_in_group(p_name)) {
|
||||
p_node->remove_from_group(p_name);
|
||||
edited = true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < p_node->get_child_count(); i++) {
|
||||
edited |= remove_node_references(p_node->get_child(i), p_name);
|
||||
}
|
||||
return edited;
|
||||
}
|
||||
|
||||
bool GroupSettingsEditor::rename_node_references(Node *p_node, const StringName &p_old_name, const StringName &p_new_name) {
|
||||
bool edited = false;
|
||||
if (p_node->is_in_group(p_old_name)) {
|
||||
p_node->remove_from_group(p_old_name);
|
||||
p_node->add_to_group(p_new_name, true);
|
||||
edited = true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < p_node->get_child_count(); i++) {
|
||||
edited |= rename_node_references(p_node->get_child(i), p_old_name, p_new_name);
|
||||
}
|
||||
return edited;
|
||||
}
|
||||
|
||||
void GroupSettingsEditor::update_groups() {
|
||||
if (updating_groups) {
|
||||
return;
|
||||
}
|
||||
updating_groups = true;
|
||||
groups_cache = ProjectSettings::get_singleton()->get_global_groups_list();
|
||||
|
||||
tree->clear();
|
||||
TreeItem *root = tree->create_item();
|
||||
|
||||
List<StringName> keys;
|
||||
for (const KeyValue<StringName, String> &E : groups_cache) {
|
||||
keys.push_back(E.key);
|
||||
}
|
||||
keys.sort_custom<NoCaseComparator>();
|
||||
|
||||
for (const StringName &E : keys) {
|
||||
TreeItem *item = tree->create_item(root);
|
||||
item->set_meta("__name", E);
|
||||
item->set_meta("__description", groups_cache[E]);
|
||||
|
||||
item->set_text(0, E);
|
||||
item->set_editable(0, false);
|
||||
|
||||
item->set_text(1, groups_cache[E]);
|
||||
item->set_editable(1, true);
|
||||
item->add_button(2, get_editor_theme_icon(SNAME("Remove")));
|
||||
item->set_selectable(2, false);
|
||||
}
|
||||
|
||||
updating_groups = false;
|
||||
}
|
||||
|
||||
void GroupSettingsEditor::connect_filesystem_dock_signals(FileSystemDock *p_fs_dock) {
|
||||
p_fs_dock->connect("files_moved", callable_mp(ProjectSettings::get_singleton(), &ProjectSettings::remove_scene_groups_cache).unbind(1));
|
||||
p_fs_dock->connect("file_removed", callable_mp(ProjectSettings::get_singleton(), &ProjectSettings::remove_scene_groups_cache));
|
||||
}
|
||||
|
||||
void GroupSettingsEditor::_confirm_rename() {
|
||||
TreeItem *ti = tree->get_selected();
|
||||
if (!ti) {
|
||||
return;
|
||||
}
|
||||
|
||||
String old_name = ti->get_meta("__name");
|
||||
String new_name = rename_group->get_text().strip_edges();
|
||||
|
||||
if (old_name == new_name) {
|
||||
return;
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Rename Group"));
|
||||
|
||||
String property_new_name = GLOBAL_GROUP_PREFIX + new_name;
|
||||
String property_old_name = GLOBAL_GROUP_PREFIX + old_name;
|
||||
|
||||
String description = ti->get_meta("__description");
|
||||
|
||||
undo_redo->add_do_property(ProjectSettings::get_singleton(), property_new_name, description);
|
||||
undo_redo->add_undo_property(ProjectSettings::get_singleton(), property_new_name, Variant());
|
||||
|
||||
undo_redo->add_do_property(ProjectSettings::get_singleton(), property_old_name, Variant());
|
||||
undo_redo->add_undo_property(ProjectSettings::get_singleton(), property_old_name, description);
|
||||
|
||||
if (rename_check_box->is_pressed()) {
|
||||
undo_redo->add_do_method(this, "rename_references", old_name, new_name);
|
||||
undo_redo->add_undo_method(this, "rename_references", new_name, old_name);
|
||||
}
|
||||
|
||||
undo_redo->add_do_method(this, "call_deferred", "update_groups");
|
||||
undo_redo->add_undo_method(this, "call_deferred", "update_groups");
|
||||
|
||||
undo_redo->add_do_method(this, "emit_signal", group_changed);
|
||||
undo_redo->add_undo_method(this, "emit_signal", group_changed);
|
||||
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
void GroupSettingsEditor::_confirm_delete() {
|
||||
TreeItem *ti = tree->get_selected();
|
||||
if (!ti) {
|
||||
return;
|
||||
}
|
||||
|
||||
String name = ti->get_text(0);
|
||||
String description = groups_cache[name];
|
||||
String property_name = GLOBAL_GROUP_PREFIX + name;
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Remove Group"));
|
||||
|
||||
undo_redo->add_do_property(ProjectSettings::get_singleton(), property_name, Variant());
|
||||
undo_redo->add_undo_property(ProjectSettings::get_singleton(), property_name, description);
|
||||
|
||||
if (remove_check_box->is_pressed()) {
|
||||
undo_redo->add_do_method(this, "remove_references", name);
|
||||
}
|
||||
|
||||
undo_redo->add_do_method(this, "call_deferred", "update_groups");
|
||||
undo_redo->add_undo_method(this, "call_deferred", "update_groups");
|
||||
|
||||
undo_redo->add_do_method(this, "emit_signal", group_changed);
|
||||
undo_redo->add_undo_method(this, "emit_signal", group_changed);
|
||||
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
void GroupSettingsEditor::show_message(const String &p_message) {
|
||||
message->set_text(p_message);
|
||||
message->popup_centered();
|
||||
}
|
||||
|
||||
void GroupSettingsEditor::_show_remove_dialog() {
|
||||
if (!remove_dialog) {
|
||||
remove_dialog = memnew(ConfirmationDialog);
|
||||
remove_dialog->connect("confirmed", callable_mp(this, &GroupSettingsEditor::_confirm_delete));
|
||||
|
||||
VBoxContainer *vbox = memnew(VBoxContainer);
|
||||
remove_label = memnew(Label);
|
||||
vbox->add_child(remove_label);
|
||||
|
||||
remove_check_box = memnew(CheckBox);
|
||||
remove_check_box->set_text(TTR("Delete references from all scenes"));
|
||||
vbox->add_child(remove_check_box);
|
||||
|
||||
remove_dialog->add_child(vbox);
|
||||
|
||||
add_child(remove_dialog);
|
||||
}
|
||||
|
||||
TreeItem *ti = tree->get_selected();
|
||||
if (!ti) {
|
||||
return;
|
||||
}
|
||||
|
||||
remove_check_box->set_pressed(false);
|
||||
remove_label->set_text(vformat(TTR("Delete group \"%s\"?"), ti->get_text(0)));
|
||||
|
||||
remove_dialog->reset_size();
|
||||
remove_dialog->popup_centered();
|
||||
}
|
||||
|
||||
void GroupSettingsEditor::_show_rename_dialog() {
|
||||
if (!rename_group_dialog) {
|
||||
rename_group_dialog = memnew(ConfirmationDialog);
|
||||
rename_group_dialog->set_title(TTR("Rename Group"));
|
||||
rename_group_dialog->connect("confirmed", callable_mp(this, &GroupSettingsEditor::_confirm_rename));
|
||||
|
||||
VBoxContainer *vbc = memnew(VBoxContainer);
|
||||
rename_group_dialog->add_child(vbc);
|
||||
|
||||
HBoxContainer *hbc = memnew(HBoxContainer);
|
||||
hbc->add_child(memnew(Label(TTR("Name:"))));
|
||||
|
||||
rename_group = memnew(LineEdit);
|
||||
rename_group->set_custom_minimum_size(Size2(300 * EDSCALE, 1));
|
||||
hbc->add_child(rename_group);
|
||||
vbc->add_child(hbc);
|
||||
|
||||
rename_group_dialog->register_text_enter(rename_group);
|
||||
|
||||
rename_validation_panel = memnew(EditorValidationPanel);
|
||||
rename_validation_panel->add_line(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Group name is valid."));
|
||||
rename_validation_panel->set_update_callback(callable_mp(this, &GroupSettingsEditor::_check_rename));
|
||||
rename_validation_panel->set_accept_button(rename_group_dialog->get_ok_button());
|
||||
|
||||
rename_group->connect("text_changed", callable_mp(rename_validation_panel, &EditorValidationPanel::update).unbind(1));
|
||||
|
||||
vbc->add_child(rename_validation_panel);
|
||||
|
||||
rename_check_box = memnew(CheckBox);
|
||||
rename_check_box->set_text(TTR("Rename references in all scenes"));
|
||||
vbc->add_child(rename_check_box);
|
||||
|
||||
add_child(rename_group_dialog);
|
||||
}
|
||||
|
||||
TreeItem *ti = tree->get_selected();
|
||||
if (!ti) {
|
||||
return;
|
||||
}
|
||||
|
||||
rename_check_box->set_pressed(false);
|
||||
|
||||
String name = ti->get_meta("__name");
|
||||
|
||||
rename_group->set_text(name);
|
||||
rename_group_dialog->set_meta("__name", name);
|
||||
|
||||
rename_validation_panel->update();
|
||||
|
||||
rename_group_dialog->reset_size();
|
||||
rename_group_dialog->popup_centered();
|
||||
rename_group->select_all();
|
||||
rename_group->grab_focus();
|
||||
}
|
||||
|
||||
GroupSettingsEditor::GroupSettingsEditor() {
|
||||
ProjectSettings::get_singleton()->add_hidden_prefix("global_group/");
|
||||
|
||||
HBoxContainer *hbc = memnew(HBoxContainer);
|
||||
add_child(hbc);
|
||||
|
||||
Label *l = memnew(Label);
|
||||
l->set_text(TTR("Name:"));
|
||||
hbc->add_child(l);
|
||||
|
||||
group_name = memnew(LineEdit);
|
||||
group_name->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
group_name->set_clear_button_enabled(true);
|
||||
group_name->connect("text_changed", callable_mp(this, &GroupSettingsEditor::_group_name_text_changed));
|
||||
group_name->connect("text_submitted", callable_mp(this, &GroupSettingsEditor::_text_submitted));
|
||||
hbc->add_child(group_name);
|
||||
|
||||
l = memnew(Label);
|
||||
l->set_text(TTR("Description:"));
|
||||
hbc->add_child(l);
|
||||
|
||||
group_description = memnew(LineEdit);
|
||||
group_description->set_clear_button_enabled(true);
|
||||
group_description->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
group_description->connect("text_submitted", callable_mp(this, &GroupSettingsEditor::_text_submitted));
|
||||
hbc->add_child(group_description);
|
||||
|
||||
add_button = memnew(Button);
|
||||
add_button->set_text(TTR("Add"));
|
||||
add_button->set_disabled(true);
|
||||
add_button->connect("pressed", callable_mp(this, &GroupSettingsEditor::_add_group));
|
||||
hbc->add_child(add_button);
|
||||
|
||||
tree = memnew(Tree);
|
||||
tree->set_hide_root(true);
|
||||
tree->set_select_mode(Tree::SELECT_SINGLE);
|
||||
tree->set_allow_reselect(true);
|
||||
|
||||
tree->set_columns(3);
|
||||
tree->set_column_titles_visible(true);
|
||||
|
||||
tree->set_column_title(0, TTR("Name"));
|
||||
tree->set_column_title(1, TTR("Description"));
|
||||
tree->set_column_expand(2, false);
|
||||
|
||||
tree->connect("item_edited", callable_mp(this, &GroupSettingsEditor::_item_edited));
|
||||
tree->connect("item_activated", callable_mp(this, &GroupSettingsEditor::_show_rename_dialog));
|
||||
tree->connect("button_clicked", callable_mp(this, &GroupSettingsEditor::_item_button_pressed));
|
||||
tree->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
|
||||
add_child(tree, true);
|
||||
|
||||
message = memnew(AcceptDialog);
|
||||
add_child(message);
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/**************************************************************************/
|
||||
/* group_settings_editor.h */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef GROUP_SETTINGS_EDITOR_H
|
||||
#define GROUP_SETTINGS_EDITOR_H
|
||||
|
||||
#include "scene/gui/dialogs.h"
|
||||
|
||||
class CheckBox;
|
||||
class EditorFileSystemDirectory;
|
||||
class EditorValidationPanel;
|
||||
class FileSystemDock;
|
||||
class Label;
|
||||
class Tree;
|
||||
|
||||
class GroupSettingsEditor : public VBoxContainer {
|
||||
GDCLASS(GroupSettingsEditor, VBoxContainer);
|
||||
|
||||
const String GLOBAL_GROUP_PREFIX = "global_group/";
|
||||
const StringName group_changed = "group_changed";
|
||||
|
||||
HashMap<StringName, String> groups_cache;
|
||||
|
||||
bool updating_groups = false;
|
||||
|
||||
AcceptDialog *message = nullptr;
|
||||
Tree *tree = nullptr;
|
||||
LineEdit *group_name = nullptr;
|
||||
LineEdit *group_description = nullptr;
|
||||
Button *add_button = nullptr;
|
||||
|
||||
ConfirmationDialog *remove_dialog = nullptr;
|
||||
CheckBox *remove_check_box = nullptr;
|
||||
Label *remove_label = nullptr;
|
||||
|
||||
ConfirmationDialog *rename_group_dialog = nullptr;
|
||||
LineEdit *rename_group = nullptr;
|
||||
CheckBox *rename_check_box = nullptr;
|
||||
EditorValidationPanel *rename_validation_panel = nullptr;
|
||||
|
||||
void _show_remove_dialog();
|
||||
void _show_rename_dialog();
|
||||
|
||||
String _check_new_group_name(const String &p_name);
|
||||
void _check_rename();
|
||||
|
||||
void _add_group();
|
||||
void _add_group(const String &p_name, const String &p_description);
|
||||
|
||||
void _modify_references(const StringName &p_name, const StringName &p_new_name, bool p_is_rename);
|
||||
|
||||
void _confirm_rename();
|
||||
void _confirm_delete();
|
||||
|
||||
void _text_submitted(const String &p_text);
|
||||
void _group_name_text_changed(const String &p_name);
|
||||
|
||||
void _item_edited();
|
||||
void _item_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void show_message(const String &p_message);
|
||||
|
||||
void remove_references(const StringName &p_name);
|
||||
void rename_references(const StringName &p_old_name, const StringName &p_new_name);
|
||||
|
||||
bool remove_node_references(Node *p_node, const StringName &p_name);
|
||||
bool rename_node_references(Node *p_node, const StringName &p_old_name, const StringName &p_new_name);
|
||||
|
||||
void update_groups();
|
||||
void connect_filesystem_dock_signals(FileSystemDock *p_fs_dock);
|
||||
|
||||
GroupSettingsEditor();
|
||||
};
|
||||
|
||||
#endif // GROUP_SETTINGS_EDITOR_H
|
File diff suppressed because it is too large
Load Diff
|
@ -34,57 +34,94 @@
|
|||
#include "scene/gui/dialogs.h"
|
||||
|
||||
class Button;
|
||||
class CheckBox;
|
||||
class CheckButton;
|
||||
class EditorValidationPanel;
|
||||
class Label;
|
||||
class LineEdit;
|
||||
class PopupMenu;
|
||||
class Tree;
|
||||
class TreeItem;
|
||||
|
||||
class GroupDialog : public AcceptDialog {
|
||||
GDCLASS(GroupDialog, AcceptDialog);
|
||||
class GroupsEditor : public VBoxContainer {
|
||||
GDCLASS(GroupsEditor, VBoxContainer);
|
||||
|
||||
AcceptDialog *error = nullptr;
|
||||
const String GLOBAL_GROUP_PREFIX = "global_group/";
|
||||
|
||||
bool updating_tree = false;
|
||||
bool updating_groups = false;
|
||||
bool groups_dirty = false;
|
||||
bool update_groups_and_tree_queued = false;
|
||||
|
||||
Node *node = nullptr;
|
||||
Node *scene_root_node = nullptr;
|
||||
SceneTree *scene_tree = nullptr;
|
||||
TreeItem *groups_root = nullptr;
|
||||
|
||||
LineEdit *add_group_text = nullptr;
|
||||
Button *add_group_button = nullptr;
|
||||
ConfirmationDialog *add_group_dialog = nullptr;
|
||||
LineEdit *add_group_name = nullptr;
|
||||
LineEdit *add_group_description = nullptr;
|
||||
CheckButton *global_group_button = nullptr;
|
||||
EditorValidationPanel *add_validation_panel = nullptr;
|
||||
|
||||
Tree *groups = nullptr;
|
||||
ConfirmationDialog *rename_group_dialog = nullptr;
|
||||
LineEdit *rename_group = nullptr;
|
||||
CheckBox *rename_check_box = nullptr;
|
||||
EditorValidationPanel *rename_validation_panel = nullptr;
|
||||
|
||||
Tree *nodes_to_add = nullptr;
|
||||
TreeItem *add_node_root = nullptr;
|
||||
LineEdit *add_filter = nullptr;
|
||||
ConfirmationDialog *remove_group_dialog = nullptr;
|
||||
CheckBox *remove_check_box = nullptr;
|
||||
Label *remove_label = nullptr;
|
||||
|
||||
Tree *nodes_to_remove = nullptr;
|
||||
TreeItem *remove_node_root = nullptr;
|
||||
LineEdit *remove_filter = nullptr;
|
||||
PopupMenu *menu = nullptr;
|
||||
|
||||
Label *group_empty = nullptr;
|
||||
LineEdit *filter = nullptr;
|
||||
Button *add = nullptr;
|
||||
Tree *tree = nullptr;
|
||||
|
||||
Button *add_button = nullptr;
|
||||
Button *remove_button = nullptr;
|
||||
HashMap<Node *, HashMap<StringName, bool>> scene_groups_cache;
|
||||
HashMap<StringName, bool> scene_groups_for_caching;
|
||||
|
||||
String selected_group;
|
||||
HashMap<StringName, bool> scene_groups;
|
||||
HashMap<StringName, String> global_groups;
|
||||
|
||||
void _group_selected();
|
||||
void _update_scene_groups(Node *p_node);
|
||||
void _cache_scene_groups(Node *p_node);
|
||||
|
||||
void _remove_filter_changed(const String &p_filter);
|
||||
void _add_filter_changed(const String &p_filter);
|
||||
void _show_add_group_dialog();
|
||||
void _show_rename_group_dialog();
|
||||
void _show_remove_group_dialog();
|
||||
|
||||
void _add_pressed();
|
||||
void _removed_pressed();
|
||||
void _add_group_pressed(const String &p_name);
|
||||
void _add_group_text_changed(const String &p_new_text);
|
||||
void _check_add();
|
||||
void _check_rename();
|
||||
void _validate_name(const String &p_name, EditorValidationPanel *p_validation_panel);
|
||||
|
||||
void _group_renamed();
|
||||
void _rename_group_item(const String &p_old_name, const String &p_new_name);
|
||||
void _update_tree();
|
||||
|
||||
void _add_group(String p_name);
|
||||
void _modify_group_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
|
||||
void _delete_group_item(const String &p_name);
|
||||
void _update_groups();
|
||||
void _load_scene_groups(Node *p_node);
|
||||
|
||||
void _load_groups(Node *p_current);
|
||||
void _load_nodes(Node *p_current);
|
||||
void _add_scene_group(const String &p_name);
|
||||
void _rename_scene_group(const String &p_old_name, const String &p_new_name);
|
||||
void _remove_scene_group(const String &p_name);
|
||||
|
||||
bool _has_group(const String &p_name);
|
||||
void _set_group_checked(const String &p_name, bool p_checked);
|
||||
|
||||
void _confirm_add();
|
||||
void _confirm_rename();
|
||||
void _confirm_delete();
|
||||
|
||||
void _item_edited();
|
||||
void _item_mouse_selected(const Vector2 &p_pos, MouseButton p_mouse_button);
|
||||
void _modify_group(Object *p_item, int p_column, int p_id, MouseButton p_mouse_button);
|
||||
void _menu_id_pressed(int p_id);
|
||||
|
||||
void _update_groups_and_tree();
|
||||
void _queue_update_groups_and_tree();
|
||||
|
||||
void _groups_gui_input(Ref<InputEvent> p_event);
|
||||
|
||||
void _node_removed(Node *p_node);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
@ -94,45 +131,8 @@ public:
|
|||
enum ModifyButton {
|
||||
DELETE_GROUP,
|
||||
COPY_GROUP,
|
||||
};
|
||||
|
||||
void edit();
|
||||
|
||||
GroupDialog();
|
||||
};
|
||||
|
||||
class GroupsEditor : public VBoxContainer {
|
||||
GDCLASS(GroupsEditor, VBoxContainer);
|
||||
|
||||
Node *node = nullptr;
|
||||
TreeItem *groups_root = nullptr;
|
||||
|
||||
GroupDialog *group_dialog = nullptr;
|
||||
AcceptDialog *error = nullptr;
|
||||
|
||||
LineEdit *group_name = nullptr;
|
||||
Button *add = nullptr;
|
||||
Tree *tree = nullptr;
|
||||
|
||||
String selected_group;
|
||||
|
||||
void update_tree();
|
||||
void _add_group(const String &p_group = "");
|
||||
void _modify_group(Object *p_item, int p_column, int p_id, MouseButton p_button);
|
||||
void _group_name_changed(const String &p_new_text);
|
||||
|
||||
void _group_selected();
|
||||
void _group_renamed();
|
||||
|
||||
void _show_group_dialog();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
enum ModifyButton {
|
||||
DELETE_GROUP,
|
||||
COPY_GROUP,
|
||||
RENAME_GROUP,
|
||||
CONVERT_GROUP,
|
||||
};
|
||||
|
||||
void set_current(Node *p_node);
|
||||
|
|
|
@ -45,6 +45,7 @@ ProjectSettingsEditor *ProjectSettingsEditor::singleton = nullptr;
|
|||
|
||||
void ProjectSettingsEditor::connect_filesystem_dock_signals(FileSystemDock *p_fs_dock) {
|
||||
localization_editor->connect_filesystem_dock_signals(p_fs_dock);
|
||||
group_settings->connect_filesystem_dock_signals(p_fs_dock);
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::popup_project_settings(bool p_clear_filter) {
|
||||
|
@ -62,6 +63,7 @@ void ProjectSettingsEditor::popup_project_settings(bool p_clear_filter) {
|
|||
|
||||
localization_editor->update_translations();
|
||||
autoload_settings->update_autoload();
|
||||
group_settings->update_groups();
|
||||
plugin_settings->update_plugins();
|
||||
import_defaults_editor->clear();
|
||||
|
||||
|
@ -709,6 +711,11 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
|
|||
shaders_global_shader_uniforms_editor->connect("globals_changed", callable_mp(this, &ProjectSettingsEditor::queue_save));
|
||||
tab_container->add_child(shaders_global_shader_uniforms_editor);
|
||||
|
||||
group_settings = memnew(GroupSettingsEditor);
|
||||
group_settings->set_name(TTR("Global Groups"));
|
||||
group_settings->connect("group_changed", callable_mp(this, &ProjectSettingsEditor::queue_save));
|
||||
tab_container->add_child(group_settings);
|
||||
|
||||
plugin_settings = memnew(EditorPluginSettings);
|
||||
plugin_settings->set_name(TTR("Plugins"));
|
||||
tab_container->add_child(plugin_settings);
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "editor/editor_data.h"
|
||||
#include "editor/editor_plugin_settings.h"
|
||||
#include "editor/editor_sectioned_inspector.h"
|
||||
#include "editor/group_settings_editor.h"
|
||||
#include "editor/import_defaults_editor.h"
|
||||
#include "editor/localization_editor.h"
|
||||
#include "editor/shader_globals_editor.h"
|
||||
|
@ -58,6 +59,7 @@ class ProjectSettingsEditor : public AcceptDialog {
|
|||
LocalizationEditor *localization_editor = nullptr;
|
||||
EditorAutoloadSettings *autoload_settings = nullptr;
|
||||
ShaderGlobalsEditor *shaders_global_shader_uniforms_editor = nullptr;
|
||||
GroupSettingsEditor *group_settings = nullptr;
|
||||
EditorPluginSettings *plugin_settings = nullptr;
|
||||
|
||||
LineEdit *search_box = nullptr;
|
||||
|
@ -122,6 +124,7 @@ public:
|
|||
void update_plugins();
|
||||
|
||||
EditorAutoloadSettings *get_autoload_settings() { return autoload_settings; }
|
||||
GroupSettingsEditor *get_group_settings() { return group_settings; }
|
||||
TabContainer *get_tabs() { return tab_container; }
|
||||
|
||||
void queue_save();
|
||||
|
|
|
@ -3069,8 +3069,13 @@ static void _add_nodes_to_options(const Node *p_base, const Node *p_node, List<S
|
|||
|
||||
void Node::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
|
||||
String pf = p_function;
|
||||
if ((pf == "has_node" || pf == "get_node") && p_idx == 0) {
|
||||
if (p_idx == 0 && (pf == "has_node" || pf == "get_node")) {
|
||||
_add_nodes_to_options(this, this, r_options);
|
||||
} else if (p_idx == 0 && (pf == "add_to_group" || pf == "remove_from_group" || pf == "is_in_group")) {
|
||||
HashMap<StringName, String> global_groups = ProjectSettings::get_singleton()->get_global_groups_list();
|
||||
for (const KeyValue<StringName, String> &E : global_groups) {
|
||||
r_options->push_back(E.key.operator String().quote());
|
||||
}
|
||||
}
|
||||
Object::get_argument_options(p_function, p_idx, r_options);
|
||||
}
|
||||
|
|
|
@ -1714,6 +1714,19 @@ void SceneTree::get_argument_options(const StringName &p_function, int p_idx, Li
|
|||
filename = dir_access->get_next();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bool add_options = false;
|
||||
if (p_idx == 0) {
|
||||
add_options = p_function == "get_nodes_in_group" || p_function == "has_group" || p_function == "get_first_node_in_group" || p_function == "set_group" || p_function == "notify_group" || p_function == "call_group" || p_function == "add_to_group";
|
||||
} else if (p_idx == 1) {
|
||||
add_options = p_function == "set_group_flags" || p_function == "call_group_flags" || p_function == "notify_group_flags";
|
||||
}
|
||||
if (add_options) {
|
||||
HashMap<StringName, String> global_groups = ProjectSettings::get_singleton()->get_global_groups_list();
|
||||
for (const KeyValue<StringName, String> &E : global_groups) {
|
||||
r_options->push_back(E.key.operator String().quote());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1849,6 +1849,44 @@ void SceneState::add_editable_instance(const NodePath &p_path) {
|
|||
editable_instances.push_back(p_path);
|
||||
}
|
||||
|
||||
bool SceneState::remove_group_references(const StringName &p_name) {
|
||||
bool edited = false;
|
||||
for (NodeData &node : nodes) {
|
||||
for (const int &group : node.groups) {
|
||||
if (names[group] == p_name) {
|
||||
node.groups.erase(group);
|
||||
edited = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return edited;
|
||||
}
|
||||
|
||||
bool SceneState::rename_group_references(const StringName &p_old_name, const StringName &p_new_name) {
|
||||
bool edited = false;
|
||||
for (const NodeData &node : nodes) {
|
||||
for (const int &group : node.groups) {
|
||||
if (names[group] == p_old_name) {
|
||||
names.write[group] = p_new_name;
|
||||
edited = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return edited;
|
||||
}
|
||||
|
||||
HashSet<StringName> SceneState::get_all_groups() {
|
||||
HashSet<StringName> ret;
|
||||
for (const NodeData &node : nodes) {
|
||||
for (const int &group : node.groups) {
|
||||
ret.insert(names[group]);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Vector<String> SceneState::_get_node_groups(int p_idx) const {
|
||||
Vector<StringName> groups = get_node_groups(p_idx);
|
||||
Vector<String> ret;
|
||||
|
|
|
@ -204,6 +204,10 @@ public:
|
|||
void add_connection(int p_from, int p_to, int p_signal, int p_method, int p_flags, int p_unbinds, const Vector<int> &p_binds);
|
||||
void add_editable_instance(const NodePath &p_path);
|
||||
|
||||
bool remove_group_references(const StringName &p_name);
|
||||
bool rename_group_references(const StringName &p_old_name, const StringName &p_new_name);
|
||||
HashSet<StringName> get_all_groups();
|
||||
|
||||
virtual void set_last_modified_time(uint64_t p_time) { last_modified_time = p_time; }
|
||||
uint64_t get_last_modified_time() const { return last_modified_time; }
|
||||
|
||||
|
|
Loading…
Reference in New Issue