diff --git a/editor/file_system/editor_file_system.cpp b/editor/file_system/editor_file_system.cpp index cdfb403f2fe..125a24c035c 100644 --- a/editor/file_system/editor_file_system.cpp +++ b/editor/file_system/editor_file_system.cpp @@ -129,6 +129,11 @@ String EditorFileSystemDirectory::get_file_path(int p_idx) const { return get_path().path_join(get_file(p_idx)); } +ResourceUID::ID EditorFileSystemDirectory::get_file_uid(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, files.size(), ResourceUID::INVALID_ID); + return files[p_idx]->uid; +} + Vector EditorFileSystemDirectory::get_file_deps(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, files.size(), Vector()); Vector deps; diff --git a/editor/file_system/editor_file_system.h b/editor/file_system/editor_file_system.h index 93774502194..843b3757f59 100644 --- a/editor/file_system/editor_file_system.h +++ b/editor/file_system/editor_file_system.h @@ -91,6 +91,7 @@ public: int get_file_count() const; String get_file(int p_idx) const; String get_file_path(int p_idx) const; + ResourceUID::ID get_file_uid(int p_idx) const; StringName get_file_type(int p_idx) const; StringName get_file_resource_script_class(int p_idx) const; Vector get_file_deps(int p_idx) const; diff --git a/editor/gui/editor_quick_open_dialog.cpp b/editor/gui/editor_quick_open_dialog.cpp index 76371d220ef..223db86ee77 100644 --- a/editor/gui/editor_quick_open_dialog.cpp +++ b/editor/gui/editor_quick_open_dialog.cpp @@ -230,8 +230,11 @@ void EditorQuickOpenDialog::item_pressed(bool p_double_click) { } void EditorQuickOpenDialog::preview_property() { - Ref loaded_resource = ResourceLoader::load(container->get_selected()); - ERR_FAIL_COND_MSG(loaded_resource.is_null(), "Cannot load resource from path '" + container->get_selected() + "'."); + ERR_FAIL_COND(container->get_selected() == ResourceUID::INVALID_ID); + String path = container->get_selected_path(); + + Ref loaded_resource = ResourceLoader::load(path); + ERR_FAIL_COND_MSG(loaded_resource.is_null(), "Cannot load resource from path '" + path + "'."); Resource *res = Object::cast_to(property_object); if (res) { @@ -274,7 +277,7 @@ void EditorQuickOpenDialog::update_property() { ERR_FAIL_MSG(err_msg); } - item_selected_callback.call(container->get_selected()); + item_selected_callback.call(container->get_selected_path()); } void EditorQuickOpenDialog::cancel_pressed() { @@ -417,12 +420,15 @@ QuickOpenResultContainer::QuickOpenResultContainer() { } void QuickOpenResultContainer::_menu_option(int p_option) { + ERR_FAIL_COND(get_selected() == ResourceUID::INVALID_ID); + String selected_path = get_selected_path(); + switch (p_option) { case FILE_SHOW_IN_FILESYSTEM: { - FileSystemDock::get_singleton()->navigate_to_path(get_selected()); + FileSystemDock::get_singleton()->navigate_to_path(selected_path); } break; case FILE_SHOW_IN_FILE_MANAGER: { - String dir = ProjectSettings::get_singleton()->globalize_path(get_selected()); + String dir = ProjectSettings::get_singleton()->globalize_path(selected_path); OS::get_singleton()->shell_show_in_file_manager(dir, true); } break; } @@ -473,111 +479,146 @@ void QuickOpenResultContainer::init(const Vector &p_base_types) { E->enable_highlights = enable_highlights; } + bool history_modified = false; + if (first_open && history_file->load(_get_cache_file_path()) == OK) { // Load history when opening for the first time. file_type_icons.insert(SNAME("__default_icon"), get_editor_theme_icon(SNAME("Object"))); - bool history_modified = false; Vector history_keys = history_file->get_section_keys("selected_history"); for (const String &type : history_keys) { const StringName type_name = type; - const PackedStringArray paths = history_file->get_value("selected_history", type); + const PackedStringArray history_uids = history_file->get_value("selected_history", type); - PackedStringArray cleaned_paths; - cleaned_paths.resize(paths.size()); + PackedStringArray cleaned_text_uids; + cleaned_text_uids.resize(history_uids.size()); + + Vector cleaned_ids; + cleaned_ids.resize(history_uids.size()); - Vector loaded_candidates; - loaded_candidates.resize(paths.size()); { - QuickOpenResultCandidate *candidates_write = loaded_candidates.ptrw(); - String *cleanup_write = cleaned_paths.ptrw(); + String *text_write = cleaned_text_uids.ptrw(); + ResourceUID::ID *id_write = cleaned_ids.ptrw(); int i = 0; - for (String path : paths) { - if (path.begins_with("uid://")) { - ResourceUID::ID id = ResourceUID::get_singleton()->text_to_id(path); - if (!ResourceUID::get_singleton()->has_id(id)) { + for (String uid : history_uids) { +#ifndef DISABLE_DEPRECATED + if (!uid.begins_with("uid://")) { + // uid might be a path here, if config was written by older editor version + ResourceUID::ID id = EditorFileSystem::get_singleton()->get_file_uid(uid); + if (id == ResourceUID::INVALID_ID) { continue; } - path = ResourceUID::get_singleton()->get_id_path(id); + uid = ResourceUID::get_singleton()->id_to_text(id); } +#endif - if (!ResourceLoader::exists(path)) { + ResourceUID::ID id = ResourceUID::get_singleton()->text_to_id(uid); + if (id == ResourceUID::INVALID_ID || !ResourceUID::get_singleton()->has_id(id)) { continue; } - filetypes.insert(path, type_name); - QuickOpenResultCandidate candidate; - _setup_candidate(candidate, path); - candidates_write[i] = candidate; - cleanup_write[i] = path; + filetypes.insert(id, type_name); + text_write[i] = uid; + id_write[i] = id; i++; } - loaded_candidates.resize(i); - cleaned_paths.resize(i); - selected_history.insert(type, loaded_candidates); - if (i < paths.size()) { + cleaned_text_uids.resize(i); + selected_history.insert(type, cleaned_ids); + + if (i < history_uids.size()) { // Some paths removed, need to update history. if (i == 0) { history_file->erase_section_key("selected_history", type); } else { - history_file->set_value("selected_history", type, cleaned_paths); + history_file->set_value("selected_history", type, cleaned_text_uids); } history_modified = true; } } } - if (history_modified) { - history_file->save(_get_cache_file_path()); + } else if (!first_open && base_types.size() == 1) { + const StringName &type = base_types[0]; + Vector *history = selected_history.getptr(type); + + if (history) { + Vector clean_history; + + for (const ResourceUID::ID &uid : *history) { + if (ResourceUID::get_singleton()->has_id(uid)) { + clean_history.push_back(uid); + } else { + history_modified = true; + } + } + + if (clean_history.is_empty()) { + selected_history.erase(type); + } else if (history_modified) { + *history = clean_history; + } } } + if (history_modified) { + history_file->save(_get_cache_file_path()); + } + _create_initial_results(); } -void QuickOpenResultContainer::_sort_filepaths(int p_max_results) { +void QuickOpenResultContainer::_sort_uids(int p_max_results) { struct FilepathComparator { - bool operator()(const String &p_lhs, const String &p_rhs) const { + bool operator()(const ResourceUID::ID &p_lhs, const ResourceUID::ID &p_rhs) const { + String lhs_path = ResourceUID::get_singleton()->get_id_path(p_lhs); + String rhs_path = ResourceUID::get_singleton()->get_id_path(p_rhs); + // Sort on (length, alphanumeric) to prioritize shorter filepaths - return p_lhs.length() == p_rhs.length() ? p_lhs < p_rhs : p_lhs.length() < p_rhs.length(); + return lhs_path.length() == rhs_path.length() ? lhs_path < rhs_path : lhs_path.length() < rhs_path.length(); } }; - SortArray sorter; - if (filepaths.size() > p_max_results) { - sorter.partial_sort(0, filepaths.size(), p_max_results, filepaths.ptrw()); + SortArray sorter{}; + + if ((int)uids.size() > p_max_results) { + sorter.partial_sort(0, uids.size(), p_max_results, uids.ptr()); } else { - sorter.sort(filepaths.ptrw(), filepaths.size()); + sorter.sort(uids.ptr(), uids.size()); } } void QuickOpenResultContainer::_create_initial_results() { file_type_icons.clear(); file_type_icons.insert(SNAME("__default_icon"), get_editor_theme_icon(SNAME("Object"))); - filepaths.clear(); + uids.clear(); filetypes.clear(); history_set.clear(); - Vector *history = _get_history(); + + Vector *history = _get_history(); if (history) { - for (const QuickOpenResultCandidate &candidate : *history) { - history_set.insert(candidate.file_path); + for (const ResourceUID::ID &uid : *history) { + history_set.insert(uid); } } - _find_filepaths_in_folder(EditorFileSystem::get_singleton()->get_filesystem(), include_addons_toggle->is_pressed()); - _sort_filepaths(result_items.size()); - max_total_results = MIN(filepaths.size(), result_items.size()); + + _find_uids_in_folder(EditorFileSystem::get_singleton()->get_filesystem(), include_addons_toggle->is_pressed()); + _sort_uids(result_items.size()); + max_total_results = MIN(uids.size(), result_items.size()); update_results(); } -void QuickOpenResultContainer::_find_filepaths_in_folder(EditorFileSystemDirectory *p_directory, bool p_include_addons) { +void QuickOpenResultContainer::_find_uids_in_folder(EditorFileSystemDirectory *p_directory, bool p_include_addons) { for (int i = 0; i < p_directory->get_subdir_count(); i++) { if (p_include_addons || p_directory->get_name() != "addons") { - _find_filepaths_in_folder(p_directory->get_subdir(i), p_include_addons); + _find_uids_in_folder(p_directory->get_subdir(i), p_include_addons); } } for (int i = 0; i < p_directory->get_file_count(); i++) { - String file_path = p_directory->get_file_path(i); + ResourceUID::ID uid = p_directory->get_file_uid(i); + if (uid == ResourceUID::INVALID_ID) { + continue; + } const StringName engine_type = p_directory->get_file_type(i); const StringName script_type = p_directory->get_file_resource_script_class(i); @@ -589,8 +630,8 @@ void QuickOpenResultContainer::_find_filepaths_in_folder(EditorFileSystemDirecto bool is_valid = ClassDB::is_parent_class(engine_type, parent_type) || (!is_engine_type && EditorNode::get_editor_data().script_class_is_parent(script_type, parent_type)); if (is_valid) { - filepaths.append(file_path); - filetypes.insert(file_path, actual_type); + uids.push_back(uid); + filetypes.insert(uid, actual_type); break; // Stop testing base types as soon as we get a match. } } @@ -602,26 +643,53 @@ void QuickOpenResultContainer::set_query_and_update(const String &p_query) { update_results(); } -Vector *QuickOpenResultContainer::_get_history() { +Vector *QuickOpenResultContainer::_get_history() { if (base_types.size() == 1) { return selected_history.getptr(base_types[0]); } return nullptr; } -void QuickOpenResultContainer::_setup_candidate(QuickOpenResultCandidate &p_candidate, const String &p_filepath) { - p_candidate.file_path = ResourceUID::ensure_path(p_filepath); - p_candidate.result = nullptr; +QuickOpenResultCandidate QuickOpenResultCandidate::from_uid(const ResourceUID::ID &p_uid, bool &r_success) { + if (p_uid == ResourceUID::INVALID_ID || !ResourceUID::get_singleton()->has_id(p_uid)) { + r_success = false; + return QuickOpenResultCandidate(); + } + + QuickOpenResultCandidate candidate; + candidate.uid = p_uid; + candidate.result = nullptr; + r_success = true; + return candidate; +} + +QuickOpenResultCandidate QuickOpenResultCandidate::from_result(const FuzzySearchResult &p_result, bool &r_success) { + ResourceUID::ID uid = EditorFileSystem::get_singleton()->get_file_uid(p_result.target); + + QuickOpenResultCandidate candidate = from_uid(uid, r_success); + if (!r_success) { + return QuickOpenResultCandidate(); + } + + candidate.result = &p_result; + return candidate; +} + +void QuickOpenResultContainer::_add_candidate(QuickOpenResultCandidate &p_candidate) { + ERR_FAIL_COND(!ResourceUID::get_singleton()->has_id(p_candidate.uid)); + StringName actual_type; { - StringName *actual_type_ptr = filetypes.getptr(p_filepath); + StringName *actual_type_ptr = filetypes.getptr(p_candidate.uid); if (actual_type_ptr) { actual_type = *actual_type_ptr; } else { - ERR_PRINT(vformat("EditorQuickOpenDialog: No type for path %s.", p_candidate.file_path)); + ERR_PRINT(vformat("EditorQuickOpenDialog: No type for path %s.", ResourceUID::get_singleton()->get_id_path(p_candidate.uid))); } } - EditorResourcePreview::PreviewItem item = EditorResourcePreview::get_singleton()->get_resource_preview_if_available(p_candidate.file_path); + + String file_path = ResourceUID::get_singleton()->get_id_path(p_candidate.uid); + EditorResourcePreview::PreviewItem item = EditorResourcePreview::get_singleton()->get_resource_preview_if_available(file_path); if (item.preview.is_valid()) { p_candidate.thumbnail = item.preview; } else if (file_type_icons.has(actual_type)) { @@ -632,46 +700,54 @@ void QuickOpenResultContainer::_setup_candidate(QuickOpenResultCandidate &p_cand } else { p_candidate.thumbnail = *file_type_icons.getptr(SNAME("__default_icon")); } -} -void QuickOpenResultContainer::_setup_candidate(QuickOpenResultCandidate &p_candidate, const FuzzySearchResult &p_result) { - _setup_candidate(p_candidate, p_result.target); - p_candidate.result = &p_result; + candidates.push_back(p_candidate); + candidates_uids.insert(p_candidate.uid); } void QuickOpenResultContainer::update_results() { candidates.clear(); + candidates_uids.clear(); + if (query.is_empty()) { _use_default_candidates(); } else { _score_and_sort_candidates(); } + _update_result_items(MIN(candidates.size(), max_total_results), 0); } void QuickOpenResultContainer::_use_default_candidates() { - HashSet existing_paths; - Vector *history = _get_history(); + HashSet existing_uids; + + Vector *history = _get_history(); if (history) { - candidates.append_array(*history); - for (const QuickOpenResultCandidate &candi : *history) { - existing_paths.insert(candi.file_path); + for (const ResourceUID::ID &uid : *history) { + bool success; + QuickOpenResultCandidate candidate = QuickOpenResultCandidate::from_uid(uid, success); + if (!success) { + continue; + } + _add_candidate(candidate); } } - int i = candidates.size(); - candidates.resize(MIN(max_total_results, filepaths.size())); - QuickOpenResultCandidate *candidates_w = candidates.ptrw(); - int count = candidates.size(); - - for (const String &filepath : filepaths) { - if (i >= count) { + for (const ResourceUID::ID &uid : uids) { + if (candidates.size() >= max_total_results) { break; } - if (existing_paths.has(filepath)) { + if (candidates_uids.has(uid)) { continue; } - _setup_candidate(candidates_w[i++], filepath); + + bool success; + QuickOpenResultCandidate candidate = QuickOpenResultCandidate::from_uid(uid, success); + if (!success) { + continue; + } + + _add_candidate(candidate); } } @@ -684,15 +760,28 @@ void QuickOpenResultContainer::_update_fuzzy_search_results() { int max_misses = EDITOR_GET("filesystem/quick_open_dialog/max_fuzzy_misses"); fuzzy_search.allow_subsequences = fuzzy_matching; fuzzy_search.max_misses = fuzzy_matching ? max_misses : 0; - fuzzy_search.search_all(filepaths, search_results); + + PackedStringArray paths; + paths.reserve_exact(uids.size()); + + for (const ResourceUID::ID &uid : uids) { + paths.push_back(ResourceUID::get_singleton()->get_id_path(uid)); + } + + fuzzy_search.search_all(paths, search_results); } void QuickOpenResultContainer::_score_and_sort_candidates() { _update_fuzzy_search_results(); - candidates.resize(search_results.size()); - QuickOpenResultCandidate *candidates_write = candidates.ptrw(); + for (const FuzzySearchResult &result : search_results) { - _setup_candidate(*candidates_write++, result); + bool success; + QuickOpenResultCandidate candidate = QuickOpenResultCandidate::from_result(result, success); + if (!success) { + continue; + } + + _add_candidate(candidate); } } @@ -718,7 +807,7 @@ void QuickOpenResultContainer::_update_result_items(int p_new_visible_results_co no_results_container->set_visible(!any_results); if (!any_results) { - if (filepaths.is_empty()) { + if (uids.is_empty()) { no_results_label->set_text(TTR("No files found for this type")); } else { no_results_label->set_text(TTR("No results found")); @@ -815,8 +904,8 @@ void QuickOpenResultContainer::_select_item(int p_index) { } result_items[selection_index]->highlight_item(true); - bool in_history = history_set.has(candidates[selection_index].file_path); - file_details_path->set_text(get_selected() + (in_history ? TTR(" (recently opened)") : "")); + bool in_history = history_set.has(candidates[selection_index].uid); + file_details_path->set_text(get_selected_path() + (in_history ? TTR(" (recently opened)") : "")); emit_signal(SNAME("selection_changed")); @@ -926,9 +1015,16 @@ bool QuickOpenResultContainer::has_nothing_selected() const { return selection_index < 0; } -String QuickOpenResultContainer::get_selected() const { - ERR_FAIL_COND_V_MSG(has_nothing_selected(), String(), "Tried to get selected file, but nothing was selected."); - return candidates[selection_index].file_path; +ResourceUID::ID QuickOpenResultContainer::get_selected() const { + ERR_FAIL_COND_V_MSG(has_nothing_selected(), ResourceUID::INVALID_ID, "Tried to get selected file, but nothing was selected."); + return candidates[selection_index].uid; +} + +String QuickOpenResultContainer::get_selected_path() const { + ERR_FAIL_COND_V_MSG(has_nothing_selected(), "", "Tried to get selected file path, but nothing was selected."); + String path = ResourceUID::get_singleton()->get_id_path(candidates[selection_index].uid); + ERR_FAIL_COND_V_MSG(path.is_empty(), "", "Failed to get selected file path."); + return path; } QuickOpenDisplayMode QuickOpenResultContainer::get_adaptive_display_mode(const Vector &p_base_types) { @@ -971,40 +1067,39 @@ void QuickOpenResultContainer::save_selected_item() { } const StringName &base_type = base_types[0]; - QuickOpenResultCandidate &selected = candidates.write[selection_index]; - Vector *type_history = selected_history.getptr(base_type); + ResourceUID::ID selected = get_selected(); + Vector *type_history = selected_history.getptr(base_type); if (!type_history) { - selected_history.insert(base_type, Vector()); + selected_history.insert(base_type, Vector()); type_history = selected_history.getptr(base_type); } else { for (int i = 0; i < type_history->size(); i++) { - if (selected.file_path == type_history->get(i).file_path) { + if (selected == type_history->get(i)) { type_history->remove_at(i); break; } } } - selected.result = nullptr; - history_set.insert(selected.file_path); + history_set.insert(selected); type_history->insert(0, selected); if (type_history->size() > MAX_HISTORY_SIZE) { type_history->resize(MAX_HISTORY_SIZE); } - PackedStringArray paths; - paths.resize(type_history->size()); + PackedStringArray history_uids; + history_uids.resize(type_history->size()); { - String *paths_write = paths.ptrw(); + String *uids_write = history_uids.ptrw(); int i = 0; - for (const QuickOpenResultCandidate &candidate : *type_history) { - paths_write[i] = _get_uid_string(candidate.file_path); + for (const ResourceUID::ID &uid : *type_history) { + uids_write[i] = ResourceUID::get_singleton()->id_to_text(uid); i++; } } - history_file->set_value("selected_history", base_type, paths); + history_file->set_value("selected_history", base_type, history_uids); history_file->save(_get_cache_file_path()); } @@ -1200,8 +1295,10 @@ QuickOpenResultListItem::QuickOpenResultListItem() { void QuickOpenResultListItem::set_content(const QuickOpenResultCandidate &p_candidate, bool p_highlight) { thumbnail->set_texture(p_candidate.thumbnail); - name->set_text(p_candidate.file_path.get_file()); - path->set_text(p_candidate.file_path.get_base_dir()); + + String file_path = ResourceUID::get_singleton()->get_id_path(p_candidate.uid); + name->set_text(file_path.get_file()); + path->set_text(file_path.get_base_dir()); name->reset_highlights(); path->reset_highlights(); @@ -1271,8 +1368,10 @@ QuickOpenResultGridItem::QuickOpenResultGridItem() { void QuickOpenResultGridItem::set_content(const QuickOpenResultCandidate &p_candidate, bool p_highlight) { thumbnail->set_texture(p_candidate.thumbnail); - name->set_text(p_candidate.file_path.get_file()); - name->set_tooltip_text(p_candidate.file_path); + + String file_path = ResourceUID::get_singleton()->get_id_path(p_candidate.uid); + name->set_text(file_path.get_file()); + name->set_tooltip_text(file_path); name->reset_highlights(); if (p_highlight && p_candidate.result != nullptr) { diff --git a/editor/gui/editor_quick_open_dialog.h b/editor/gui/editor_quick_open_dialog.h index 025b0f4e1ed..e3fc4f2ef07 100644 --- a/editor/gui/editor_quick_open_dialog.h +++ b/editor/gui/editor_quick_open_dialog.h @@ -60,9 +60,12 @@ enum class QuickOpenDisplayMode { }; struct QuickOpenResultCandidate { - String file_path; + ResourceUID::ID uid; Ref thumbnail; const FuzzySearchResult *result = nullptr; + + static QuickOpenResultCandidate from_uid(const ResourceUID::ID &p_uid, bool &r_success); + static QuickOpenResultCandidate from_result(const FuzzySearchResult &p_result, bool &r_success); }; class HighlightedLabel : public Label { @@ -95,7 +98,8 @@ public: void update_results(); bool has_nothing_selected() const; - String get_selected() const; + ResourceUID::ID get_selected() const; + String get_selected_path() const; bool is_instant_preview_enabled() const; void set_instant_preview_toggle_visible(bool p_visible); @@ -113,12 +117,13 @@ private: Vector search_results; Vector base_types; - Vector filepaths; - AHashMap filetypes; + LocalVector uids; + AHashMap filetypes; Vector candidates; + HashSet candidates_uids; - AHashMap> selected_history; - HashSet history_set; + AHashMap> selected_history; + HashSet history_set; String query; int selection_index = -1; @@ -151,13 +156,12 @@ private: static QuickOpenDisplayMode get_adaptive_display_mode(const Vector &p_base_types); void _ensure_result_vector_capacity(); - void _sort_filepaths(int p_max_results); + void _sort_uids(int p_max_results); void _create_initial_results(); - void _find_filepaths_in_folder(EditorFileSystemDirectory *p_directory, bool p_include_addons); + void _find_uids_in_folder(EditorFileSystemDirectory *p_directory, bool p_include_addons); - Vector *_get_history(); - void _setup_candidate(QuickOpenResultCandidate &p_candidate, const String &p_filepath); - void _setup_candidate(QuickOpenResultCandidate &p_candidate, const FuzzySearchResult &p_result); + Vector *_get_history(); + void _add_candidate(QuickOpenResultCandidate &p_candidate); void _update_fuzzy_search_results(); void _use_default_candidates(); void _score_and_sort_candidates();