diff --git a/doc/classes/EditorResourcePreview.xml b/doc/classes/EditorResourcePreview.xml index 68ead12c0302..ed0d5e063d2b 100644 --- a/doc/classes/EditorResourcePreview.xml +++ b/doc/classes/EditorResourcePreview.xml @@ -31,7 +31,7 @@ - Queue the [param resource] being edited for preview. Once the preview is ready, the [param receiver]'s [param receiver_func] will be called. The [param receiver_func] must take the following four arguments: [String] path, [Texture2D] preview, [Texture2D] thumbnail_preview, [Variant] userdata. [param userdata] can be anything, and will be returned when [param receiver_func] is called. + Queue the [param resource] being edited for preview. Once the preview is ready, the [param receiver]'s [param receiver_func] will be called. The [param receiver_func] must take the following four arguments: [String] path, [Texture2D] preview, [Texture2D] thumbnail_preview, [Texture2D] custom_type_icon, [Variant] userdata. [param userdata] can be anything, and will be returned when [param receiver_func] is called. custom_type_icon is the custom icon of the resource if it has one. [b]Note:[/b] If it was not possible to create the preview the [param receiver_func] will still be called, but the preview will be null. @@ -42,7 +42,7 @@ - Queue a resource file located at [param path] for preview. Once the preview is ready, the [param receiver]'s [param receiver_func] will be called. The [param receiver_func] must take the following four arguments: [String] path, [Texture2D] preview, [Texture2D] thumbnail_preview, [Variant] userdata. [param userdata] can be anything, and will be returned when [param receiver_func] is called. + Queue a resource file located at [param path] for preview. Once the preview is ready, the [param receiver]'s [param receiver_func] will be called. The [param receiver_func] must take the following four arguments: [String] path, [Texture2D] preview, [Texture2D] thumbnail_preview, [Texture2D] custom_type_icon, [Variant] userdata. [param userdata] can be anything, and will be returned when [param receiver_func] is called. custom_type_icon is the custom icon of the resource if it has one. [b]Note:[/b] If it was not possible to create the preview the [param receiver_func] will still be called, but the preview will be null. diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index 80d1e9dcd7e4..5da61f04e9ae 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -332,7 +332,7 @@ void EditorFileDialog::_post_popup() { set_process_shortcut_input(true); } -void EditorFileDialog::_thumbnail_result(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Variant &p_udata) { +void EditorFileDialog::_thumbnail_result(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Ref &p_custom_type_icon, const Variant &p_udata) { if (display_mode == DISPLAY_LIST || p_preview.is_null()) { return; } @@ -347,7 +347,7 @@ void EditorFileDialog::_thumbnail_result(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Variant &p_udata) { +void EditorFileDialog::_thumbnail_done(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Ref &p_custom_type_icon, const Variant &p_udata) { set_process(false); preview_waiting = false; diff --git a/editor/editor_file_dialog.h b/editor/editor_file_dialog.h index 021f2e6d4cee..043bb4f9ab55 100644 --- a/editor/editor_file_dialog.h +++ b/editor/editor_file_dialog.h @@ -221,8 +221,8 @@ class EditorFileDialog : public ConfirmationDialog { void _save_to_recent(); // Callback function is callback(String p_path,Ref preview,Variant udata) preview null if could not load. - void _thumbnail_result(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Variant &p_udata); - void _thumbnail_done(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Variant &p_udata); + void _thumbnail_result(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Ref &p_custom_type_icon, const Variant &p_udata); + void _thumbnail_done(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Ref &p_custom_type_icon, const Variant &p_udata); void _request_single_thumbnail(const String &p_path); virtual void shortcut_input(const Ref &p_event) override; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index b854da8e4f4d..cf576a3527e0 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -5521,7 +5521,7 @@ void EditorNode::_reposition_active_tab(int idx_to) { _update_scene_tabs(); } -void EditorNode::_thumbnail_done(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Variant &p_udata) { +void EditorNode::_thumbnail_done(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Ref &p_custom_type_icon, const Variant &p_udata) { int p_tab = p_udata.operator signed int(); if (p_preview.is_valid()) { Rect2 rect = scene_tabs->get_tab_rect(p_tab); diff --git a/editor/editor_node.h b/editor/editor_node.h index eefe45ca1fa1..6a3fc2cb9346 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -656,7 +656,7 @@ class EditorNode : public Node { void _scene_tab_exit(); void _scene_tab_input(const Ref &p_input); void _reposition_active_tab(int idx_to); - void _thumbnail_done(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Variant &p_udata); + void _thumbnail_done(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Ref &p_custom_type_icon, const Variant &p_udata); void _scene_tab_script_edited(int p_tab); Dictionary _get_main_scene_state(); diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index db4d12c7610a..d11e3f6b61d6 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -99,7 +99,7 @@ void EditorResourcePreview::_thread_func(void *ud) { erp->_thread(); } -void EditorResourcePreview::_preview_ready(const String &p_str, const Ref &p_texture, const Ref &p_small_texture, ObjectID id, const StringName &p_func, const Variant &p_ud) { +void EditorResourcePreview::_preview_ready(const String &p_str, const Ref &p_texture, const Ref &p_small_texture, const Ref &p_custom_type_icon, ObjectID id, const StringName &p_func, const Variant &p_ud) { String path = p_str; { MutexLock lock(preview_mutex); @@ -118,16 +118,17 @@ void EditorResourcePreview::_preview_ready(const String &p_str, const Refpush_call(id, p_func, path, p_texture, p_small_texture, p_ud); + MessageQueue::get_singleton()->push_call(id, p_func, path, p_texture, p_small_texture, p_custom_type_icon, p_ud); } -void EditorResourcePreview::_generate_preview(Ref &r_texture, Ref &r_small_texture, const QueueItem &p_item, const String &cache_base) { +void EditorResourcePreview::_generate_preview(Ref &r_texture, Ref &r_small_texture, Ref &r_custom_type_icon, const QueueItem &p_item, const String &cache_base) { String type; if (p_item.resource.is_valid()) { @@ -184,11 +185,42 @@ void EditorResourcePreview::_generate_preview(Ref &r_texture, Ref< break; } + // Fetch custom type icon. + r_custom_type_icon = Ref(); + Ref res; + if (p_item.resource.is_valid()) { + res = p_item.resource; + } else { + res = ResourceLoader::load(p_item.path, ""); + } + if (res.is_valid()) { + r_custom_type_icon = EditorNode::get_singleton()->get_object_icon(res.ptr(), ""); + } + if (!p_item.resource.is_valid()) { + // Cache the preview in case it's a resource on disk + bool has_custom_type_icon = r_custom_type_icon.is_valid(); + bool has_texture = r_texture.is_valid(); + bool has_small_texture = r_small_texture.is_valid(); + if (has_texture || has_custom_type_icon) { + // Preview is valid, saving the cache now... + if (has_texture) { + ResourceSaver::save(r_texture, cache_base + ".png"); + + // Small texture could only exist if the main texture exists. + if (has_small_texture) { + ResourceSaver::save(r_small_texture, cache_base + "_small.png"); + } + } + if (has_custom_type_icon) { + ResourceSaver::save(r_custom_type_icon, cache_base + "_custom_type_icon.png"); + } + } + // cache the preview in case it's a resource on disk if (r_texture.is_valid()) { //wow it generated a preview... save cache - bool has_small_texture = r_small_texture.is_valid(); + has_small_texture = r_small_texture.is_valid(); ResourceSaver::save(r_texture, cache_base + ".png"); if (has_small_texture) { ResourceSaver::save(r_small_texture, cache_base + "_small.png"); @@ -196,7 +228,9 @@ void EditorResourcePreview::_generate_preview(Ref &r_texture, Ref< Ref f = FileAccess::open(cache_base + ".txt", FileAccess::WRITE); ERR_FAIL_COND_MSG(f.is_null(), "Cannot create file '" + cache_base + ".txt'. Check user write permissions."); f->store_line(itos(thumbnail_size)); + f->store_line(itos(has_texture)); f->store_line(itos(has_small_texture)); + f->store_line(itos(has_custom_type_icon)); f->store_line(itos(FileAccess::get_modified_time(p_item.path))); f->store_line(FileAccess::get_md5(p_item.path)); } @@ -217,7 +251,7 @@ void EditorResourcePreview::_iterate() { path += ":" + itos(cache[item.path].last_hash); //keep last hash (see description of what this is in condition below) } - _preview_ready(path, cache[item.path].preview, cache[item.path].small_preview, item.id, item.function, item.userdata); + _preview_ready(path, cache[item.path].preview, cache[item.path].small_preview, cache[item.path].custom_type_icon, item.id, item.function, item.userdata); preview_mutex.unlock(); } else { @@ -225,15 +259,16 @@ void EditorResourcePreview::_iterate() { Ref texture; Ref small_texture; + Ref custom_type_icon_texture; int thumbnail_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size"); thumbnail_size *= EDSCALE; if (item.resource.is_valid()) { - _generate_preview(texture, small_texture, item, String()); + _generate_preview(texture, small_texture, custom_type_icon_texture, item, String()); //adding hash to the end of path (should be ID::) because of 5 argument limit to call_deferred - _preview_ready(item.path + ":" + itos(item.resource->hash_edited_version()), texture, small_texture, item.id, item.function, item.userdata); + _preview_ready(item.path + ":" + itos(item.resource->hash_edited_version()), texture, small_texture, custom_type_icon_texture, item.id, item.function, item.userdata); } else { String temp_path = EditorPaths::get_singleton()->get_cache_dir(); @@ -246,11 +281,13 @@ void EditorResourcePreview::_iterate() { Ref f = FileAccess::open(file, FileAccess::READ); if (f.is_null()) { // No cache found, generate - _generate_preview(texture, small_texture, item, cache_base); + _generate_preview(texture, small_texture, custom_type_icon_texture, item, cache_base); } else { uint64_t modtime = FileAccess::get_modified_time(item.path); int tsize = f->get_line().to_int(); + bool has_texture = f->get_line().to_int(); bool has_small_texture = f->get_line().to_int(); + bool has_custom_type_icon = f->get_line().to_int(); uint64_t last_modtime = f->get_line().to_int(); bool cache_valid = true; @@ -275,7 +312,9 @@ void EditorResourcePreview::_iterate() { ERR_PRINT("Cannot create file '" + file + "'. Check user write permissions."); } else { f2->store_line(itos(thumbnail_size)); + f2->store_line(itos(has_texture)); f2->store_line(itos(has_small_texture)); + f2->store_line(itos(has_custom_type_icon)); f2->store_line(itos(modtime)); f2->store_line(md5); } @@ -289,32 +328,44 @@ void EditorResourcePreview::_iterate() { img.instantiate(); Ref small_img; small_img.instantiate(); + Ref custom_type_icon_img; + custom_type_icon_img.instantiate(); - if (img->load(cache_base + ".png") != OK) { - cache_valid = false; - } else { - texture.instantiate(); - texture->set_image(img); - - if (has_small_texture) { - if (small_img->load(cache_base + "_small.png") != OK) { - cache_valid = false; - } else { - small_texture.instantiate(); - small_texture->set_image(small_img); + if (has_texture) { + if (img->load(cache_base + ".png") != OK) { + cache_valid = false; + } else { + texture.instantiate(); + texture->create_from_image(img); + + if (has_small_texture) { + if (small_img->load(cache_base + "_small.png") != OK) { + cache_valid = false; + } else { + small_texture.instantiate(); + small_texture->create_from_image(small_img); + } } } } + + if (has_custom_type_icon) { + if (custom_type_icon_img->load(cache_base + "_custom_type_icon.png") != OK) { + cache_valid = false; + } else { + custom_type_icon_texture.instantiate(); + custom_type_icon_texture->create_from_image(custom_type_icon_img); + } + } } if (!cache_valid) { - _generate_preview(texture, small_texture, item, cache_base); + _generate_preview(texture, small_texture, custom_type_icon_texture, item, cache_base); } } - _preview_ready(item.path, texture, small_texture, item.id, item.function, item.userdata); + _preview_ready(item.path, texture, small_texture, custom_type_icon_texture, item.id, item.function, item.userdata); } } - } else { preview_mutex.unlock(); } @@ -340,7 +391,7 @@ void EditorResourcePreview::queue_edited_resource_preview(const Ref &p if (cache.has(path_id) && cache[path_id].last_hash == p_res->hash_edited_version()) { cache[path_id].order = order++; - p_receiver->call(p_receiver_func, path_id, cache[path_id].preview, cache[path_id].small_preview, p_userdata); + p_receiver->call(p_receiver_func, path_id, cache[path_id].preview, cache[path_id].small_preview, cache[path_id].custom_type_icon, p_userdata); return; } @@ -365,7 +416,7 @@ void EditorResourcePreview::queue_resource_preview(const String &p_path, Object if (cache.has(p_path)) { cache[p_path].order = order++; - p_receiver->call(p_receiver_func, p_path, cache[p_path].preview, cache[p_path].small_preview, p_userdata); + p_receiver->call(p_receiver_func, p_path, cache[p_path].preview, cache[p_path].small_preview, cache[p_path].custom_type_icon, p_userdata); return; } diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h index aae7c5b164cb..b2c153490a75 100644 --- a/editor/editor_resource_preview.h +++ b/editor/editor_resource_preview.h @@ -84,6 +84,7 @@ class EditorResourcePreview : public Node { struct Item { Ref preview; Ref small_preview; + Ref custom_type_icon; int order = 0; uint32_t last_hash = 0; uint64_t modified_time = 0; @@ -93,8 +94,8 @@ class EditorResourcePreview : public Node { HashMap cache; - void _preview_ready(const String &p_str, const Ref &p_texture, const Ref &p_small_texture, ObjectID id, const StringName &p_func, const Variant &p_ud); - void _generate_preview(Ref &r_texture, Ref &r_small_texture, const QueueItem &p_item, const String &cache_base); + void _preview_ready(const String &p_str, const Ref &p_texture, const Ref &p_small_texture, const Ref &p_custom_type_icon, ObjectID id, const StringName &p_func, const Variant &p_ud); + void _generate_preview(Ref &r_texture, Ref &r_small_texture, Ref &p_custom_type_icon, const QueueItem &p_item, const String &cache_base); static void _thread_func(void *ud); void _thread(); @@ -108,7 +109,7 @@ class EditorResourcePreview : public Node { public: static EditorResourcePreview *get_singleton(); - // p_receiver_func callback has signature (String p_path, Ref p_preview, Ref p_preview_small, Variant p_userdata) + // p_receiver_func callback has signature (String p_path, Ref p_preview, Ref p_preview_small, Ref p_custom_type_icon, Variant p_userdata) // p_preview will be null if there was an error void queue_resource_preview(const String &p_path, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata); void queue_edited_resource_preview(const Ref &p_res, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index e1924c7994c8..05d9b390c94a 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -132,6 +132,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory FileInfo fi; fi.name = p_dir->get_file(i); fi.type = p_dir->get_file_type(i); + fi.path = p_dir->get_file_path(i); fi.import_broken = !p_dir->get_file_import_is_valid(i); fi.modified_time = p_dir->get_file_modified_time(i); @@ -157,8 +158,10 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory file_item->set_custom_color(0, get_theme_color(SNAME("accent_color"), SNAME("Editor"))); } Array udata; - udata.push_back(tree_update_id); - udata.push_back(file_item); + udata.resize(3); + udata[0] = tree_update_id; + udata[1] = file_item; + udata[2] = fi.type; EditorResourcePreview::get_singleton()->queue_resource_preview(file_metadata, this, "_tree_thumbnail_done", udata); } } else if (display_mode == DISPLAY_MODE_SPLIT) { @@ -286,8 +289,10 @@ void FileSystemDock::_update_tree(const Vector &p_uncollapsed_paths, boo } if (!favorite.ends_with("/")) { Array udata; - udata.push_back(tree_update_id); - udata.push_back(ti); + udata.resize(3); + udata[0] = tree_update_id; + udata[1] = ti; + udata[2] = ResourceLoader::get_resource_type(favorite); EditorResourcePreview::get_singleton()->queue_resource_preview(favorite, this, "_tree_thumbnail_done", udata); } } @@ -581,30 +586,45 @@ void FileSystemDock::navigate_to_path(const String &p_path) { _navigate_to_path(p_path); } -void FileSystemDock::_file_list_thumbnail_done(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Variant &p_udata) { - if ((file_list_vb->is_visible_in_tree() || path == p_path.get_base_dir()) && p_preview.is_valid()) { +void FileSystemDock::_file_list_thumbnail_done(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Ref &p_custom_type_icon, const Variant &p_udata) { + if ((file_list_vb->is_visible_in_tree() || path == p_path.get_base_dir()) && (p_preview.is_valid() || p_custom_type_icon.is_valid())) { Array uarr = p_udata; int idx = uarr[0]; String file = uarr[1]; + String resource_type = uarr[2]; if (idx < files->get_item_count() && files->get_item_text(idx) == file && files->get_item_metadata(idx) == p_path) { if (file_list_display_mode == FILE_LIST_DISPLAY_LIST) { + // Prioritize showing previews before custom icons. if (p_small_preview.is_valid()) { files->set_item_icon(idx, p_small_preview); + } else if (resource_type == "Resource" && p_custom_type_icon.is_valid()) { + files->set_item_icon(idx, p_custom_type_icon); } } else { - files->set_item_icon(idx, p_preview); + if (resource_type == "Resource" && p_custom_type_icon.is_valid()) { + files->set_item_tag_icon(idx, p_custom_type_icon); + } + if (p_preview.is_valid()) { + files->set_item_icon(idx, p_preview); + } } } } } -void FileSystemDock::_tree_thumbnail_done(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Variant &p_udata) { - if (p_small_preview.is_valid()) { +void FileSystemDock::_tree_thumbnail_done(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Ref &p_custom_type_icon, const Variant &p_udata) { + if (p_small_preview.is_valid() || p_custom_type_icon.is_valid()) { Array uarr = p_udata; if (tree_update_id == (int)uarr[0]) { TreeItem *file_item = Object::cast_to(uarr[1]); + String resource_type = (String)uarr[2]; if (file_item) { - file_item->set_icon(0, p_small_preview); + // Prioritize showing previews before custom icons. + if (p_small_preview.is_valid()) { + file_item->set_icon(0, p_small_preview); + } else if (resource_type == "Resource" && p_custom_type_icon.is_valid()) { + file_item->set_icon(0, p_custom_type_icon); + } } } } @@ -941,9 +961,10 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) { // Generate the preview. if (!finfo->import_broken) { Array udata; - udata.resize(2); + udata.resize(3); udata[0] = item_index; udata[1] = fname; + udata[2] = ftype; EditorResourcePreview::get_singleton()->queue_resource_preview(fpath, this, "_file_list_thumbnail_done", udata); } @@ -1083,9 +1104,10 @@ void FileSystemDock::_preview_invalidated(const String &p_path) { if (files->get_item_metadata(i) == p_path) { // Re-request preview. Array udata; - udata.resize(2); + udata.resize(3); udata[0] = i; udata[1] = files->get_item_text(i); + udata[2] = ResourceLoader::get_resource_type(p_path); EditorResourcePreview::get_singleton()->queue_resource_preview(p_path, this, "_file_list_thumbnail_done", udata); break; } diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 9060f5c0a4d9..c3d312373afe 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -295,8 +295,8 @@ class FileSystemDock : public VBoxContainer { void _get_drag_target_folder(String &target, bool &target_favorites, const Point2 &p_point, Control *p_from) const; void _preview_invalidated(const String &p_path); - void _file_list_thumbnail_done(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Variant &p_udata); - void _tree_thumbnail_done(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Variant &p_udata); + void _file_list_thumbnail_done(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Ref &p_custom_type_icon, const Variant &p_udata); + void _tree_thumbnail_done(const String &p_path, const Ref &p_preview, const Ref &p_small_preview, const Ref &p_custom_type_icon, const Variant &p_udata); void _update_display_mode(bool p_force = false);