You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-14 13:41:12 +00:00
Allow fixing indirect missing dependencies manually
This commit is contained in:
@@ -37,10 +37,28 @@
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/file_system/editor_file_system.h"
|
||||
#include "editor/gui/editor_file_dialog.h"
|
||||
#include "editor/gui/editor_quick_open_dialog.h"
|
||||
#include "editor/settings/editor_settings.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/item_list.h"
|
||||
#include "scene/gui/margin_container.h"
|
||||
#include "scene/gui/popup_menu.h"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
static void _setup_search_file_dialog(EditorFileDialog *p_dialog, const String &p_file, const String &p_type) {
|
||||
p_dialog->set_title(vformat(TTR("Search Replacement For: %s"), p_file.get_file()));
|
||||
|
||||
// Set directory to closest existing directory.
|
||||
p_dialog->set_current_dir(p_file.get_base_dir());
|
||||
|
||||
p_dialog->clear_filters();
|
||||
List<String> ext;
|
||||
ResourceLoader::get_recognized_extensions_for_type(p_type, &ext);
|
||||
for (const String &E : ext) {
|
||||
p_dialog->add_filter("*." + E);
|
||||
}
|
||||
}
|
||||
|
||||
void DependencyEditor::_searched(const String &p_path) {
|
||||
HashMap<String, String> dep_rename;
|
||||
@@ -59,17 +77,7 @@ void DependencyEditor::_load_pressed(Object *p_item, int p_cell, int p_button, M
|
||||
TreeItem *ti = Object::cast_to<TreeItem>(p_item);
|
||||
replacing = ti->get_text(1);
|
||||
|
||||
search->set_title(TTR("Search Replacement For:") + " " + replacing.get_file());
|
||||
|
||||
// Set directory to closest existing directory.
|
||||
search->set_current_dir(replacing.get_base_dir());
|
||||
|
||||
search->clear_filters();
|
||||
List<String> ext;
|
||||
ResourceLoader::get_recognized_extensions_for_type(ti->get_metadata(0), &ext);
|
||||
for (const String &E : ext) {
|
||||
search->add_filter("*." + E);
|
||||
}
|
||||
_setup_search_file_dialog(search, replacing, ti->get_metadata(0));
|
||||
search->popup_file_dialog();
|
||||
}
|
||||
|
||||
@@ -171,6 +179,30 @@ void DependencyEditor::_notification(int p_what) {
|
||||
}
|
||||
}
|
||||
|
||||
static String _get_resolved_dep_path(const String &p_dep) {
|
||||
if (p_dep.get_slice_count("::") < 3) {
|
||||
return p_dep.get_slice("::", 0); // No UID, just return the path.
|
||||
}
|
||||
|
||||
const String uid_text = p_dep.get_slice("::", 0);
|
||||
ResourceUID::ID uid = ResourceUID::get_singleton()->text_to_id(uid_text);
|
||||
|
||||
// Dependency is in UID format, obtain proper path.
|
||||
if (uid != ResourceUID::INVALID_ID && ResourceUID::get_singleton()->has_id(uid)) {
|
||||
return ResourceUID::get_singleton()->get_id_path(uid);
|
||||
}
|
||||
|
||||
// UID fallback path.
|
||||
return p_dep.get_slice("::", 2);
|
||||
}
|
||||
|
||||
static String _get_stored_dep_path(const String &p_dep) {
|
||||
if (p_dep.get_slice_count("::") > 2) {
|
||||
return p_dep.get_slice("::", 2);
|
||||
}
|
||||
return p_dep.get_slice("::", 0);
|
||||
}
|
||||
|
||||
void DependencyEditor::_update_list() {
|
||||
List<String> deps;
|
||||
ResourceLoader::get_dependencies(editing, &deps, true);
|
||||
@@ -184,47 +216,27 @@ void DependencyEditor::_update_list() {
|
||||
|
||||
bool broken = false;
|
||||
|
||||
for (const String &n : deps) {
|
||||
for (const String &dep : deps) {
|
||||
TreeItem *item = tree->create_item(root);
|
||||
String path;
|
||||
String type;
|
||||
|
||||
if (n.contains("::")) {
|
||||
path = n.get_slice("::", 0);
|
||||
type = n.get_slice("::", 1);
|
||||
const String path = _get_resolved_dep_path(dep);
|
||||
if (FileAccess::exists(path)) {
|
||||
item->set_text(0, path.get_file());
|
||||
item->set_text(1, path);
|
||||
} else {
|
||||
path = n;
|
||||
type = "Resource";
|
||||
}
|
||||
|
||||
ResourceUID::ID uid = ResourceUID::get_singleton()->text_to_id(path);
|
||||
if (uid != ResourceUID::INVALID_ID) {
|
||||
// Dependency is in uid format, obtain proper path.
|
||||
if (ResourceUID::get_singleton()->has_id(uid)) {
|
||||
path = ResourceUID::get_singleton()->get_id_path(uid);
|
||||
} else if (n.get_slice_count("::") >= 3) {
|
||||
// If uid can't be found, try to use fallback path.
|
||||
path = n.get_slice("::", 2);
|
||||
} else {
|
||||
ERR_PRINT("Invalid dependency UID and fallback path.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
String name = path.get_file();
|
||||
|
||||
Ref<Texture2D> icon = EditorNode::get_singleton()->get_class_icon(type);
|
||||
item->set_text(0, name);
|
||||
item->set_icon(0, icon);
|
||||
item->set_metadata(0, type);
|
||||
item->set_text(1, path);
|
||||
|
||||
if (!FileAccess::exists(path)) {
|
||||
const String &stored_path = _get_stored_dep_path(dep);
|
||||
item->set_text(0, stored_path.get_file());
|
||||
item->set_text(1, stored_path);
|
||||
item->set_custom_color(1, Color(1, 0.4, 0.3));
|
||||
missing.push_back(path);
|
||||
missing.push_back(stored_path);
|
||||
broken = true;
|
||||
}
|
||||
|
||||
const String type = dep.contains("::") ? dep.get_slice("::", 1) : "Resource";
|
||||
Ref<Texture2D> icon = EditorNode::get_singleton()->get_class_icon(type);
|
||||
item->set_icon(0, icon);
|
||||
item->set_metadata(0, type);
|
||||
|
||||
item->add_button(1, folder, 0);
|
||||
}
|
||||
|
||||
@@ -260,10 +272,8 @@ DependencyEditor::DependencyEditor() {
|
||||
tree->set_column_titles_visible(true);
|
||||
tree->set_column_title(0, TTR("Resource"));
|
||||
tree->set_column_clip_content(0, true);
|
||||
tree->set_column_expand_ratio(0, 2);
|
||||
tree->set_column_title(1, TTR("Path"));
|
||||
tree->set_column_clip_content(1, true);
|
||||
tree->set_column_expand_ratio(1, 1);
|
||||
tree->set_hide_root(true);
|
||||
tree->connect("button_clicked", callable_mp(this, &DependencyEditor::_load_pressed));
|
||||
|
||||
@@ -293,7 +303,6 @@ DependencyEditor::DependencyEditor() {
|
||||
search = memnew(EditorFileDialog);
|
||||
search->connect("file_selected", callable_mp(this, &DependencyEditor::_searched));
|
||||
search->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
|
||||
search->set_title(TTR("Search Replacement Resource:"));
|
||||
add_child(search);
|
||||
}
|
||||
|
||||
@@ -728,61 +737,163 @@ DependencyRemoveDialog::DependencyRemoveDialog() {
|
||||
}
|
||||
|
||||
//////////////
|
||||
enum {
|
||||
BUTTON_ID_SEARCH,
|
||||
BUTTON_ID_OPEN_DEPS_EDITOR,
|
||||
};
|
||||
|
||||
void DependencyErrorDialog::show(const String &p_for_file, const Vector<String> &report) {
|
||||
void DependencyErrorDialog::show(const String &p_for_file, const HashMap<String, HashSet<String>> &p_report) {
|
||||
for_file = p_for_file;
|
||||
set_title(TTR("Error loading:") + " " + p_for_file.get_file());
|
||||
files->clear();
|
||||
|
||||
TreeItem *root = files->create_item(nullptr);
|
||||
for (int i = 0; i < report.size(); i++) {
|
||||
String dep;
|
||||
String type = "Object";
|
||||
dep = report[i].get_slice("::", 0);
|
||||
if (report[i].get_slice_count("::") > 0) {
|
||||
type = report[i].get_slice("::", 1);
|
||||
// TRANSLATORS: The placeholder is a filename.
|
||||
set_title(vformat(TTR("Error loading: %s"), p_for_file.get_file()));
|
||||
|
||||
HashMap<String, HashSet<String>> missing_to_owners;
|
||||
for (const KeyValue<String, HashSet<String>> &E : p_report) {
|
||||
for (const String &missing : E.value) {
|
||||
missing_to_owners[missing].insert(E.key);
|
||||
}
|
||||
|
||||
Ref<Texture2D> icon = EditorNode::get_singleton()->get_class_icon(type);
|
||||
|
||||
TreeItem *ti = files->create_item(root);
|
||||
ti->set_text(0, dep);
|
||||
ti->set_icon(0, icon);
|
||||
}
|
||||
|
||||
files->clear();
|
||||
TreeItem *root = files->create_item(nullptr);
|
||||
Ref<Texture2D> folder_icon = get_theme_icon(SNAME("folder"), SNAME("FileDialog"));
|
||||
|
||||
for (const KeyValue<String, HashSet<String>> &E : missing_to_owners) {
|
||||
const String &missing_path = E.key.get_slice("::", 0);
|
||||
const String &missing_type = E.key.get_slice("::", 1);
|
||||
|
||||
TreeItem *missing_ti = root->create_child();
|
||||
missing_ti->set_text(0, missing_path);
|
||||
missing_ti->set_metadata(0, E.key);
|
||||
missing_ti->set_auto_translate_mode(0, AUTO_TRANSLATE_MODE_DISABLED);
|
||||
missing_ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(missing_type));
|
||||
missing_ti->set_icon(1, get_editor_theme_icon(icon_name_fail));
|
||||
missing_ti->add_button(1, folder_icon, BUTTON_ID_SEARCH, false, TTRC("Search"));
|
||||
missing_ti->set_collapsed(true);
|
||||
|
||||
for (const String &owner_path : E.value) {
|
||||
TreeItem *owner_ti = missing_ti->create_child();
|
||||
// TRANSLATORS: The placeholder is a file path.
|
||||
owner_ti->set_text(0, vformat(TTR("Referenced by %s"), owner_path));
|
||||
owner_ti->set_metadata(0, owner_path);
|
||||
owner_ti->set_auto_translate_mode(0, AUTO_TRANSLATE_MODE_DISABLED);
|
||||
owner_ti->add_button(1, files->get_editor_theme_icon(SNAME("Edit")), BUTTON_ID_OPEN_DEPS_EDITOR, false, TTRC("Fix Dependencies"));
|
||||
}
|
||||
}
|
||||
|
||||
set_ok_button_text(TTRC("Open Anyway"));
|
||||
popup_centered();
|
||||
}
|
||||
|
||||
void DependencyErrorDialog::ok_pressed() {
|
||||
EditorNode::get_singleton()->load_scene_or_resource(for_file, true);
|
||||
EditorNode::get_singleton()->load_scene_or_resource(for_file, !errors_fixed);
|
||||
}
|
||||
|
||||
void DependencyErrorDialog::custom_action(const String &) {
|
||||
EditorNode::get_singleton()->fix_dependencies(for_file);
|
||||
void DependencyErrorDialog::_on_files_button_clicked(TreeItem *p_item, int p_column, int p_id, MouseButton p_button) {
|
||||
switch (p_id) {
|
||||
case BUTTON_ID_SEARCH: {
|
||||
const String &meta = p_item->get_metadata(0);
|
||||
const String &missing_path = meta.get_slice("::", 0);
|
||||
const String &missing_type = meta.get_slice("::", 1);
|
||||
if (replacement_file_dialog == nullptr) {
|
||||
replacement_file_dialog = memnew(EditorFileDialog);
|
||||
replacement_file_dialog->connect("file_selected", callable_mp(this, &DependencyErrorDialog::_on_replacement_file_selected));
|
||||
replacement_file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
|
||||
add_child(replacement_file_dialog);
|
||||
}
|
||||
replacing_item = p_item;
|
||||
_setup_search_file_dialog(replacement_file_dialog, missing_path, missing_type);
|
||||
replacement_file_dialog->popup_file_dialog();
|
||||
} break;
|
||||
|
||||
case BUTTON_ID_OPEN_DEPS_EDITOR: {
|
||||
const String &owner_path = p_item->get_metadata(0);
|
||||
if (deps_editor == nullptr) {
|
||||
deps_editor = memnew(DependencyEditor);
|
||||
deps_editor->connect(SceneStringName(visibility_changed), callable_mp(this, &DependencyErrorDialog::_check_for_resolved));
|
||||
add_child(deps_editor);
|
||||
}
|
||||
deps_editor->edit(owner_path);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void DependencyErrorDialog::_on_replacement_file_selected(const String &p_path) {
|
||||
const String &missing_path = String(replacing_item->get_metadata(0)).get_slice("::", 0);
|
||||
|
||||
for (TreeItem *owner_ti = replacing_item->get_first_child(); owner_ti; owner_ti = owner_ti->get_next()) {
|
||||
const String &owner_path = owner_ti->get_metadata(0);
|
||||
ResourceLoader::rename_dependencies(owner_path, { { missing_path, p_path } });
|
||||
}
|
||||
|
||||
_check_for_resolved();
|
||||
}
|
||||
|
||||
void DependencyErrorDialog::_check_for_resolved() {
|
||||
if (deps_editor && deps_editor->is_visible()) {
|
||||
return; // Only update when the dialog is closed.
|
||||
}
|
||||
|
||||
errors_fixed = true;
|
||||
HashMap<String, LocalVector<String>> owner_deps;
|
||||
|
||||
TreeItem *root = files->get_root();
|
||||
for (TreeItem *missing_ti = root->get_first_child(); missing_ti; missing_ti = missing_ti->get_next()) {
|
||||
bool all_owners_fixed = true;
|
||||
|
||||
for (TreeItem *owner_ti = missing_ti->get_first_child(); owner_ti; owner_ti = owner_ti->get_next()) {
|
||||
const String &owner_path = owner_ti->get_metadata(0);
|
||||
|
||||
if (!owner_deps.has(owner_path)) {
|
||||
List<String> deps;
|
||||
ResourceLoader::get_dependencies(owner_path, &deps);
|
||||
|
||||
LocalVector<String> &stored_paths = owner_deps[owner_path];
|
||||
for (const String &dep : deps) {
|
||||
if (!errors_fixed && !FileAccess::exists(_get_resolved_dep_path(dep))) {
|
||||
errors_fixed = false;
|
||||
}
|
||||
stored_paths.push_back(_get_stored_dep_path(dep));
|
||||
}
|
||||
}
|
||||
const LocalVector<String> &stored_paths = owner_deps[owner_path];
|
||||
const String &missing_path = String(missing_ti->get_metadata(0)).get_slice("::", 0);
|
||||
|
||||
if (stored_paths.has(missing_path)) {
|
||||
all_owners_fixed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
missing_ti->set_icon(1, get_editor_theme_icon(all_owners_fixed ? icon_name_check : icon_name_fail));
|
||||
}
|
||||
|
||||
set_ok_button_text(errors_fixed ? TTRC("Open") : TTRC("Open Anyway"));
|
||||
}
|
||||
|
||||
DependencyErrorDialog::DependencyErrorDialog() {
|
||||
icon_name_fail = StringName("ImportFail");
|
||||
icon_name_check = StringName("ImportCheck");
|
||||
|
||||
VBoxContainer *vb = memnew(VBoxContainer);
|
||||
add_child(vb);
|
||||
|
||||
files = memnew(Tree);
|
||||
files->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
files->set_hide_root(true);
|
||||
vb->add_margin_child(TTR("Load failed due to missing dependencies:"), files, true);
|
||||
files->set_select_mode(Tree::SELECT_ROW);
|
||||
files->set_columns(2);
|
||||
files->set_column_expand(1, false);
|
||||
files->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
files->connect("button_clicked", callable_mp(this, &DependencyErrorDialog::_on_files_button_clicked));
|
||||
vb->add_margin_child(TTRC("Load failed due to missing dependencies:"), files, true);
|
||||
|
||||
set_min_size(Size2(500, 220) * EDSCALE);
|
||||
set_ok_button_text(TTR("Open Anyway"));
|
||||
set_cancel_button_text(TTR("Close"));
|
||||
set_min_size(Size2(500, 320) * EDSCALE);
|
||||
set_cancel_button_text(TTRC("Close"));
|
||||
|
||||
text = memnew(Label);
|
||||
Label *text = memnew(Label(TTRC("Which action should be taken?")));
|
||||
text->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
|
||||
vb->add_child(text);
|
||||
text->set_text(TTR("Which action should be taken?"));
|
||||
|
||||
fdep = add_button(TTR("Fix Dependencies"), true, "fixdeps");
|
||||
|
||||
set_title(TTR("Errors loading!"));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
Reference in New Issue
Block a user