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/CMakeLists.txt | 15 +- source/blender/editors/asset/ED_asset_handle.h | 45 ++ source/blender/editors/asset/ED_asset_library.h | 35 ++ source/blender/editors/asset/ED_asset_list.h | 54 ++ source/blender/editors/asset/ED_asset_list.hh | 38 ++ source/blender/editors/asset/ED_asset_mark_clear.h | 37 ++ .../editors/asset/ED_asset_temp_id_consumer.h | 48 ++ source/blender/editors/asset/asset_edit.cc | 82 --- source/blender/editors/asset/asset_list.cc | 587 -------------------- source/blender/editors/asset/asset_ops.cc | 292 ---------- .../editors/asset/asset_temp_id_consumer.cc | 113 ---- .../blender/editors/asset/intern/asset_handle.cc | 5 +- .../asset/intern/asset_library_reference_enum.cc | 6 +- source/blender/editors/asset/intern/asset_list.cc | 589 +++++++++++++++++++++ .../editors/asset/intern/asset_mark_clear.cc | 83 +++ source/blender/editors/asset/intern/asset_ops.cc | 292 ++++++++++ .../editors/asset/intern/asset_temp_id_consumer.cc | 116 ++++ source/blender/editors/include/ED_asset.h | 81 +-- .../editors/interface/interface_context_menu.c | 2 +- 19 files changed, 1369 insertions(+), 1151 deletions(-) create mode 100644 source/blender/editors/asset/ED_asset_handle.h create mode 100644 source/blender/editors/asset/ED_asset_library.h create mode 100644 source/blender/editors/asset/ED_asset_list.h create mode 100644 source/blender/editors/asset/ED_asset_list.hh create mode 100644 source/blender/editors/asset/ED_asset_mark_clear.h create mode 100644 source/blender/editors/asset/ED_asset_temp_id_consumer.h delete mode 100644 source/blender/editors/asset/asset_edit.cc delete mode 100644 source/blender/editors/asset/asset_list.cc delete mode 100644 source/blender/editors/asset/asset_ops.cc delete mode 100644 source/blender/editors/asset/asset_temp_id_consumer.cc create mode 100644 source/blender/editors/asset/intern/asset_list.cc create mode 100644 source/blender/editors/asset/intern/asset_mark_clear.cc create mode 100644 source/blender/editors/asset/intern/asset_ops.cc create mode 100644 source/blender/editors/asset/intern/asset_temp_id_consumer.cc (limited to 'source/blender') diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt index 64024eea986..d56edd43d7d 100644 --- a/source/blender/editors/asset/CMakeLists.txt +++ b/source/blender/editors/asset/CMakeLists.txt @@ -16,6 +16,7 @@ # ***** END GPL LICENSE BLOCK ***** set(INC + . ../include ../../blenkernel ../../blenlib @@ -30,14 +31,20 @@ set(INC_SYS ) set(SRC - asset_edit.cc - asset_list.cc - asset_ops.cc - asset_temp_id_consumer.cc intern/asset_handle.cc intern/asset_library_reference.cc intern/asset_library_reference_enum.cc + intern/asset_list.cc + intern/asset_mark_clear.cc + intern/asset_ops.cc + intern/asset_temp_id_consumer.cc + ED_asset_handle.h + ED_asset_library.h + ED_asset_list.h + ED_asset_list.hh + ED_asset_mark_clear.h + ED_asset_temp_id_consumer.h intern/asset_library_reference.hh ) diff --git a/source/blender/editors/asset/ED_asset_handle.h b/source/blender/editors/asset/ED_asset_handle.h new file mode 100644 index 00000000000..c51ce422c25 --- /dev/null +++ b/source/blender/editors/asset/ED_asset_handle.h @@ -0,0 +1,45 @@ +/* + * 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 + */ + +#pragma once + +#include "DNA_ID_enums.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct AssetHandle; +struct AssetLibraryReference; +struct bContext; + +const char *ED_asset_handle_get_name(const struct AssetHandle *asset); +struct AssetMetaData *ED_asset_handle_get_metadata(const struct AssetHandle *asset); +struct ID *ED_asset_handle_get_local_id(const struct AssetHandle *asset); +ID_Type ED_asset_handle_get_id_type(const struct AssetHandle *asset); +int ED_asset_handle_get_preview_icon_id(const struct AssetHandle *asset); +void ED_asset_handle_get_full_library_path(const struct bContext *C, + const struct AssetLibraryReference *asset_library, + const struct AssetHandle *asset, + char r_full_lib_path[]); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/asset/ED_asset_library.h b/source/blender/editors/asset/ED_asset_library.h new file mode 100644 index 00000000000..905d097d223 --- /dev/null +++ b/source/blender/editors/asset/ED_asset_library.h @@ -0,0 +1,35 @@ +/* + * 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 + */ + +#pragma once + +#include "DNA_asset_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int ED_asset_library_reference_to_enum_value(const AssetLibraryReference *library); +AssetLibraryReference ED_asset_library_reference_from_enum_value(int value); +const struct EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(void); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/asset/ED_asset_list.h b/source/blender/editors/asset/ED_asset_list.h new file mode 100644 index 00000000000..1e7f0f0de55 --- /dev/null +++ b/source/blender/editors/asset/ED_asset_list.h @@ -0,0 +1,54 @@ +/* + * 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 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct AssetFilterSettings; +struct AssetHandle; +struct AssetLibraryReference; +struct bContext; +struct ID; +struct wmNotifier; + +void ED_assetlist_storage_fetch(const struct AssetLibraryReference *library_reference, + const struct AssetFilterSettings *filter_settings, + const struct bContext *C); +void ED_assetlist_ensure_previews_job(const struct AssetLibraryReference *library_reference, + struct bContext *C); +void ED_assetlist_clear(const struct AssetLibraryReference *library_reference, struct bContext *C); +bool ED_assetlist_storage_has_list_for_library(const AssetLibraryReference *library_reference); +void ED_assetlist_storage_tag_main_data_dirty(void); +void ED_assetlist_storage_id_remap(struct ID *id_old, struct ID *id_new); +void ED_assetlist_storage_exit(void); + +struct ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle); +const char *ED_assetlist_library_path(const struct AssetLibraryReference *library_reference); + +bool ED_assetlist_listen(const struct AssetLibraryReference *library_reference, + const struct wmNotifier *notifier); +int ED_assetlist_size(const struct AssetLibraryReference *library_reference); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/asset/ED_asset_list.hh b/source/blender/editors/asset/ED_asset_list.hh new file mode 100644 index 00000000000..7f41fba3457 --- /dev/null +++ b/source/blender/editors/asset/ED_asset_list.hh @@ -0,0 +1,38 @@ +/* + * 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 + */ + +#pragma once + +#include + +#include "BLI_function_ref.hh" + +struct AssetLibraryReference; +struct AssetHandle; +struct bContext; +struct FileDirEntry; + +std::string ED_assetlist_asset_filepath_get(const bContext *C, + const AssetLibraryReference &library_reference, + const AssetHandle &asset_handle); + +/* Can return false to stop iterating. */ +using AssetListIterFn = blender::FunctionRef; +void ED_assetlist_iterate(const AssetLibraryReference *library_reference, AssetListIterFn fn); diff --git a/source/blender/editors/asset/ED_asset_mark_clear.h b/source/blender/editors/asset/ED_asset_mark_clear.h new file mode 100644 index 00000000000..cdd1f0d080b --- /dev/null +++ b/source/blender/editors/asset/ED_asset_mark_clear.h @@ -0,0 +1,37 @@ +/* + * 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 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct bContext; +struct ID; + +bool ED_asset_mark_id(const struct bContext *C, struct ID *id); +bool ED_asset_clear_id(struct ID *id); + +bool ED_asset_can_mark_single_from_context(const struct bContext *C); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/asset/ED_asset_temp_id_consumer.h b/source/blender/editors/asset/ED_asset_temp_id_consumer.h new file mode 100644 index 00000000000..8aa53f9ea3b --- /dev/null +++ b/source/blender/editors/asset/ED_asset_temp_id_consumer.h @@ -0,0 +1,48 @@ +/* + * 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 + */ + +#pragma once + +#include "DNA_ID_enums.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct AssetTempIDConsumer AssetTempIDConsumer; + +struct AssetHandle; +struct AssetLibraryReference; +struct bContext; +struct Main; +struct ReportList; + +AssetTempIDConsumer *ED_asset_temp_id_consumer_create(const struct AssetHandle *handle); +void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer); +struct ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer, + const struct bContext *C, + const struct AssetLibraryReference *asset_library, + ID_Type id_type, + struct Main *bmain, + struct ReportList *reports); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/asset/asset_edit.cc b/source/blender/editors/asset/asset_edit.cc deleted file mode 100644 index c55e7a95120..00000000000 --- a/source/blender/editors/asset/asset_edit.cc +++ /dev/null @@ -1,82 +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 - */ - -#include -#include - -#include "BKE_asset.h" -#include "BKE_context.h" -#include "BKE_lib_id.h" - -#include "BLO_readfile.h" - -#include "DNA_ID.h" -#include "DNA_asset_types.h" -#include "DNA_space_types.h" - -#include "UI_interface_icons.h" - -#include "RNA_access.h" - -#include "ED_asset.h" - -using namespace blender; - -bool ED_asset_mark_id(const bContext *C, ID *id) -{ - if (id->asset_data) { - return false; - } - if (!BKE_id_can_be_asset(id)) { - return false; - } - - id_fake_user_set(id); - - id->asset_data = BKE_asset_metadata_create(); - - UI_icon_render_id(C, nullptr, id, ICON_SIZE_PREVIEW, true); - - /* Important for asset storage to update properly! */ - ED_assetlist_storage_tag_main_data_dirty(); - - return true; -} - -bool ED_asset_clear_id(ID *id) -{ - if (!id->asset_data) { - return false; - } - BKE_asset_metadata_free(&id->asset_data); - /* Don't clear fake user here, there's no guarantee that it was actually set by - * #ED_asset_mark_id(), it might have been something/someone else. */ - - /* Important for asset storage to update properly! */ - ED_assetlist_storage_tag_main_data_dirty(); - - return true; -} - -bool ED_asset_can_make_single_from_context(const bContext *C) -{ - /* Context needs a "id" pointer to be set for #ASSET_OT_mark()/#ASSET_OT_clear() to use. */ - return CTX_data_pointer_get_type_silent(C, "id", &RNA_ID).data != nullptr; -} 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(); -} - -/** \} */ diff --git a/source/blender/editors/asset/asset_ops.cc b/source/blender/editors/asset/asset_ops.cc deleted file mode 100644 index 79edd1f8a6a..00000000000 --- a/source/blender/editors/asset/asset_ops.cc +++ /dev/null @@ -1,292 +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 - */ - -#include "BKE_context.h" -#include "BKE_report.h" - -#include "BLI_vector.hh" - -#include "ED_asset.h" - -#include "RNA_access.h" - -#include "WM_api.h" -#include "WM_types.h" - -/* -------------------------------------------------------------------- */ - -using PointerRNAVec = blender::Vector; - -static bool asset_operation_poll(bContext * /*C*/) -{ - return U.experimental.use_asset_browser; -} - -/** - * Return the IDs to operate on as PointerRNA vector. Either a single one ("id" context member) or - * multiple ones ("selected_ids" context member). - */ -static PointerRNAVec asset_operation_get_ids_from_context(const bContext *C) -{ - PointerRNAVec ids; - - PointerRNA idptr = CTX_data_pointer_get_type(C, "id", &RNA_ID); - if (idptr.data) { - /* Single ID. */ - ids.append(idptr); - } - else { - ListBase list; - CTX_data_selected_ids(C, &list); - LISTBASE_FOREACH (CollectionPointerLink *, link, &list) { - ids.append(link->ptr); - } - BLI_freelistN(&list); - } - - return ids; -} - -/* -------------------------------------------------------------------- */ - -class AssetMarkHelper { - public: - void operator()(const bContext &C, PointerRNAVec &ids); - - void reportResults(ReportList &reports) const; - bool wasSuccessful() const; - - private: - struct Stats { - int tot_created = 0; - int tot_already_asset = 0; - ID *last_id = nullptr; - }; - - Stats stats; -}; - -void AssetMarkHelper::operator()(const bContext &C, PointerRNAVec &ids) -{ - for (PointerRNA &ptr : ids) { - BLI_assert(RNA_struct_is_ID(ptr.type)); - - ID *id = static_cast(ptr.data); - if (id->asset_data) { - stats.tot_already_asset++; - continue; - } - - if (ED_asset_mark_id(&C, id)) { - stats.last_id = id; - stats.tot_created++; - } - } -} - -bool AssetMarkHelper::wasSuccessful() const -{ - return stats.tot_created > 0; -} - -void AssetMarkHelper::reportResults(ReportList &reports) const -{ - /* User feedback on failure. */ - if (!wasSuccessful()) { - if ((stats.tot_already_asset > 0)) { - BKE_report(&reports, - RPT_ERROR, - "Selected data-blocks are already assets (or do not support use as assets)"); - } - else { - BKE_report(&reports, - RPT_ERROR, - "No data-blocks to create assets for found (or do not support use as assets)"); - } - } - /* User feedback on success. */ - else if (stats.tot_created == 1) { - /* If only one data-block: Give more useful message by printing asset name. */ - BKE_reportf(&reports, RPT_INFO, "Data-block '%s' is now an asset", stats.last_id->name + 2); - } - else { - BKE_reportf(&reports, RPT_INFO, "%i data-blocks are now assets", stats.tot_created); - } -} - -static int asset_mark_exec(bContext *C, wmOperator *op) -{ - PointerRNAVec ids = asset_operation_get_ids_from_context(C); - - AssetMarkHelper mark_helper; - mark_helper(*C, ids); - mark_helper.reportResults(*op->reports); - - if (!mark_helper.wasSuccessful()) { - return OPERATOR_CANCELLED; - } - - WM_main_add_notifier(NC_ID | NA_EDITED, nullptr); - WM_main_add_notifier(NC_ASSET | NA_ADDED, nullptr); - - return OPERATOR_FINISHED; -} - -static void ASSET_OT_mark(wmOperatorType *ot) -{ - ot->name = "Mark Asset"; - ot->description = - "Enable easier reuse of selected data-blocks through the Asset Browser, with the help of " - "customizable metadata (like previews, descriptions and tags)"; - ot->idname = "ASSET_OT_mark"; - - ot->exec = asset_mark_exec; - ot->poll = asset_operation_poll; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* -------------------------------------------------------------------- */ - -class AssetClearHelper { - public: - void operator()(PointerRNAVec &ids); - - void reportResults(ReportList &reports) const; - bool wasSuccessful() const; - - private: - struct Stats { - int tot_cleared = 0; - ID *last_id = nullptr; - }; - - Stats stats; -}; - -void AssetClearHelper::operator()(PointerRNAVec &ids) -{ - for (PointerRNA &ptr : ids) { - BLI_assert(RNA_struct_is_ID(ptr.type)); - - ID *id = static_cast(ptr.data); - if (!id->asset_data) { - continue; - } - - if (ED_asset_clear_id(id)) { - stats.tot_cleared++; - stats.last_id = id; - } - } -} - -void AssetClearHelper::reportResults(ReportList &reports) const -{ - if (!wasSuccessful()) { - BKE_report(&reports, RPT_ERROR, "No asset data-blocks selected/focused"); - } - else if (stats.tot_cleared == 1) { - /* If only one data-block: Give more useful message by printing asset name. */ - BKE_reportf( - &reports, RPT_INFO, "Data-block '%s' is no asset anymore", stats.last_id->name + 2); - } - else { - BKE_reportf(&reports, RPT_INFO, "%i data-blocks are no assets anymore", stats.tot_cleared); - } -} - -bool AssetClearHelper::wasSuccessful() const -{ - return stats.tot_cleared > 0; -} - -static int asset_clear_exec(bContext *C, wmOperator *op) -{ - PointerRNAVec ids = asset_operation_get_ids_from_context(C); - - AssetClearHelper clear_helper; - clear_helper(ids); - clear_helper.reportResults(*op->reports); - - if (!clear_helper.wasSuccessful()) { - return OPERATOR_CANCELLED; - } - - WM_main_add_notifier(NC_ID | NA_EDITED, nullptr); - WM_main_add_notifier(NC_ASSET | NA_REMOVED, nullptr); - - return OPERATOR_FINISHED; -} - -static void ASSET_OT_clear(wmOperatorType *ot) -{ - ot->name = "Clear Asset"; - ot->description = - "Delete all asset metadata and turn the selected asset data-blocks back into normal " - "data-blocks"; - ot->idname = "ASSET_OT_clear"; - - ot->exec = asset_clear_exec; - ot->poll = asset_operation_poll; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* -------------------------------------------------------------------- */ - -static bool asset_list_refresh_poll(bContext *C) -{ - const AssetLibraryReference *library = CTX_wm_asset_library(C); - if (!library) { - return false; - } - - return ED_assetlist_storage_has_list_for_library(library); -} - -static int asset_list_refresh_exec(bContext *C, wmOperator *UNUSED(unused)) -{ - const AssetLibraryReference *library = CTX_wm_asset_library(C); - ED_assetlist_clear(library, C); - return OPERATOR_FINISHED; -} - -static void ASSET_OT_list_refresh(struct wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Refresh Asset List"; - ot->description = "Trigger a reread of the assets"; - ot->idname = "ASSET_OT_list_refresh"; - - /* api callbacks */ - ot->exec = asset_list_refresh_exec; - ot->poll = asset_list_refresh_poll; -} - -/* -------------------------------------------------------------------- */ - -void ED_operatortypes_asset(void) -{ - WM_operatortype_append(ASSET_OT_mark); - WM_operatortype_append(ASSET_OT_clear); - - WM_operatortype_append(ASSET_OT_list_refresh); -} diff --git a/source/blender/editors/asset/asset_temp_id_consumer.cc b/source/blender/editors/asset/asset_temp_id_consumer.cc deleted file mode 100644 index 464320fdce9..00000000000 --- a/source/blender/editors/asset/asset_temp_id_consumer.cc +++ /dev/null @@ -1,113 +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 - * - * API for temporary loading of asset IDs. - * Uses the `BLO_library_temp_xxx()` API internally. - */ - -#include "DNA_asset_types.h" -#include "DNA_space_types.h" - -#include "BKE_report.h" - -#include "BLI_utility_mixins.hh" - -#include "BLO_readfile.h" - -#include "MEM_guardedalloc.h" - -#include "ED_asset.h" - -using namespace blender; - -class AssetTemporaryIDConsumer : NonCopyable, NonMovable { - const AssetHandle &handle_; - TempLibraryContext *temp_lib_context_ = nullptr; - - public: - AssetTemporaryIDConsumer(const AssetHandle &handle) : handle_(handle) - { - } - ~AssetTemporaryIDConsumer() - { - if (temp_lib_context_) { - BLO_library_temp_free(temp_lib_context_); - } - } - - ID *get_local_id() - { - return ED_asset_handle_get_local_id(&handle_); - } - - ID *import_id(const bContext *C, - const AssetLibraryReference &asset_library, - ID_Type id_type, - Main &bmain, - ReportList &reports) - { - const char *asset_name = ED_asset_handle_get_name(&handle_); - char blend_file_path[FILE_MAX_LIBEXTRA]; - ED_asset_handle_get_full_library_path(C, &asset_library, &handle_, blend_file_path); - - temp_lib_context_ = BLO_library_temp_load_id( - &bmain, blend_file_path, id_type, asset_name, &reports); - - if (temp_lib_context_ == nullptr || temp_lib_context_->temp_id == nullptr) { - BKE_reportf(&reports, RPT_ERROR, "Unable to load %s from %s", asset_name, blend_file_path); - return nullptr; - } - - BLI_assert(GS(temp_lib_context_->temp_id->name) == id_type); - return temp_lib_context_->temp_id; - } -}; - -AssetTempIDConsumer *ED_asset_temp_id_consumer_create(const AssetHandle *handle) -{ - if (!handle) { - return nullptr; - } - BLI_assert(handle->file_data->asset_data != nullptr); - return reinterpret_cast( - OBJECT_GUARDED_NEW(AssetTemporaryIDConsumer, *handle)); -} - -void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer) -{ - OBJECT_GUARDED_SAFE_DELETE(*consumer, AssetTemporaryIDConsumer); -} - -ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer_, - const bContext *C, - const AssetLibraryReference *asset_library, - ID_Type id_type, - Main *bmain, - ReportList *reports) -{ - if (!(consumer_ && asset_library && bmain && reports)) { - return nullptr; - } - AssetTemporaryIDConsumer *consumer = reinterpret_cast(consumer_); - - if (ID *local_id = consumer->get_local_id()) { - return local_id; - } - return consumer->import_id(C, *asset_library, id_type, *bmain, *reports); -} diff --git a/source/blender/editors/asset/intern/asset_handle.cc b/source/blender/editors/asset/intern/asset_handle.cc index 428fa62d130..aae85e61372 100644 --- a/source/blender/editors/asset/intern/asset_handle.cc +++ b/source/blender/editors/asset/intern/asset_handle.cc @@ -24,12 +24,15 @@ * although that doesn't always work (see #rna_def_asset_handle()). */ +#include + #include "DNA_asset_types.h" #include "DNA_space_types.h" #include "BLO_readfile.h" -#include "ED_asset.h" +#include "ED_asset_handle.h" +#include "ED_asset_list.hh" const char *ED_asset_handle_get_name(const AssetHandle *asset) { diff --git a/source/blender/editors/asset/intern/asset_library_reference_enum.cc b/source/blender/editors/asset/intern/asset_library_reference_enum.cc index f3c6ec1952c..13595ffa6ba 100644 --- a/source/blender/editors/asset/intern/asset_library_reference_enum.cc +++ b/source/blender/editors/asset/intern/asset_library_reference_enum.cc @@ -30,12 +30,12 @@ #include "DNA_asset_types.h" #include "DNA_userdef_types.h" -#include "ED_asset.h" - #include "UI_resources.h" #include "RNA_define.h" +#include "ED_asset_library.h" + /** * Return an index that can be used to uniquely identify \a library, assuming * that all relevant indices were created with this function. @@ -153,4 +153,4 @@ const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf() RNA_enum_item_end(&item, &totitem); return item; -} \ No newline at end of file +} diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc new file mode 100644 index 00000000000..445269d563e --- /dev/null +++ b/source/blender/editors/asset/intern/asset_list.cc @@ -0,0 +1,589 @@ +/* + * 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_fileselect.h" + +#include "WM_api.h" +#include "WM_types.h" + +/* XXX uses private header of file-space. */ +#include "../space_file/filelist.h" + +#include "ED_asset_handle.h" +#include "ED_asset_list.h" +#include "ED_asset_list.hh" +#include "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(); +} + +/** \} */ diff --git a/source/blender/editors/asset/intern/asset_mark_clear.cc b/source/blender/editors/asset/intern/asset_mark_clear.cc new file mode 100644 index 00000000000..ba348e38823 --- /dev/null +++ b/source/blender/editors/asset/intern/asset_mark_clear.cc @@ -0,0 +1,83 @@ +/* + * 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 + * + * Functions for marking and clearing assets. + */ + +#include +#include + +#include "BKE_asset.h" +#include "BKE_context.h" +#include "BKE_lib_id.h" + +#include "BLO_readfile.h" + +#include "DNA_ID.h" +#include "DNA_asset_types.h" +#include "DNA_space_types.h" + +#include "UI_interface_icons.h" + +#include "RNA_access.h" + +#include "ED_asset_list.h" +#include "ED_asset_mark_clear.h" + +bool ED_asset_mark_id(const bContext *C, ID *id) +{ + if (id->asset_data) { + return false; + } + if (!BKE_id_can_be_asset(id)) { + return false; + } + + id_fake_user_set(id); + + id->asset_data = BKE_asset_metadata_create(); + + UI_icon_render_id(C, nullptr, id, ICON_SIZE_PREVIEW, true); + + /* Important for asset storage to update properly! */ + ED_assetlist_storage_tag_main_data_dirty(); + + return true; +} + +bool ED_asset_clear_id(ID *id) +{ + if (!id->asset_data) { + return false; + } + BKE_asset_metadata_free(&id->asset_data); + /* Don't clear fake user here, there's no guarantee that it was actually set by + * #ED_asset_mark_id(), it might have been something/someone else. */ + + /* Important for asset storage to update properly! */ + ED_assetlist_storage_tag_main_data_dirty(); + + return true; +} + +bool ED_asset_can_mark_single_from_context(const bContext *C) +{ + /* Context needs a "id" pointer to be set for #ASSET_OT_mark()/#ASSET_OT_clear() to use. */ + return CTX_data_pointer_get_type_silent(C, "id", &RNA_ID).data != nullptr; +} diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc new file mode 100644 index 00000000000..79edd1f8a6a --- /dev/null +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -0,0 +1,292 @@ +/* + * 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 + */ + +#include "BKE_context.h" +#include "BKE_report.h" + +#include "BLI_vector.hh" + +#include "ED_asset.h" + +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" + +/* -------------------------------------------------------------------- */ + +using PointerRNAVec = blender::Vector; + +static bool asset_operation_poll(bContext * /*C*/) +{ + return U.experimental.use_asset_browser; +} + +/** + * Return the IDs to operate on as PointerRNA vector. Either a single one ("id" context member) or + * multiple ones ("selected_ids" context member). + */ +static PointerRNAVec asset_operation_get_ids_from_context(const bContext *C) +{ + PointerRNAVec ids; + + PointerRNA idptr = CTX_data_pointer_get_type(C, "id", &RNA_ID); + if (idptr.data) { + /* Single ID. */ + ids.append(idptr); + } + else { + ListBase list; + CTX_data_selected_ids(C, &list); + LISTBASE_FOREACH (CollectionPointerLink *, link, &list) { + ids.append(link->ptr); + } + BLI_freelistN(&list); + } + + return ids; +} + +/* -------------------------------------------------------------------- */ + +class AssetMarkHelper { + public: + void operator()(const bContext &C, PointerRNAVec &ids); + + void reportResults(ReportList &reports) const; + bool wasSuccessful() const; + + private: + struct Stats { + int tot_created = 0; + int tot_already_asset = 0; + ID *last_id = nullptr; + }; + + Stats stats; +}; + +void AssetMarkHelper::operator()(const bContext &C, PointerRNAVec &ids) +{ + for (PointerRNA &ptr : ids) { + BLI_assert(RNA_struct_is_ID(ptr.type)); + + ID *id = static_cast(ptr.data); + if (id->asset_data) { + stats.tot_already_asset++; + continue; + } + + if (ED_asset_mark_id(&C, id)) { + stats.last_id = id; + stats.tot_created++; + } + } +} + +bool AssetMarkHelper::wasSuccessful() const +{ + return stats.tot_created > 0; +} + +void AssetMarkHelper::reportResults(ReportList &reports) const +{ + /* User feedback on failure. */ + if (!wasSuccessful()) { + if ((stats.tot_already_asset > 0)) { + BKE_report(&reports, + RPT_ERROR, + "Selected data-blocks are already assets (or do not support use as assets)"); + } + else { + BKE_report(&reports, + RPT_ERROR, + "No data-blocks to create assets for found (or do not support use as assets)"); + } + } + /* User feedback on success. */ + else if (stats.tot_created == 1) { + /* If only one data-block: Give more useful message by printing asset name. */ + BKE_reportf(&reports, RPT_INFO, "Data-block '%s' is now an asset", stats.last_id->name + 2); + } + else { + BKE_reportf(&reports, RPT_INFO, "%i data-blocks are now assets", stats.tot_created); + } +} + +static int asset_mark_exec(bContext *C, wmOperator *op) +{ + PointerRNAVec ids = asset_operation_get_ids_from_context(C); + + AssetMarkHelper mark_helper; + mark_helper(*C, ids); + mark_helper.reportResults(*op->reports); + + if (!mark_helper.wasSuccessful()) { + return OPERATOR_CANCELLED; + } + + WM_main_add_notifier(NC_ID | NA_EDITED, nullptr); + WM_main_add_notifier(NC_ASSET | NA_ADDED, nullptr); + + return OPERATOR_FINISHED; +} + +static void ASSET_OT_mark(wmOperatorType *ot) +{ + ot->name = "Mark Asset"; + ot->description = + "Enable easier reuse of selected data-blocks through the Asset Browser, with the help of " + "customizable metadata (like previews, descriptions and tags)"; + ot->idname = "ASSET_OT_mark"; + + ot->exec = asset_mark_exec; + ot->poll = asset_operation_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* -------------------------------------------------------------------- */ + +class AssetClearHelper { + public: + void operator()(PointerRNAVec &ids); + + void reportResults(ReportList &reports) const; + bool wasSuccessful() const; + + private: + struct Stats { + int tot_cleared = 0; + ID *last_id = nullptr; + }; + + Stats stats; +}; + +void AssetClearHelper::operator()(PointerRNAVec &ids) +{ + for (PointerRNA &ptr : ids) { + BLI_assert(RNA_struct_is_ID(ptr.type)); + + ID *id = static_cast(ptr.data); + if (!id->asset_data) { + continue; + } + + if (ED_asset_clear_id(id)) { + stats.tot_cleared++; + stats.last_id = id; + } + } +} + +void AssetClearHelper::reportResults(ReportList &reports) const +{ + if (!wasSuccessful()) { + BKE_report(&reports, RPT_ERROR, "No asset data-blocks selected/focused"); + } + else if (stats.tot_cleared == 1) { + /* If only one data-block: Give more useful message by printing asset name. */ + BKE_reportf( + &reports, RPT_INFO, "Data-block '%s' is no asset anymore", stats.last_id->name + 2); + } + else { + BKE_reportf(&reports, RPT_INFO, "%i data-blocks are no assets anymore", stats.tot_cleared); + } +} + +bool AssetClearHelper::wasSuccessful() const +{ + return stats.tot_cleared > 0; +} + +static int asset_clear_exec(bContext *C, wmOperator *op) +{ + PointerRNAVec ids = asset_operation_get_ids_from_context(C); + + AssetClearHelper clear_helper; + clear_helper(ids); + clear_helper.reportResults(*op->reports); + + if (!clear_helper.wasSuccessful()) { + return OPERATOR_CANCELLED; + } + + WM_main_add_notifier(NC_ID | NA_EDITED, nullptr); + WM_main_add_notifier(NC_ASSET | NA_REMOVED, nullptr); + + return OPERATOR_FINISHED; +} + +static void ASSET_OT_clear(wmOperatorType *ot) +{ + ot->name = "Clear Asset"; + ot->description = + "Delete all asset metadata and turn the selected asset data-blocks back into normal " + "data-blocks"; + ot->idname = "ASSET_OT_clear"; + + ot->exec = asset_clear_exec; + ot->poll = asset_operation_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* -------------------------------------------------------------------- */ + +static bool asset_list_refresh_poll(bContext *C) +{ + const AssetLibraryReference *library = CTX_wm_asset_library(C); + if (!library) { + return false; + } + + return ED_assetlist_storage_has_list_for_library(library); +} + +static int asset_list_refresh_exec(bContext *C, wmOperator *UNUSED(unused)) +{ + const AssetLibraryReference *library = CTX_wm_asset_library(C); + ED_assetlist_clear(library, C); + return OPERATOR_FINISHED; +} + +static void ASSET_OT_list_refresh(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Refresh Asset List"; + ot->description = "Trigger a reread of the assets"; + ot->idname = "ASSET_OT_list_refresh"; + + /* api callbacks */ + ot->exec = asset_list_refresh_exec; + ot->poll = asset_list_refresh_poll; +} + +/* -------------------------------------------------------------------- */ + +void ED_operatortypes_asset(void) +{ + WM_operatortype_append(ASSET_OT_mark); + WM_operatortype_append(ASSET_OT_clear); + + WM_operatortype_append(ASSET_OT_list_refresh); +} diff --git a/source/blender/editors/asset/intern/asset_temp_id_consumer.cc b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc new file mode 100644 index 00000000000..bed35fdeeb5 --- /dev/null +++ b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc @@ -0,0 +1,116 @@ +/* + * 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 + * + * API for temporary loading of asset IDs. + * Uses the `BLO_library_temp_xxx()` API internally. + */ + +#include + +#include "DNA_asset_types.h" +#include "DNA_space_types.h" + +#include "BKE_report.h" + +#include "BLI_utility_mixins.hh" + +#include "BLO_readfile.h" + +#include "MEM_guardedalloc.h" + +#include "ED_asset_handle.h" +#include "ED_asset_temp_id_consumer.h" + +using namespace blender; + +class AssetTemporaryIDConsumer : NonCopyable, NonMovable { + const AssetHandle &handle_; + TempLibraryContext *temp_lib_context_ = nullptr; + + public: + AssetTemporaryIDConsumer(const AssetHandle &handle) : handle_(handle) + { + } + ~AssetTemporaryIDConsumer() + { + if (temp_lib_context_) { + BLO_library_temp_free(temp_lib_context_); + } + } + + ID *get_local_id() + { + return ED_asset_handle_get_local_id(&handle_); + } + + ID *import_id(const bContext *C, + const AssetLibraryReference &asset_library, + ID_Type id_type, + Main &bmain, + ReportList &reports) + { + const char *asset_name = ED_asset_handle_get_name(&handle_); + char blend_file_path[FILE_MAX_LIBEXTRA]; + ED_asset_handle_get_full_library_path(C, &asset_library, &handle_, blend_file_path); + + temp_lib_context_ = BLO_library_temp_load_id( + &bmain, blend_file_path, id_type, asset_name, &reports); + + if (temp_lib_context_ == nullptr || temp_lib_context_->temp_id == nullptr) { + BKE_reportf(&reports, RPT_ERROR, "Unable to load %s from %s", asset_name, blend_file_path); + return nullptr; + } + + BLI_assert(GS(temp_lib_context_->temp_id->name) == id_type); + return temp_lib_context_->temp_id; + } +}; + +AssetTempIDConsumer *ED_asset_temp_id_consumer_create(const AssetHandle *handle) +{ + if (!handle) { + return nullptr; + } + BLI_assert(handle->file_data->asset_data != nullptr); + return reinterpret_cast( + OBJECT_GUARDED_NEW(AssetTemporaryIDConsumer, *handle)); +} + +void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer) +{ + OBJECT_GUARDED_SAFE_DELETE(*consumer, AssetTemporaryIDConsumer); +} + +ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer_, + const bContext *C, + const AssetLibraryReference *asset_library, + ID_Type id_type, + Main *bmain, + ReportList *reports) +{ + if (!(consumer_ && asset_library && bmain && reports)) { + return nullptr; + } + AssetTemporaryIDConsumer *consumer = reinterpret_cast(consumer_); + + if (ID *local_id = consumer->get_local_id()) { + return local_id; + } + return consumer->import_id(C, *asset_library, id_type, *bmain, *reports); +} diff --git a/source/blender/editors/include/ED_asset.h b/source/blender/editors/include/ED_asset.h index 40cc7f4194f..42faf716560 100644 --- a/source/blender/editors/include/ED_asset.h +++ b/source/blender/editors/include/ED_asset.h @@ -16,70 +16,19 @@ /** \file * \ingroup editors + * + * The public API for assets is defined in dedicated headers. This is a utility file that just + * includes all of these. */ #pragma once -#include "DNA_ID_enums.h" - #ifdef __cplusplus extern "C" { #endif -struct AssetFilterSettings; -struct AssetLibraryReference; -struct Main; -struct ReportList; -struct bContext; -struct wmNotifier; - -typedef struct AssetTempIDConsumer AssetTempIDConsumer; - -bool ED_asset_mark_id(const struct bContext *C, struct ID *id); -bool ED_asset_clear_id(struct ID *id); - -bool ED_asset_can_make_single_from_context(const struct bContext *C); - -int ED_asset_library_reference_to_enum_value(const struct AssetLibraryReference *library); -struct AssetLibraryReference ED_asset_library_reference_from_enum_value(int value); -const struct EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(void); - -const char *ED_asset_handle_get_name(const AssetHandle *asset); -AssetMetaData *ED_asset_handle_get_metadata(const AssetHandle *asset); -struct ID *ED_asset_handle_get_local_id(const AssetHandle *asset); -ID_Type ED_asset_handle_get_id_type(const AssetHandle *asset); -int ED_asset_handle_get_preview_icon_id(const AssetHandle *asset); -void ED_asset_handle_get_full_library_path(const struct bContext *C, - const AssetLibraryReference *asset_library, - const AssetHandle *asset, - char r_full_lib_path[]); - -AssetTempIDConsumer *ED_asset_temp_id_consumer_create(const AssetHandle *handle); -void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer); -struct ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer, - const struct bContext *C, - const AssetLibraryReference *asset_library, - ID_Type id_type, - struct Main *bmain, - struct ReportList *reports); - -void ED_assetlist_storage_fetch(const struct AssetLibraryReference *library_reference, - const struct AssetFilterSettings *filter_settings, - const struct bContext *C); -void ED_assetlist_ensure_previews_job(const struct AssetLibraryReference *library_reference, - struct bContext *C); -void ED_assetlist_clear(const struct AssetLibraryReference *library_reference, struct bContext *C); -bool ED_assetlist_storage_has_list_for_library(const AssetLibraryReference *library_reference); -void ED_assetlist_storage_tag_main_data_dirty(void); -void ED_assetlist_storage_id_remap(struct ID *id_old, struct ID *id_new); -void ED_assetlist_storage_exit(void); - -struct ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle); -const char *ED_assetlist_library_path(const struct AssetLibraryReference *library_reference); - -bool ED_assetlist_listen(const struct AssetLibraryReference *library_reference, - const struct wmNotifier *notifier); -int ED_assetlist_size(const struct AssetLibraryReference *library_reference); +/* Barely anything here. Just general editor level functions. Actual asset level code is in + * dedicated headers. */ void ED_operatortypes_asset(void); @@ -87,17 +36,13 @@ void ED_operatortypes_asset(void); } #endif -/* TODO move to C++ asset-list header? */ -#ifdef __cplusplus +#include "../asset/ED_asset_handle.h" +#include "../asset/ED_asset_library.h" +#include "../asset/ED_asset_list.h" +#include "../asset/ED_asset_mark_clear.h" +#include "../asset/ED_asset_temp_id_consumer.h" -# include - -std::string ED_assetlist_asset_filepath_get(const bContext *C, - const AssetLibraryReference &library_reference, - const AssetHandle &asset_handle); - -# include "BLI_function_ref.hh" -/* Can return false to stop iterating. */ -using AssetListIterFn = blender::FunctionRef; -void ED_assetlist_iterate(const AssetLibraryReference *library_reference, AssetListIterFn fn); +/* C++ only headers. */ +#ifdef __cplusplus +# include "../asset/ED_asset_list.hh" #endif diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 3049e2bd7b8..d917534895d 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -952,7 +952,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev } /* If the button represents an id, it can set the "id" context pointer. */ - if (U.experimental.use_asset_browser && ED_asset_can_make_single_from_context(C)) { + if (U.experimental.use_asset_browser && ED_asset_can_mark_single_from_context(C)) { ID *id = CTX_data_pointer_get_type(C, "id", &RNA_ID).data; /* Gray out items depending on if data-block is an asset. Preferably this could be done via -- cgit v1.2.3