From 64bb49fa4e3c8bd7e02fd6e5252e40f089d787de Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 21 Jul 2021 20:17:34 +0200 Subject: Cleanup: Move reorganize asset files I'm trying to move away from general files with lots of things in them, and instead have many small & focused files. I find that easier to work with since everything has clear responsibilities, even if there is some minor overhead in managing all these files. I also try to differentiate more clearly between public and internal files. So source files and internal headers are in a `intern/` sub-directory, public functions are in a number of headers one level higher. For convenience and to make this compatible with our existing general headers in `editors/include`, I made the `ED_asset.h` there include all these public headers. This is of course a bit of an experiment, let's see how it works in practice. Also corrected the name of `ED_asset_can_make_single_from_context()`. --- source/blender/editors/asset/asset_list.cc | 587 ----------------------------- 1 file changed, 587 deletions(-) delete mode 100644 source/blender/editors/asset/asset_list.cc (limited to 'source/blender/editors/asset/asset_list.cc') diff --git a/source/blender/editors/asset/asset_list.cc b/source/blender/editors/asset/asset_list.cc deleted file mode 100644 index 9e611edd715..00000000000 --- a/source/blender/editors/asset/asset_list.cc +++ /dev/null @@ -1,587 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup edasset - * - * Abstractions to manage runtime asset lists with a global cache for multiple UI elements to - * access. - * Internally this uses the #FileList API and structures from `filelist.c`. This is just because it - * contains most necessary logic already and there's not much time for a more long-term solution. - */ - -#include -#include - -#include "BKE_context.h" - -#include "BLI_map.hh" -#include "BLI_path_util.h" -#include "BLI_utility_mixins.hh" - -#include "DNA_asset_types.h" -#include "DNA_space_types.h" - -#include "BKE_preferences.h" - -#include "ED_asset.h" -#include "ED_fileselect.h" - -#include "WM_api.h" -#include "WM_types.h" - -/* XXX uses private header of file-space. */ -#include "../space_file/filelist.h" - -#include "intern/asset_library_reference.hh" - -namespace blender::ed::asset { - -/* -------------------------------------------------------------------- */ -/** \name Asset list API - * - * Internally re-uses #FileList from the File Browser. It does all the heavy lifting already. - * \{ */ - -/** - * RAII wrapper for `FileList` - */ -class FileListWrapper { - static void filelist_free_fn(FileList *list) - { - filelist_free(list); - MEM_freeN(list); - } - - std::unique_ptr file_list_; - - public: - explicit FileListWrapper(eFileSelectType filesel_type) - : file_list_(filelist_new(filesel_type), filelist_free_fn) - { - } - FileListWrapper(FileListWrapper &&other) = default; - FileListWrapper &operator=(FileListWrapper &&other) = default; - ~FileListWrapper() - { - /* Destructs the owned pointer. */ - file_list_ = nullptr; - } - - operator FileList *() const - { - return file_list_.get(); - } -}; - -class PreviewTimer { - /* Non-owning! The Window-Manager registers and owns this. */ - wmTimer *timer_ = nullptr; - - public: - void ensureRunning(const bContext *C) - { - if (!timer_) { - timer_ = WM_event_add_timer_notifier( - CTX_wm_manager(C), CTX_wm_window(C), NC_ASSET | ND_ASSET_LIST_PREVIEW, 0.01); - } - } - - void stop(const bContext *C) - { - if (timer_) { - WM_event_remove_timer_notifier(CTX_wm_manager(C), CTX_wm_window(C), timer_); - timer_ = nullptr; - } - } -}; - -class AssetList : NonCopyable { - FileListWrapper filelist_; - AssetLibraryReference library_ref_; - PreviewTimer previews_timer_; - - public: - AssetList() = delete; - AssetList(eFileSelectType filesel_type, const AssetLibraryReference &asset_library_ref); - AssetList(AssetList &&other) = default; - ~AssetList() = default; - - void setup(const AssetFilterSettings *filter_settings = nullptr); - void fetch(const bContext &C); - void ensurePreviewsJob(bContext *C); - void clear(bContext *C); - - bool needsRefetch() const; - void iterate(AssetListIterFn fn) const; - bool listen(const wmNotifier ¬ifier) const; - int size() const; - void tagMainDataDirty() const; - void remapID(ID *id_old, ID *id_new) const; - StringRef filepath() const; -}; - -AssetList::AssetList(eFileSelectType filesel_type, const AssetLibraryReference &asset_library_ref) - : filelist_(filesel_type), library_ref_(asset_library_ref) -{ -} - -void AssetList::setup(const AssetFilterSettings *filter_settings) -{ - FileList *files = filelist_; - - bUserAssetLibrary *user_library = nullptr; - - /* Ensure valid repository, or fall-back to local one. */ - if (library_ref_.type == ASSET_LIBRARY_CUSTOM) { - BLI_assert(library_ref_.custom_library_index >= 0); - - user_library = BKE_preferences_asset_library_find_from_index( - &U, library_ref_.custom_library_index); - } - - /* Relevant bits from file_refresh(). */ - /* TODO pass options properly. */ - filelist_setrecursion(files, 1); - filelist_setsorting(files, FILE_SORT_ALPHA, false); - filelist_setlibrary(files, &library_ref_); - /* TODO different filtering settings require the list to be reread. That's a no-go for when we - * want to allow showing the same asset library with different filter settings (as in, - * different ID types). The filelist needs to be made smarter somehow, maybe goes together with - * the plan to separate the view (preview caching, filtering, etc. ) from the data. */ - filelist_setfilter_options( - files, - filter_settings != nullptr, - true, - true, /* Just always hide parent, prefer to not add an extra user option for this. */ - FILE_TYPE_BLENDERLIB, - filter_settings ? filter_settings->id_types : FILTER_ID_ALL, - true, - "", - ""); - - char path[FILE_MAXDIR] = ""; - if (user_library) { - BLI_strncpy(path, user_library->path, sizeof(path)); - filelist_setdir(files, path); - } - else { - filelist_setdir(files, path); - } -} - -void AssetList::fetch(const bContext &C) -{ - FileList *files = filelist_; - - if (filelist_needs_force_reset(files)) { - filelist_readjob_stop(files, CTX_wm_manager(&C)); - filelist_clear(files); - } - - if (filelist_needs_reading(files)) { - if (!filelist_pending(files)) { - filelist_readjob_start(files, NC_ASSET | ND_ASSET_LIST_READING, &C); - } - } - filelist_sort(files); - filelist_filter(files); -} - -bool AssetList::needsRefetch() const -{ - return filelist_needs_force_reset(filelist_) || filelist_needs_reading(filelist_); -} - -void AssetList::iterate(AssetListIterFn fn) const -{ - FileList *files = filelist_; - int numfiles = filelist_files_ensure(files); - - for (int i = 0; i < numfiles; i++) { - FileDirEntry *file = filelist_file(files, i); - if (!fn(*file)) { - break; - } - } -} - -void AssetList::ensurePreviewsJob(bContext *C) -{ - FileList *files = filelist_; - int numfiles = filelist_files_ensure(files); - - filelist_cache_previews_set(files, true); - filelist_file_cache_slidingwindow_set(files, 256); - /* TODO fetch all previews for now. */ - filelist_file_cache_block(files, numfiles / 2); - filelist_cache_previews_update(files); - - { - const bool previews_running = filelist_cache_previews_running(files) && - !filelist_cache_previews_done(files); - if (previews_running) { - previews_timer_.ensureRunning(C); - } - else { - /* Preview is not running, no need to keep generating update events! */ - previews_timer_.stop(C); - } - } -} - -void AssetList::clear(bContext *C) -{ - /* Based on #ED_fileselect_clear() */ - - FileList *files = filelist_; - filelist_readjob_stop(files, CTX_wm_manager(C)); - filelist_freelib(files); - filelist_clear(files); - - WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST, nullptr); -} - -/** - * \return True if the asset-list needs a UI redraw. - */ -bool AssetList::listen(const wmNotifier ¬ifier) const -{ - switch (notifier.category) { - case NC_ID: { - if (ELEM(notifier.action, NA_RENAME)) { - return true; - } - break; - } - case NC_ASSET: - if (ELEM(notifier.data, ND_ASSET_LIST, ND_ASSET_LIST_READING, ND_ASSET_LIST_PREVIEW)) { - return true; - } - if (ELEM(notifier.action, NA_ADDED, NA_REMOVED, NA_EDITED)) { - return true; - } - break; - } - - return false; -} - -/** - * \return The number of assets in the list. - */ -int AssetList::size() const -{ - return filelist_files_ensure(filelist_); -} - -void AssetList::tagMainDataDirty() const -{ - if (filelist_needs_reset_on_main_changes(filelist_)) { - /* Full refresh of the file list if local asset data was changed. Refreshing this view - * is cheap and users expect this to be updated immediately. */ - filelist_tag_force_reset(filelist_); - } -} - -void AssetList::remapID(ID * /*id_old*/, ID * /*id_new*/) const -{ - /* Trigger full re-fetch of the file list if main data was changed, don't even attempt remap - * pointers. We could give file list types a id-remap callback, but it's probably not worth it. - * Refreshing local file lists is relatively cheap. */ - tagMainDataDirty(); -} - -StringRef AssetList::filepath() const -{ - return filelist_dir(filelist_); -} -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Runtime asset list cache - * \{ */ - -/** - * Class managing a global asset list map, each entry being a list for a specific asset library. - */ -class AssetListStorage { - using AssetListMap = Map; - - public: - /* Purely static class, can't instantiate this. */ - AssetListStorage() = delete; - - static void fetch_library(const AssetLibraryReference &library_reference, - const bContext &C, - const AssetFilterSettings *filter_settings = nullptr); - static void destruct(); - static AssetList *lookup_list(const AssetLibraryReference &library_ref); - static void tagMainDataDirty(); - static void remapID(ID *id_new, ID *id_old); - - private: - static std::optional asset_library_reference_to_fileselect_type( - const AssetLibraryReference &library_reference); - - using is_new_t = bool; - static std::tuple ensure_list_storage( - const AssetLibraryReference &library_reference, eFileSelectType filesel_type); - - static AssetListMap &global_storage(); -}; - -void AssetListStorage::fetch_library(const AssetLibraryReference &library_reference, - const bContext &C, - const AssetFilterSettings *filter_settings) -{ - std::optional filesel_type = asset_library_reference_to_fileselect_type(library_reference); - if (!filesel_type) { - return; - } - - auto [list, is_new] = ensure_list_storage(library_reference, *filesel_type); - if (is_new || list.needsRefetch()) { - list.setup(filter_settings); - list.fetch(C); - } -} - -void AssetListStorage::destruct() -{ - global_storage().~AssetListMap(); -} - -AssetList *AssetListStorage::lookup_list(const AssetLibraryReference &library_ref) -{ - return global_storage().lookup_ptr(library_ref); -} - -void AssetListStorage::tagMainDataDirty() -{ - for (AssetList &list : global_storage().values()) { - list.tagMainDataDirty(); - } -} - -void AssetListStorage::remapID(ID *id_new, ID *id_old) -{ - for (AssetList &list : global_storage().values()) { - list.remapID(id_new, id_old); - } -} - -std::optional AssetListStorage::asset_library_reference_to_fileselect_type( - const AssetLibraryReference &library_reference) -{ - switch (library_reference.type) { - case ASSET_LIBRARY_CUSTOM: - return FILE_LOADLIB; - case ASSET_LIBRARY_LOCAL: - return FILE_MAIN_ASSET; - } - - return std::nullopt; -} - -std::tuple AssetListStorage::ensure_list_storage( - const AssetLibraryReference &library_reference, eFileSelectType filesel_type) -{ - AssetListMap &storage = global_storage(); - - if (AssetList *list = storage.lookup_ptr(library_reference)) { - return {*list, false}; - } - storage.add(library_reference, AssetList(filesel_type, library_reference)); - return {storage.lookup(library_reference), true}; -} - -/** - * Wrapper for Construct on First Use idiom, to avoid the Static Initialization Fiasco. - */ -AssetListStorage::AssetListMap &AssetListStorage::global_storage() -{ - static AssetListMap global_storage_; - return global_storage_; -} - -/** \} */ - -} // namespace blender::ed::asset - -/* -------------------------------------------------------------------- */ -/** \name C-API - * \{ */ - -using namespace blender::ed::asset; - -/** - * Invoke asset list reading, potentially in a parallel job. Won't wait until the job is done, - * and may return earlier. - */ -void ED_assetlist_storage_fetch(const AssetLibraryReference *library_reference, - const AssetFilterSettings *filter_settings, - const bContext *C) -{ - AssetListStorage::fetch_library(*library_reference, *C, filter_settings); -} - -void ED_assetlist_ensure_previews_job(const AssetLibraryReference *library_reference, bContext *C) -{ - - AssetList *list = AssetListStorage::lookup_list(*library_reference); - if (list) { - list->ensurePreviewsJob(C); - } -} - -void ED_assetlist_clear(const AssetLibraryReference *library_reference, bContext *C) -{ - AssetList *list = AssetListStorage::lookup_list(*library_reference); - if (list) { - list->clear(C); - } -} - -bool ED_assetlist_storage_has_list_for_library(const AssetLibraryReference *library_reference) -{ - return AssetListStorage::lookup_list(*library_reference) != nullptr; -} - -void ED_assetlist_iterate(const AssetLibraryReference *library_reference, AssetListIterFn fn) -{ - AssetList *list = AssetListStorage::lookup_list(*library_reference); - if (list) { - list->iterate(fn); - } -} - -/* TODO hack to use the File Browser path, so we can keep all the import logic handled by the asset - * API. Get rid of this once the File Browser is integrated better with the asset list. */ -static const char *assetlist_library_path_from_sfile_get_hack(const bContext *C) -{ - SpaceFile *sfile = CTX_wm_space_file(C); - if (!sfile || !ED_fileselect_is_asset_browser(sfile)) { - return nullptr; - } - - FileAssetSelectParams *asset_select_params = ED_fileselect_get_asset_params(sfile); - if (!asset_select_params) { - return nullptr; - } - - return filelist_dir(sfile->files); -} - -std::string ED_assetlist_asset_filepath_get(const bContext *C, - const AssetLibraryReference &library_reference, - const AssetHandle &asset_handle) -{ - if (ED_asset_handle_get_local_id(&asset_handle) || - !ED_asset_handle_get_metadata(&asset_handle)) { - return {}; - } - const char *library_path = ED_assetlist_library_path(&library_reference); - if (!library_path && C) { - library_path = assetlist_library_path_from_sfile_get_hack(C); - } - if (!library_path) { - return {}; - } - const char *asset_relpath = asset_handle.file_data->relpath; - - char path[FILE_MAX_LIBEXTRA]; - BLI_join_dirfile(path, sizeof(path), library_path, asset_relpath); - - return path; -} - -ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle) -{ - ImBuf *imbuf = filelist_file_getimage(asset_handle->file_data); - if (imbuf) { - return imbuf; - } - - return filelist_geticon_image_ex(asset_handle->file_data); -} - -const char *ED_assetlist_library_path(const AssetLibraryReference *library_reference) -{ - AssetList *list = AssetListStorage::lookup_list(*library_reference); - if (list) { - return list->filepath().data(); - } - return nullptr; -} - -/** - * \return True if the region needs a UI redraw. - */ -bool ED_assetlist_listen(const AssetLibraryReference *library_reference, - const wmNotifier *notifier) -{ - AssetList *list = AssetListStorage::lookup_list(*library_reference); - if (list) { - return list->listen(*notifier); - } - return false; -} - -/** - * \return The number of assets stored in the asset list for \a library_reference, or -1 if there - * is no list fetched for it. - */ -int ED_assetlist_size(const AssetLibraryReference *library_reference) -{ - AssetList *list = AssetListStorage::lookup_list(*library_reference); - if (list) { - return list->size(); - } - return -1; -} - -/** - * Tag all asset lists in the storage that show main data as needing an update (re-fetch). - * - * This only tags the data. If the asset list is visible on screen, the space is still responsible - * for ensuring the necessary redraw. It can use #ED_assetlist_listen() to check if the asset-list - * needs a redraw for a given notifier. - */ -void ED_assetlist_storage_tag_main_data_dirty() -{ - AssetListStorage::tagMainDataDirty(); -} - -/** - * Remapping of ID pointers within the asset lists. Typically called when an ID is deleted to clear - * all references to it (\a id_new is null then). - */ -void ED_assetlist_storage_id_remap(ID *id_old, ID *id_new) -{ - AssetListStorage::remapID(id_old, id_new); -} - -/** - * Can't wait for static deallocation to run. There's nested data allocated with our guarded - * allocator, it will complain about unfreed memory on exit. - */ -void ED_assetlist_storage_exit() -{ - AssetListStorage::destruct(); -} - -/** \} */ -- cgit v1.2.3