From 33bcc4f43092c10d37d4120c860c3a222c05d9f7 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 22 Nov 2022 17:56:36 +0100 Subject: [PATCH 01/49] Initial "All" asset library loading support An "All" asset library can be selected in the Asset Browser and asset view templates now, and that will load all assets from all asset libraries. Preview loading, drag & drop and asset catalogs don't work yet. --- .../blender/asset_system/AS_asset_library.hh | 3 + .../asset_system/intern/asset_library.cc | 7 ++ .../intern/asset_library_service.cc | 40 ++++++++-- .../intern/asset_library_service.hh | 2 + .../asset_system/intern/asset_storage.hh | 2 + .../intern/asset_library_reference_enum.cc | 9 ++- .../editors/asset/intern/asset_list.cc | 26 +++---- source/blender/editors/space_file/file_draw.c | 1 + source/blender/editors/space_file/filelist.cc | 73 ++++++++++++++++--- source/blender/editors/space_file/filesel.c | 8 +- source/blender/makesdna/DNA_asset_defaults.h | 2 +- source/blender/makesdna/DNA_asset_types.h | 3 +- source/blender/makesdna/DNA_space_types.h | 3 + 13 files changed, 140 insertions(+), 39 deletions(-) diff --git a/source/blender/asset_system/AS_asset_library.hh b/source/blender/asset_system/AS_asset_library.hh index 1a558c4e322..ece3426731c 100644 --- a/source/blender/asset_system/AS_asset_library.hh +++ b/source/blender/asset_system/AS_asset_library.hh @@ -120,6 +120,9 @@ Vector all_valid_asset_library_refs(); blender::asset_system::AssetLibrary *AS_asset_library_load( const Main *bmain, const AssetLibraryReference &library_reference); +std::string AS_asset_library_root_path_from_library_ref( + const AssetLibraryReference &library_reference); + /** * Try to find an appropriate location for an asset library root from a file or directory path. * Does not check if \a input_path exists. diff --git a/source/blender/asset_system/intern/asset_library.cc b/source/blender/asset_system/intern/asset_library.cc index 45196a218aa..eebbc8db837 100644 --- a/source/blender/asset_system/intern/asset_library.cc +++ b/source/blender/asset_system/intern/asset_library.cc @@ -64,6 +64,12 @@ bool AS_asset_library_has_any_unsaved_catalogs() return service->has_any_unsaved_catalogs(); } +std::string AS_asset_library_root_path_from_library_ref( + const AssetLibraryReference &library_reference) +{ + return AssetLibraryService::root_path_from_library_ref(library_reference); +} + std::string AS_asset_library_find_suitable_root_path_from_path( const blender::StringRefNull input_path) { @@ -224,6 +230,7 @@ void AssetLibrary::refresh_catalog_simplename(struct AssetMetaData *asset_data) STRNCPY(asset_data->catalog_simple_name, catalog->simple_name.c_str()); } +/* TODO get rid of this. */ Vector all_valid_asset_library_refs() { Vector result; diff --git a/source/blender/asset_system/intern/asset_library_service.cc b/source/blender/asset_system/intern/asset_library_service.cc index 9bd2eecd468..c40e6059f66 100644 --- a/source/blender/asset_system/intern/asset_library_service.cc +++ b/source/blender/asset_system/intern/asset_library_service.cc @@ -68,12 +68,17 @@ AssetLibrary *AssetLibraryService::get_asset_library( return get_asset_library_on_disk(root_path); } - if (library_reference.type == ASSET_LIBRARY_CUSTOM) { - bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index( - &U, library_reference.custom_library_index); - if (user_library) { - return get_asset_library_on_disk(user_library->path); + /* TODO */ + if (library_reference.type == ASSET_LIBRARY_ALL) { + return get_asset_library_current_file(); + } + + if (library_reference.type == ASSET_LIBRARY_CUSTOM) { + std::string root_path = root_path_from_library_ref(library_reference); + + if (!root_path.empty()) { + return get_asset_library_on_disk(root_path); } } @@ -133,6 +138,31 @@ AssetLibrary *AssetLibraryService::get_asset_library_current_file() return lib; } +std::string AssetLibraryService::root_path_from_library_ref( + const AssetLibraryReference &library_reference) +{ + if (ELEM(library_reference.type, ASSET_LIBRARY_ALL, ASSET_LIBRARY_LOCAL)) { + return ""; + } + + const char *top_level_directory = nullptr; + + BLI_assert(library_reference.type == ASSET_LIBRARY_CUSTOM); + BLI_assert(library_reference.custom_library_index >= 0); + + bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index( + &U, library_reference.custom_library_index); + if (user_library) { + top_level_directory = user_library->path; + } + + if (!top_level_directory) { + return ""; + } + + return normalize_directory_path(top_level_directory); +} + void AssetLibraryService::allocate_service_instance() { instance_ = std::make_unique(); diff --git a/source/blender/asset_system/intern/asset_library_service.hh b/source/blender/asset_system/intern/asset_library_service.hh index 6045060e91e..c492b798fc3 100644 --- a/source/blender/asset_system/intern/asset_library_service.hh +++ b/source/blender/asset_system/intern/asset_library_service.hh @@ -54,6 +54,8 @@ class AssetLibraryService { /** Destroy the AssetLibraryService singleton. It will be reallocated by #get() if necessary. */ static void destroy(); + static std::string root_path_from_library_ref(const AssetLibraryReference &library_reference); + AssetLibrary *get_asset_library(const Main *bmain, const AssetLibraryReference &library_reference); diff --git a/source/blender/asset_system/intern/asset_storage.hh b/source/blender/asset_system/intern/asset_storage.hh index 9278b3f41d1..0814c258442 100644 --- a/source/blender/asset_system/intern/asset_storage.hh +++ b/source/blender/asset_system/intern/asset_storage.hh @@ -30,6 +30,8 @@ class AssetStorage { * faster lookups. Not possible until each asset is only represented once in the storage. */ StorageT local_id_assets_; + friend class AssetLibrary; + public: /** See #AssetLibrary::add_external_asset(). */ AssetRepresentation &add_external_asset(StringRef name, std::unique_ptr metadata); 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 773838a54cd..f88afdd8487 100644 --- a/source/blender/editors/asset/intern/asset_library_reference_enum.cc +++ b/source/blender/editors/asset/intern/asset_library_reference_enum.cc @@ -47,7 +47,7 @@ AssetLibraryReference ED_asset_library_reference_from_enum_value(int value) if (value < ASSET_LIBRARY_CUSTOM) { library.type = value; library.custom_library_index = -1; - BLI_assert(ELEM(value, ASSET_LIBRARY_LOCAL)); + BLI_assert(ELEM(value, ASSET_LIBRARY_ALL, ASSET_LIBRARY_LOCAL)); return library; } @@ -78,8 +78,11 @@ const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf( if (include_local_library) { const EnumPropertyItem predefined_items[] = { - /* For the future. */ - // {ASSET_REPO_BUNDLED, "BUNDLED", 0, "Bundled", "Show the default user assets"}, + {ASSET_LIBRARY_ALL, + "ALL", + ICON_NONE, + "All", + "Show assets from all of the listed asset libraries"}, {ASSET_LIBRARY_LOCAL, "LOCAL", ICON_CURRENT_FILE, diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc index bb72c5cc1bb..a14c5eea807 100644 --- a/source/blender/editors/asset/intern/asset_list.cc +++ b/source/blender/editors/asset/intern/asset_list.cc @@ -12,6 +12,8 @@ #include #include +#include "AS_asset_library.hh" + #include "BKE_context.h" #include "BLI_map.hh" @@ -130,16 +132,7 @@ AssetList::AssetList(eFileSelectType filesel_type, const AssetLibraryReference & void AssetList::setup() { 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); - } + std::string asset_lib_path = AS_asset_library_root_path_from_library_ref(library_ref_); /* Relevant bits from file_refresh(). */ /* TODO pass options properly. */ @@ -161,13 +154,10 @@ void AssetList::setup() filelist_setindexer(files, use_asset_indexer ? &file_indexer_asset : &file_indexer_noop); char path[FILE_MAXDIR] = ""; - if (user_library) { - BLI_strncpy(path, user_library->path, sizeof(path)); - filelist_setdir(files, path); - } - else { - filelist_setdir(files, path); + if (!asset_lib_path.empty()) { + BLI_strncpy(path, asset_lib_path.c_str(), sizeof(path)); } + filelist_setdir(files, path); } void AssetList::fetch(const bContext &C) @@ -376,7 +366,9 @@ void AssetListStorage::remapID(ID *id_new, ID *id_old) std::optional AssetListStorage::asset_library_reference_to_fileselect_type( const AssetLibraryReference &library_reference) { - switch (library_reference.type) { + switch (eAssetLibraryType(library_reference.type)) { + case ASSET_LIBRARY_ALL: + return FILE_ASSET_LIBRARY_ALL; case ASSET_LIBRARY_CUSTOM: return FILE_ASSET_LIBRARY; case ASSET_LIBRARY_LOCAL: diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index ed0132c6990..d21c5b4fa99 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -555,6 +555,7 @@ static void file_draw_preview(const SpaceFile *sfile, /* path is no more static, cannot give it directly to but... */ else if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS && (file->typeflag & FILE_TYPE_ASSET) != 0) { + /* TODO enable drag & drop support, get path from asset representation. */ char blend_path[FILE_MAX_LIBEXTRA]; if (BLO_library_path_explode(path, blend_path, NULL, NULL)) { diff --git a/source/blender/editors/space_file/filelist.cc b/source/blender/editors/space_file/filelist.cc index dc59bb1286d..b0ac3277961 100644 --- a/source/blender/editors/space_file/filelist.cc +++ b/source/blender/editors/space_file/filelist.cc @@ -320,6 +320,10 @@ static void filelist_readjob_main_assets(FileListReadJob *job_params, bool *stop, bool *do_update, float *progress); +static void filelist_readjob_all_asset_libraries(FileListReadJob *job_params, + bool *stop, + bool *do_update, + float *progress); /* helper, could probably go in BKE actually? */ static int groupname_to_code(const char *group); @@ -854,13 +858,16 @@ static bool is_filtered_lib_type(FileListInternEntry *file, const char *root, FileListFilter *filter) { - char path[FILE_MAX_LIBEXTRA], dir[FILE_MAX_LIBEXTRA], *group, *name; + if (root) { + char path[FILE_MAX_LIBEXTRA], dir[FILE_MAX_LIBEXTRA], *group, *name; - BLI_path_join(path, sizeof(path), root, file->relpath); + BLI_path_join(path, sizeof(path), root, file->relpath); - if (BLO_library_path_explode(path, dir, &group, &name)) { - return is_filtered_id_file_type(file, group, name, filter); + if (BLO_library_path_explode(path, dir, &group, &name)) { + return is_filtered_id_file_type(file, group, name, filter); + } } + return is_filtered_file_type(file, filter); } @@ -1359,11 +1366,10 @@ static bool filelist_checkdir_main(struct FileList *filelist, char *r_dir, const return filelist_checkdir_lib(filelist, r_dir, do_change); } -static bool filelist_checkdir_main_assets(struct FileList * /*filelist*/, - char * /*r_dir*/, - const bool /*do_change*/) +static bool filelist_checkdir_return_always_valid(struct FileList * /*filelist*/, + char * /*r_dir*/, + const bool /*do_change*/) { - /* Main is always valid. */ return true; } @@ -1625,6 +1631,7 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry BLI_thread_queue_push(cache->previews_done, preview); } else { + /* XXX */ if (entry->redirection_path) { BLI_strncpy(preview->filepath, entry->redirection_path, FILE_MAXDIR); } @@ -1766,12 +1773,19 @@ void filelist_settype(FileList *filelist, short type) filelist->tags |= FILELIST_TAGS_USES_MAIN_DATA; break; case FILE_MAIN_ASSET: - filelist->check_dir_fn = filelist_checkdir_main_assets; + filelist->check_dir_fn = filelist_checkdir_return_always_valid; filelist->read_job_fn = filelist_readjob_main_assets; filelist->prepare_filter_fn = prepare_filter_asset_library; filelist->filter_fn = is_filtered_main_assets; filelist->tags |= FILELIST_TAGS_USES_MAIN_DATA | FILELIST_TAGS_NO_THREADS; break; + case FILE_ASSET_LIBRARY_ALL: + filelist->check_dir_fn = filelist_checkdir_return_always_valid; + filelist->read_job_fn = filelist_readjob_all_asset_libraries; + filelist->prepare_filter_fn = prepare_filter_asset_library; + filelist->filter_fn = is_filtered_asset_library; + filelist->tags |= FILELIST_TAGS_USES_MAIN_DATA; + break; default: filelist->check_dir_fn = filelist_checkdir_dir; filelist->read_job_fn = filelist_readjob_dir; @@ -2852,6 +2866,9 @@ void filelist_entry_parent_select_set(FileList *filelist, bool filelist_islibrary(struct FileList *filelist, char *dir, char **r_group) { + if (filelist->asset_library) { + return true; + } return BLO_library_path_explode(filelist->filelist.root, dir, r_group, nullptr); } @@ -3746,6 +3763,10 @@ static void filelist_readjob_main_assets_add_items(FileListReadJob *job_params, */ static bool filelist_contains_main(const FileList *filelist, const Main *bmain) { + if (filelist->asset_library_ref && (filelist->asset_library_ref->type == ASSET_LIBRARY_ALL)) { + return true; + } + const char *blendfile_path = BKE_main_blendfile_path(bmain); return blendfile_path[0] && BLI_path_contains(filelist->filelist.root, blendfile_path); } @@ -3800,6 +3821,40 @@ static void filelist_readjob_main_assets(FileListReadJob *job_params, filelist_readjob_main_assets_add_items(job_params, stop, do_update, progress); } +static void filelist_readjob_all_asset_libraries(FileListReadJob *job_params, + bool *stop, + bool *do_update, + float *progress) +{ + FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ + BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && + (filelist->filelist.entries_num == FILEDIR_NBR_ENTRIES_UNSET)); + + filelist_readjob_load_asset_library_data(job_params, do_update); + + /* A valid, but empty file-list from now. */ + filelist->filelist.entries_num = 0; + + filelist_readjob_main_assets_add_items(job_params, stop, do_update, progress); + + /* When only doing partialy reload for main data, we're done. */ + if (job_params->only_main_data) { + return; + } + /* TODO propertly update progress? */ + + for (const AssetLibraryReference &library_ref : asset_system::all_valid_asset_library_refs()) { + if (library_ref.type == ASSET_LIBRARY_LOCAL) { + /* Already added main assets above. */ + continue; + } + std::string library_path = AS_asset_library_root_path_from_library_ref(library_ref); + BLI_strncpy(filelist->filelist.root, library_path.c_str(), sizeof(filelist->filelist.root)); + + filelist_readjob_recursive_dir_add_items(true, job_params, stop, do_update, progress); + } +} + /** * Check if the read-job is requesting a partial reread of the file list only. */ diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index af2c9d4e757..0b0ab2b0d66 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -423,16 +423,20 @@ static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params) } switch (library->type) { + case ASSET_LIBRARY_ALL: + base_params->dir[0] = '\0'; + base_params->type = FILE_ASSET_LIBRARY_ALL; + break; case ASSET_LIBRARY_LOCAL: base_params->dir[0] = '\0'; + base_params->type = FILE_MAIN_ASSET; break; case ASSET_LIBRARY_CUSTOM: BLI_assert(user_library); BLI_strncpy(base_params->dir, user_library->path, sizeof(base_params->dir)); + base_params->type = FILE_ASSET_LIBRARY; break; } - base_params->type = (library->type == ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : - FILE_ASSET_LIBRARY; } void fileselect_refresh_params(SpaceFile *sfile) diff --git a/source/blender/makesdna/DNA_asset_defaults.h b/source/blender/makesdna/DNA_asset_defaults.h index cef4c1270b8..63239f23e4e 100644 --- a/source/blender/makesdna/DNA_asset_defaults.h +++ b/source/blender/makesdna/DNA_asset_defaults.h @@ -21,7 +21,7 @@ #define _DNA_DEFAULT_AssetLibraryReference \ { \ .type = ASSET_LIBRARY_LOCAL, \ - /* Not needed really (should be ignored for #ASSET_LIBRARY_LOCAL), but helps debugging. */ \ + /* Not needed really (should be ignored for anything but #ASSET_LIBRARY_CUSTOM), but helps debugging. */ \ .custom_library_index = -1, \ } diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h index d7e3bcfc919..e492af6e048 100644 --- a/source/blender/makesdna/DNA_asset_types.h +++ b/source/blender/makesdna/DNA_asset_types.h @@ -88,8 +88,7 @@ typedef enum eAssetLibraryType { // ASSET_LIBRARY_BUNDLED = 0, /** Display assets from the current session (current "Main"). */ ASSET_LIBRARY_LOCAL = 1, - /* For the future. Display assets for the current project. */ - // ASSET_LIBRARY_PROJECT = 2, + ASSET_LIBRARY_ALL = 2, /** Display assets from custom asset libraries, as defined in the preferences * (#bUserAssetLibrary). The name will be taken from #FileSelectParams.asset_library_ref.idname diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 36c15b6e106..add85223437 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -995,12 +995,15 @@ enum eFileDetails { /** File selector types. */ typedef enum eFileSelectType { + FILE_SELECT_TYPE_UNSET = 0, FILE_LOADLIB = 1, FILE_MAIN = 2, /** Load assets from #Main. */ FILE_MAIN_ASSET = 3, /** Load assets of an asset library containing external files. */ FILE_ASSET_LIBRARY = 4, + /** Load all asset libraries. */ + FILE_ASSET_LIBRARY_ALL = 5, FILE_UNIX = 8, FILE_BLENDER = 8, /* don't display relative paths */ -- 2.30.2 From d51212c4f05e38851580f7e9e75d174695dc1b82 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 28 Nov 2022 19:37:00 +0100 Subject: [PATCH 02/49] Integrate "All" library better with the asset system Now it actually loads data from all asset libraries when this is selected. The asset representations still need to be loaded by the file browser backend, this won't change for now. This adds the concept of nested asset libraries, which I'd prefer to keep as implementation detail and not expose in the API. But for now it's needed (for the asset representation loading by the file browser backend). --- .../blender/asset_system/AS_asset_library.hh | 26 ++++- source/blender/asset_system/CMakeLists.txt | 2 + .../asset_system/intern/asset_library.cc | 27 ++++- .../intern/asset_library_service.cc | 108 ++++++++++-------- .../intern/asset_library_service.hh | 8 +- source/blender/asset_system/intern/utils.cc | 30 +++++ source/blender/asset_system/intern/utils.hh | 19 +++ source/blender/editors/space_file/filelist.cc | 37 +++--- 8 files changed, 183 insertions(+), 74 deletions(-) create mode 100644 source/blender/asset_system/intern/utils.cc create mode 100644 source/blender/asset_system/intern/utils.hh diff --git a/source/blender/asset_system/AS_asset_library.hh b/source/blender/asset_system/AS_asset_library.hh index ece3426731c..3bbed9603a5 100644 --- a/source/blender/asset_system/AS_asset_library.hh +++ b/source/blender/asset_system/AS_asset_library.hh @@ -34,7 +34,9 @@ class AssetStorage; * to also include asset indexes and more. */ class AssetLibrary { - bCallbackFuncStore on_save_callback_store_{}; + /** If this is an asset library on disk, the top-level directory path. Normalized using + * #normalize_directory_path().*/ + std::string root_path_; /** Storage for assets (better said their representations) that are considered to be part of this * library. Assets are not automatically loaded into this when loading an asset library. Assets @@ -51,6 +53,12 @@ class AssetLibrary { */ std::unique_ptr asset_storage_; + /** In some cases an asset library is a combination of multiple other ones, these are then + * referenced here. "All" asset library only currently. */ + Vector nested_libs_; /* Non-owning pointers. */ + + bCallbackFuncStore on_save_callback_store_{}; + public: /* Controlled by #ED_asset_catalogs_set_save_catalogs_when_file_is_saved, * for managing the "Save Catalog Changes" in the quit-confirmation dialog box. */ @@ -58,11 +66,16 @@ class AssetLibrary { std::unique_ptr catalog_service; + friend class AssetLibraryService; + public: - AssetLibrary(); + /** + * \param root_path: If this is an asset library on disk, the top-level directory path. + */ + AssetLibrary(StringRef root_path = ""); ~AssetLibrary(); - void load_catalogs(StringRefNull library_root_directory); + void load_catalogs(); /** Load catalogs that have changed on disk. */ void refresh(); @@ -85,6 +98,11 @@ class AssetLibrary { * case when the reference is dangling). */ bool remove_asset(AssetRepresentation &asset); + /** In some cases an asset library is a combination of multiple other ones ("All" asset library + * only currently). Iterate over the contained asset libraries, executing \a fn for each of them. + */ + void foreach_nested(FunctionRef fn); + /** * Remap ID pointers for local ID assets, see #BKE_lib_remap.h. When an ID pointer would be * mapped to null (typically when an ID gets removed), the asset is removed, because we don't @@ -105,6 +123,8 @@ class AssetLibrary { void on_blend_save_post(Main *bmain, PointerRNA **pointers, int num_pointers); + StringRefNull root_path() const; + private: std::optional find_asset_index(const AssetRepresentation &asset); }; diff --git a/source/blender/asset_system/CMakeLists.txt b/source/blender/asset_system/CMakeLists.txt index d00c3c72e3b..c9ef11d33f4 100644 --- a/source/blender/asset_system/CMakeLists.txt +++ b/source/blender/asset_system/CMakeLists.txt @@ -21,6 +21,7 @@ set(SRC intern/asset_library_service.cc intern/asset_representation.cc intern/asset_storage.cc + intern/utils.cc AS_asset_catalog.hh AS_asset_catalog_path.hh @@ -29,6 +30,7 @@ set(SRC AS_asset_representation.hh intern/asset_library_service.hh intern/asset_storage.hh + intern/utils.hh AS_asset_library.h AS_asset_representation.h diff --git a/source/blender/asset_system/intern/asset_library.cc b/source/blender/asset_system/intern/asset_library.cc index eebbc8db837..79c3c55029c 100644 --- a/source/blender/asset_system/intern/asset_library.cc +++ b/source/blender/asset_system/intern/asset_library.cc @@ -22,6 +22,7 @@ #include "asset_library_service.hh" #include "asset_storage.hh" +#include "utils.hh" using namespace blender; using namespace blender::asset_system; @@ -126,8 +127,9 @@ void AS_asset_library_remap_ids(const IDRemapper *mappings) namespace blender::asset_system { -AssetLibrary::AssetLibrary() - : asset_storage_(std::make_unique()), +AssetLibrary::AssetLibrary(StringRef root_path) + : root_path_(utils::normalize_directory_path(root_path)), + asset_storage_(std::make_unique()), catalog_service(std::make_unique()) { } @@ -139,9 +141,9 @@ AssetLibrary::~AssetLibrary() } } -void AssetLibrary::load_catalogs(StringRefNull library_root_directory) +void AssetLibrary::load_catalogs() { - auto catalog_service = std::make_unique(library_root_directory); + auto catalog_service = std::make_unique(root_path_); catalog_service->load_from_disk(); this->catalog_service = std::move(catalog_service); } @@ -167,6 +169,18 @@ bool AssetLibrary::remove_asset(AssetRepresentation &asset) return asset_storage_->remove_asset(asset); } +void AssetLibrary::foreach_nested(FunctionRef fn) +{ + for (AssetLibrary *library : nested_libs_) { + if (!library) { + BLI_assert_unreachable(); + continue; + } + + fn(*library); + } +} + void AssetLibrary::remap_ids_and_remove_invalid(const IDRemapper &mappings) { asset_storage_->remap_ids_and_remove_invalid(mappings); @@ -215,6 +229,11 @@ void AssetLibrary::on_blend_save_post(struct Main *main, } } +StringRefNull AssetLibrary::root_path() const +{ + return root_path_; +} + void AssetLibrary::refresh_catalog_simplename(struct AssetMetaData *asset_data) { if (BLI_uuid_is_nil(asset_data->catalog_id)) { diff --git a/source/blender/asset_system/intern/asset_library_service.cc b/source/blender/asset_system/intern/asset_library_service.cc index c40e6059f66..8a00ebe2a08 100644 --- a/source/blender/asset_system/intern/asset_library_service.cc +++ b/source/blender/asset_system/intern/asset_library_service.cc @@ -4,9 +4,6 @@ * \ingroup asset_system */ -#include "asset_library_service.hh" -#include "AS_asset_library.hh" - #include "BKE_blender.h" #include "BKE_preferences.h" @@ -19,6 +16,10 @@ #include "CLG_log.h" +#include "AS_asset_library.hh" +#include "asset_library_service.hh" +#include "utils.hh" + /* When enabled, use a pre file load handler (#BKE_CB_EVT_LOAD_PRE) callback to destroy the asset * library service. Without this an explicit call from the file loading code is needed to do this, * which is not as nice. @@ -57,69 +58,58 @@ void AssetLibraryService::destroy() AssetLibrary *AssetLibraryService::get_asset_library( const Main *bmain, const AssetLibraryReference &library_reference) { - if (library_reference.type == ASSET_LIBRARY_LOCAL) { - /* For the "Current File" library we get the asset library root path based on main. */ - std::string root_path = bmain ? AS_asset_library_find_suitable_root_path_from_main(bmain) : ""; + const eAssetLibraryType type = eAssetLibraryType(library_reference.type); - if (root_path.empty()) { - /* File wasn't saved yet. */ - return get_asset_library_current_file(); - } + switch (type) { + case ASSET_LIBRARY_LOCAL: { + /* For the "Current File" library we get the asset library root path based on main. */ + std::string root_path = bmain ? AS_asset_library_find_suitable_root_path_from_main(bmain) : + ""; - return get_asset_library_on_disk(root_path); - } - - /* TODO */ - if (library_reference.type == ASSET_LIBRARY_ALL) { - return get_asset_library_current_file(); - } - - if (library_reference.type == ASSET_LIBRARY_CUSTOM) { - std::string root_path = root_path_from_library_ref(library_reference); - - if (!root_path.empty()) { + if (root_path.empty()) { + /* File wasn't saved yet. */ + return get_asset_library_current_file(); + } return get_asset_library_on_disk(root_path); } + case ASSET_LIBRARY_ALL: + return get_asset_library_all(bmain); + case ASSET_LIBRARY_CUSTOM: { + std::string root_path = root_path_from_library_ref(library_reference); + + if (!root_path.empty()) { + return get_asset_library_on_disk(root_path); + } + } break; } return nullptr; } -namespace { -std::string normalize_directory_path(StringRefNull directory) +AssetLibrary *AssetLibraryService::get_asset_library_on_disk(StringRefNull root_path) { - - char dir_normalized[PATH_MAX]; - STRNCPY(dir_normalized, directory.c_str()); - BLI_path_normalize_dir(nullptr, dir_normalized, sizeof(dir_normalized)); - return std::string(dir_normalized); -} -} // namespace - -AssetLibrary *AssetLibraryService::get_asset_library_on_disk(StringRefNull top_level_directory) -{ - BLI_assert_msg(!top_level_directory.is_empty(), + BLI_assert_msg(!root_path.is_empty(), "top level directory must be given for on-disk asset library"); - std::string top_dir_trailing_slash = normalize_directory_path(top_level_directory); + std::string normalized_root_path = utils::normalize_directory_path(root_path); std::unique_ptr *lib_uptr_ptr = on_disk_libraries_.lookup_ptr( - top_dir_trailing_slash); + normalized_root_path); if (lib_uptr_ptr != nullptr) { - CLOG_INFO(&LOG, 2, "get \"%s\" (cached)", top_dir_trailing_slash.c_str()); + CLOG_INFO(&LOG, 2, "get \"%s\" (cached)", normalized_root_path.c_str()); AssetLibrary *lib = lib_uptr_ptr->get(); lib->refresh(); return lib; } - std::unique_ptr lib_uptr = std::make_unique(); + std::unique_ptr lib_uptr = std::make_unique(normalized_root_path); AssetLibrary *lib = lib_uptr.get(); lib->on_blend_save_handler_register(); - lib->load_catalogs(top_dir_trailing_slash); + lib->load_catalogs(); - on_disk_libraries_.add_new(top_dir_trailing_slash, std::move(lib_uptr)); - CLOG_INFO(&LOG, 2, "get \"%s\" (loaded)", top_dir_trailing_slash.c_str()); + on_disk_libraries_.add_new(normalized_root_path, std::move(lib_uptr)); + CLOG_INFO(&LOG, 2, "get \"%s\" (loaded)", normalized_root_path.c_str()); return lib; } @@ -138,6 +128,30 @@ AssetLibrary *AssetLibraryService::get_asset_library_current_file() return lib; } +AssetLibrary *AssetLibraryService::get_asset_library_all(const Main *bmain) +{ + if (all_library_) { + CLOG_INFO(&LOG, 2, "get all lib (cached)"); + all_library_->nested_libs_.clear(); + } + else { + CLOG_INFO(&LOG, 2, "get all lib (loaded)"); + all_library_ = std::make_unique(); + } + + for (AssetLibraryReference &library_ref : all_valid_asset_library_refs()) { + /* Skip self :) */ + if (library_ref.type == ASSET_LIBRARY_ALL) { + continue; + } + + AssetLibrary *nested_lib = get_asset_library(bmain, library_ref); + all_library_->nested_libs_.append(nested_lib); + } + + return all_library_.get(); +} + std::string AssetLibraryService::root_path_from_library_ref( const AssetLibraryReference &library_reference) { @@ -145,22 +159,16 @@ std::string AssetLibraryService::root_path_from_library_ref( return ""; } - const char *top_level_directory = nullptr; - BLI_assert(library_reference.type == ASSET_LIBRARY_CUSTOM); BLI_assert(library_reference.custom_library_index >= 0); bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index( &U, library_reference.custom_library_index); - if (user_library) { - top_level_directory = user_library->path; - } - - if (!top_level_directory) { + if (!user_library || !user_library->path[0]) { return ""; } - return normalize_directory_path(top_level_directory); + return user_library->path; } void AssetLibraryService::allocate_service_instance() diff --git a/source/blender/asset_system/intern/asset_library_service.hh b/source/blender/asset_system/intern/asset_library_service.hh index c492b798fc3..478465d25ed 100644 --- a/source/blender/asset_system/intern/asset_library_service.hh +++ b/source/blender/asset_system/intern/asset_library_service.hh @@ -33,12 +33,15 @@ namespace blender::asset_system { class AssetLibraryService { static std::unique_ptr instance_; - /* Mapping absolute path of the library's top-level directory to the AssetLibrary instance. */ + /* Mapping absolute path of the library's root path (normalize with #normalize_directory_path()!) + * the AssetLibrary instance. */ Map> on_disk_libraries_; /** Library without a known path, i.e. the "Current File" library if the file isn't saved yet. If * the file was saved, a valid path for the library can be determined and #on_disk_libraries_ * above should be used. */ std::unique_ptr current_file_library_; + /** The "all" asset library, merging all other libraries into one. */ + std::unique_ptr all_library_; /* Handlers for managing the life cycle of the AssetLibraryService instance. */ bCallbackFuncStore on_load_callback_store_; @@ -67,6 +70,9 @@ class AssetLibraryService { /** Get the "Current File" asset library. */ AssetLibrary *get_asset_library_current_file(); + /** Get the "All" asset library, merging all others into one. */ + AssetLibrary *get_asset_library_all(const Main *bmain); + /** Returns whether there are any known asset libraries with unsaved catalog edits. */ bool has_any_unsaved_catalogs() const; diff --git a/source/blender/asset_system/intern/utils.cc b/source/blender/asset_system/intern/utils.cc new file mode 100644 index 00000000000..b1e9793634d --- /dev/null +++ b/source/blender/asset_system/intern/utils.cc @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup asset_system + */ + +#include "BLI_fileops.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "utils.hh" + +namespace blender::asset_system::utils { + +std::string normalize_directory_path(StringRef directory) +{ + if (directory.is_empty()) { + return ""; + } + + char dir_normalized[PATH_MAX]; + BLI_strncpy(dir_normalized, + directory.data(), + /* + 1 for null terminator. */ + std::min(directory.size() + 1, int64_t(sizeof(dir_normalized)))); + BLI_path_normalize_dir(nullptr, dir_normalized, sizeof(dir_normalized)); + return std::string(dir_normalized); +} + +} // namespace blender::asset_system::utils diff --git a/source/blender/asset_system/intern/utils.hh b/source/blender/asset_system/intern/utils.hh new file mode 100644 index 00000000000..cccc24984ba --- /dev/null +++ b/source/blender/asset_system/intern/utils.hh @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup asset_system + */ + +#pragma once + +#include "BLI_string_ref.hh" + +namespace blender::asset_system::utils { + +/** + * Returns a normalized directory path with a trailing slash, and a maximum length of #PATH_MAX. + * Slashes are not converted to native format (they probably should be though?). + */ +std::string normalize_directory_path(StringRef directory); + +} // namespace blender::asset_system::utils diff --git a/source/blender/editors/space_file/filelist.cc b/source/blender/editors/space_file/filelist.cc index b0ac3277961..c4cce982896 100644 --- a/source/blender/editors/space_file/filelist.cc +++ b/source/blender/editors/space_file/filelist.cc @@ -320,10 +320,10 @@ static void filelist_readjob_main_assets(FileListReadJob *job_params, bool *stop, bool *do_update, float *progress); -static void filelist_readjob_all_asset_libraries(FileListReadJob *job_params, - bool *stop, - bool *do_update, - float *progress); +static void filelist_readjob_all_asset_library(FileListReadJob *job_params, + bool *stop, + bool *do_update, + float *progress); /* helper, could probably go in BKE actually? */ static int groupname_to_code(const char *group); @@ -1781,7 +1781,7 @@ void filelist_settype(FileList *filelist, short type) break; case FILE_ASSET_LIBRARY_ALL: filelist->check_dir_fn = filelist_checkdir_return_always_valid; - filelist->read_job_fn = filelist_readjob_all_asset_libraries; + filelist->read_job_fn = filelist_readjob_all_asset_library; filelist->prepare_filter_fn = prepare_filter_asset_library; filelist->filter_fn = is_filtered_asset_library; filelist->tags |= FILELIST_TAGS_USES_MAIN_DATA; @@ -3784,6 +3784,8 @@ static void filelist_readjob_asset_library(FileListReadJob *job_params, /* A valid, but empty file-list from now. */ filelist->filelist.entries_num = 0; + BLI_assert(job_params->filelist->asset_library_ref != nullptr); + /* NOP if already read. */ filelist_readjob_load_asset_library_data(job_params, do_update); @@ -3821,10 +3823,10 @@ static void filelist_readjob_main_assets(FileListReadJob *job_params, filelist_readjob_main_assets_add_items(job_params, stop, do_update, progress); } -static void filelist_readjob_all_asset_libraries(FileListReadJob *job_params, - bool *stop, - bool *do_update, - float *progress) +static void filelist_readjob_all_asset_library(FileListReadJob *job_params, + bool *stop, + bool *do_update, + float *progress) { FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && @@ -3843,16 +3845,19 @@ static void filelist_readjob_all_asset_libraries(FileListReadJob *job_params, } /* TODO propertly update progress? */ - for (const AssetLibraryReference &library_ref : asset_system::all_valid_asset_library_refs()) { - if (library_ref.type == ASSET_LIBRARY_LOCAL) { - /* Already added main assets above. */ - continue; + BLI_assert(filelist->asset_library != nullptr); + + /* Add assets from asset libraries on disk. */ + filelist->asset_library->foreach_nested([&](asset_system::AssetLibrary &nested_library) { + StringRefNull root_path = nested_library.root_path(); + if (root_path.is_empty()) { + return; } - std::string library_path = AS_asset_library_root_path_from_library_ref(library_ref); - BLI_strncpy(filelist->filelist.root, library_path.c_str(), sizeof(filelist->filelist.root)); + + BLI_strncpy(filelist->filelist.root, root_path.c_str(), sizeof(filelist->filelist.root)); filelist_readjob_recursive_dir_add_items(true, job_params, stop, do_update, progress); - } + }); } /** -- 2.30.2 From 126136baabac679e95d578267d2b4f042dc5f9bd Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 30 Nov 2022 20:15:04 +0100 Subject: [PATCH 03/49] Fix missing asset previews and broken drag & drop in "All" library Together with the changes made in master, all this does is making sure the assets are loaded and removed using the correct asset library nested within the "All" library. Now full paths for the assets can be built correctly from the asset identifier, which fixes preview loading and drag & drop. --- .../blender/asset_system/AS_asset_library.hh | 3 +++ .../asset_system/intern/asset_library.cc | 19 +++++++++++++++++- source/blender/editors/space_file/filelist.cc | 20 +++++++++++++------ 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/source/blender/asset_system/AS_asset_library.hh b/source/blender/asset_system/AS_asset_library.hh index cb75a2540ff..561452d3ac8 100644 --- a/source/blender/asset_system/AS_asset_library.hh +++ b/source/blender/asset_system/AS_asset_library.hh @@ -101,6 +101,9 @@ class AssetLibrary { /** Remove an asset from the library that was added using #add_external_asset() or * #add_local_id_asset(). Can usually be expected to be constant time complexity (worst case may * differ). + * Can also be called when this asset library is just a merged library containing multiple nested + * ones ("All" library). Will then check if it exists in a nested library and remove it. + * * \note This is save to call if \a asset is freed (dangling reference), will not perform any * change then. * \return True on success, false if the asset couldn't be found inside the library (also the diff --git a/source/blender/asset_system/intern/asset_library.cc b/source/blender/asset_system/intern/asset_library.cc index 043d1a45ef2..944e91bcf4d 100644 --- a/source/blender/asset_system/intern/asset_library.cc +++ b/source/blender/asset_system/intern/asset_library.cc @@ -170,7 +170,24 @@ AssetRepresentation &AssetLibrary::add_local_id_asset(StringRef relative_asset_p bool AssetLibrary::remove_asset(AssetRepresentation &asset) { - return asset_storage_->remove_asset(asset); + /* Usual case, only the "All" library differs and uses nested libraries (see below). */ + if (asset_storage_->remove_asset(asset)) { + return true; + } + + /* If asset is not stored in this library, check nested ones (for "All" library). */ + for (AssetLibrary *library : nested_libs_) { + if (!library) { + BLI_assert_unreachable(); + continue; + } + + if (asset_storage_->remove_asset(asset)) { + return true; + } + } + + return false; } void AssetLibrary::foreach_nested(FunctionRef fn) diff --git a/source/blender/editors/space_file/filelist.cc b/source/blender/editors/space_file/filelist.cc index 5039fcdce5a..9d1191ad80e 100644 --- a/source/blender/editors/space_file/filelist.cc +++ b/source/blender/editors/space_file/filelist.cc @@ -2928,6 +2928,11 @@ struct FileListReadJob { * `Materials/Material.001`). */ char cur_relbase[FILE_MAX_LIBEXTRA]; + /** The current asset library to load. Usually the same as #FileList.asset_library, however + * sometimes the #FileList one is a combination of multiple other ones ("All" asset library), + * which need to be loaded individually. Then this can be set to override the #FileList library. + * Use this in all loading code. */ + asset_system::AssetLibrary *load_asset_library; /** Set to request a partial read that only adds files representing #Main data (IDs). Used when * #Main may have received changes of interest (e.g. asset removed or renamed). */ bool only_main_data; @@ -3105,7 +3110,6 @@ static void filelist_readjob_list_lib_add_datablock(FileListReadJob *job_params, const int idcode, const char *group_name) { - FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ FileListInternEntry *entry = MEM_cnew(__func__); if (prefix_relpath_with_group_name) { std::string datablock_path = StringRef(group_name) + "/" + datablock_info->name; @@ -3121,13 +3125,13 @@ static void filelist_readjob_list_lib_add_datablock(FileListReadJob *job_params, if (datablock_info->asset_data) { entry->typeflag |= FILE_TYPE_ASSET; - if (filelist->asset_library) { + if (job_params->load_asset_library) { /** XXX Moving out the asset metadata like this isn't great. */ std::unique_ptr metadata = BKE_asset_metadata_move_to_unique_ptr( datablock_info->asset_data); BKE_asset_metadata_free(&datablock_info->asset_data); - entry->asset = &filelist->asset_library->add_external_asset( + entry->asset = &job_params->load_asset_library->add_external_asset( entry->relpath, datablock_info->name, std::move(metadata)); } } @@ -3631,7 +3635,7 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib, } /* Only load assets when browsing an asset library. For normal file browsing we return all * entries. `FLF_ASSETS_ONLY` filter can be enabled/disabled by the user. */ - if (filelist->asset_library_ref) { + if (job_params->load_asset_library) { list_lib_options |= LIST_LIB_ASSETS_ONLY; } std::optional lib_entries_num = filelist_readjob_list_lib( @@ -3746,6 +3750,8 @@ static void filelist_readjob_load_asset_library_data(FileListReadJob *job_params * #filelist_readjob_endjob() will move it into the real filelist. */ tmp_filelist->asset_library = AS_asset_library_load(job_params->current_main, *job_params->filelist->asset_library_ref); + /* Set asset library to load (may be overridden later for loading nested ones). */ + job_params->load_asset_library = tmp_filelist->asset_library; *do_update = true; } @@ -3783,8 +3789,8 @@ static void filelist_readjob_main_assets_add_items(FileListReadJob *job_params, entry->local_data.preview_image = BKE_asset_metadata_preview_get_from_id(id_iter->asset_data, id_iter); entry->local_data.id = id_iter; - if (filelist->asset_library) { - entry->asset = &filelist->asset_library->add_local_id_asset(entry->relpath, *id_iter); + if (job_params->load_asset_library) { + entry->asset = &job_params->load_asset_library->add_local_id_asset(entry->relpath, *id_iter); } entries_num++; BLI_addtail(&tmp_entries, entry); @@ -3902,6 +3908,8 @@ static void filelist_readjob_all_asset_library(FileListReadJob *job_params, return; } + /* Override library info to read this library. */ + job_params->load_asset_library = &nested_library; BLI_strncpy(filelist->filelist.root, root_path.c_str(), sizeof(filelist->filelist.root)); filelist_readjob_recursive_dir_add_items(true, job_params, stop, do_update, progress); -- 2.30.2 From fb2303fb739e72e263289d9aeee10031b10a3ec6 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 2 Dec 2022 16:58:47 +0100 Subject: [PATCH 04/49] Avoid ugly nested library storage We actually don't have to do this, since we can just iterate over all loaded libraries after calling the loading for the "All" asset library. --- .../blender/asset_system/AS_asset_library.hh | 32 +++++++------ .../asset_system/intern/asset_library.cc | 46 ++++++------------- .../intern/asset_library_service.cc | 31 +++++++------ .../intern/asset_library_service.hh | 6 ++- source/blender/editors/space_file/filelist.cc | 7 +-- 5 files changed, 56 insertions(+), 66 deletions(-) diff --git a/source/blender/asset_system/AS_asset_library.hh b/source/blender/asset_system/AS_asset_library.hh index a9ed5ff0594..d377f2334b8 100644 --- a/source/blender/asset_system/AS_asset_library.hh +++ b/source/blender/asset_system/AS_asset_library.hh @@ -56,10 +56,6 @@ class AssetLibrary { */ std::unique_ptr asset_storage_; - /** In some cases an asset library is a combination of multiple other ones, these are then - * referenced here. "All" asset library only currently. */ - Vector nested_libs_; /* Non-owning pointers. */ - bCallbackFuncStore on_save_callback_store_{}; public: @@ -78,6 +74,17 @@ class AssetLibrary { AssetLibrary(StringRef root_path = ""); ~AssetLibrary(); + /** + * Execute \a fn for every asset library that is loaded. The asset library is passed to the + * \a fn call. + * + * \param skip_all_library: When true, the \a fn will also be executed for the "All" asset + * library. This is just a combination of the other ones, so usually + * iterating over it is redundant. + */ + static void foreach_loaded(FunctionRef fn, + bool include_all_library = true); + void load_catalogs(); /** Load catalogs that have changed on disk. */ @@ -101,8 +108,6 @@ class AssetLibrary { /** Remove an asset from the library that was added using #add_external_asset() or * #add_local_id_asset(). Can usually be expected to be constant time complexity (worst case may * differ). - * Can also be called when this asset library is just a merged library containing multiple nested - * ones ("All" library). Will then check if it exists in a nested library and remove it. * * \note This is save to call if \a asset is freed (dangling reference), will not perform any * change then. @@ -110,11 +115,6 @@ class AssetLibrary { * case when the reference is dangling). */ bool remove_asset(AssetRepresentation &asset); - /** In some cases an asset library is a combination of multiple other ones ("All" asset library - * only currently). Iterate over the contained asset libraries, executing \a fn for each of them. - */ - void foreach_nested(FunctionRef fn); - /** * Remap ID pointers for local ID assets, see #BKE_lib_remap.h. When an ID pointer would be * mapped to null (typically when an ID gets removed), the asset is removed, because we don't @@ -142,9 +142,6 @@ class AssetLibrary { AssetIdentifier asset_identifier_from_library(StringRef relative_asset_path); StringRefNull root_path() const; - - private: - std::optional find_asset_index(const AssetRepresentation &asset); }; Vector all_valid_asset_library_refs(); @@ -152,6 +149,13 @@ Vector all_valid_asset_library_refs(); } // namespace blender::asset_system /** + * Load the data for an asset library, but not the asset representations themselves (loading these + * is currently not done in the asset system). + * + * For the "All" asset library (#ASSET_LIBRARY_ALL), every other known asset library will be + * loaded as well. So a call to #AssetLibrary::foreach_loaded() can be expected to iterate over all + * libraries. + * * \warning Catalogs are reloaded, invalidating catalog pointers. Do not store catalog pointers, * store CatalogIDs instead and lookup the catalog where needed. */ diff --git a/source/blender/asset_system/intern/asset_library.cc b/source/blender/asset_system/intern/asset_library.cc index 1fe477707a1..b763822c1ac 100644 --- a/source/blender/asset_system/intern/asset_library.cc +++ b/source/blender/asset_system/intern/asset_library.cc @@ -121,9 +121,11 @@ void AS_asset_library_refresh_catalog_simplename(struct ::AssetLibrary *asset_li void AS_asset_library_remap_ids(const IDRemapper *mappings) { AssetLibraryService *service = AssetLibraryService::get(); - service->foreach_loaded_asset_library([mappings](asset_system::AssetLibrary &library) { - library.remap_ids_and_remove_invalid(*mappings); - }); + service->foreach_loaded_asset_library( + [mappings](asset_system::AssetLibrary &library) { + library.remap_ids_and_remove_invalid(*mappings); + }, + true); } namespace blender::asset_system { @@ -142,6 +144,13 @@ AssetLibrary::~AssetLibrary() } } +void AssetLibrary::foreach_loaded(FunctionRef fn, + const bool include_all_library) +{ + AssetLibraryService *service = AssetLibraryService::get(); + service->foreach_loaded_asset_library(fn, include_all_library); +} + void AssetLibrary::load_catalogs() { auto catalog_service = std::make_unique(root_path()); @@ -170,36 +179,7 @@ AssetRepresentation &AssetLibrary::add_local_id_asset(StringRef relative_asset_p bool AssetLibrary::remove_asset(AssetRepresentation &asset) { - /* Usual case, only the "All" library differs and uses nested libraries (see below). */ - if (asset_storage_->remove_asset(asset)) { - return true; - } - - /* If asset is not stored in this library, check nested ones (for "All" library). */ - for (AssetLibrary *library : nested_libs_) { - if (!library) { - BLI_assert_unreachable(); - continue; - } - - if (asset_storage_->remove_asset(asset)) { - return true; - } - } - - return false; -} - -void AssetLibrary::foreach_nested(FunctionRef fn) -{ - for (AssetLibrary *library : nested_libs_) { - if (!library) { - BLI_assert_unreachable(); - continue; - } - - fn(*library); - } + return asset_storage_->remove_asset(asset); } void AssetLibrary::remap_ids_and_remove_invalid(const IDRemapper &mappings) diff --git a/source/blender/asset_system/intern/asset_library_service.cc b/source/blender/asset_system/intern/asset_library_service.cc index 8a00ebe2a08..7f44143fef8 100644 --- a/source/blender/asset_system/intern/asset_library_service.cc +++ b/source/blender/asset_system/intern/asset_library_service.cc @@ -132,7 +132,6 @@ AssetLibrary *AssetLibraryService::get_asset_library_all(const Main *bmain) { if (all_library_) { CLOG_INFO(&LOG, 2, "get all lib (cached)"); - all_library_->nested_libs_.clear(); } else { CLOG_INFO(&LOG, 2, "get all lib (loaded)"); @@ -145,8 +144,8 @@ AssetLibrary *AssetLibraryService::get_asset_library_all(const Main *bmain) continue; } - AssetLibrary *nested_lib = get_asset_library(bmain, library_ref); - all_library_->nested_libs_.append(nested_lib); + /* Ensure all asset libraries are loaded. */ + get_asset_library(bmain, library_ref); } return all_library_.get(); @@ -214,21 +213,25 @@ void AssetLibraryService::app_handler_unregister() bool AssetLibraryService::has_any_unsaved_catalogs() const { - if (current_file_library_ && current_file_library_->catalog_service->has_unsaved_changes()) { - return true; - } + bool has_unsaved_changes = false; - for (const auto &asset_lib_uptr : on_disk_libraries_.values()) { - if (asset_lib_uptr->catalog_service->has_unsaved_changes()) { - return true; - } - } - - return false; + foreach_loaded_asset_library( + [&has_unsaved_changes](AssetLibrary &library) { + if (library.catalog_service->has_unsaved_changes()) { + has_unsaved_changes = true; + } + }, + true); + return has_unsaved_changes; } -void AssetLibraryService::foreach_loaded_asset_library(FunctionRef fn) const +void AssetLibraryService::foreach_loaded_asset_library(FunctionRef fn, + const bool include_all_library) const { + if (include_all_library && all_library_) { + fn(*all_library_); + } + if (current_file_library_) { fn(*current_file_library_); } diff --git a/source/blender/asset_system/intern/asset_library_service.hh b/source/blender/asset_system/intern/asset_library_service.hh index 478465d25ed..1dd9bda7aca 100644 --- a/source/blender/asset_system/intern/asset_library_service.hh +++ b/source/blender/asset_system/intern/asset_library_service.hh @@ -70,13 +70,15 @@ class AssetLibraryService { /** Get the "Current File" asset library. */ AssetLibrary *get_asset_library_current_file(); - /** Get the "All" asset library, merging all others into one. */ + /** Get the "All" asset library, which loads all others and merges them into one. */ AssetLibrary *get_asset_library_all(const Main *bmain); /** Returns whether there are any known asset libraries with unsaved catalog edits. */ bool has_any_unsaved_catalogs() const; - void foreach_loaded_asset_library(FunctionRef fn) const; + /** See AssetLibrary::foreach_loaded(). */ + void foreach_loaded_asset_library(FunctionRef fn, + bool include_all_library) const; protected: /** Allocate a new instance of the service and assign it to `instance_`. */ diff --git a/source/blender/editors/space_file/filelist.cc b/source/blender/editors/space_file/filelist.cc index ca83ff6d51b..60c34ceda5f 100644 --- a/source/blender/editors/space_file/filelist.cc +++ b/source/blender/editors/space_file/filelist.cc @@ -3125,7 +3125,7 @@ static void filelist_readjob_list_lib_add_datablock(FileListReadJob *job_params, if (datablock_info->asset_data) { entry->typeflag |= FILE_TYPE_ASSET; - if (job_params->asset_library) { + if (job_params->load_asset_library) { /* Take ownership over the asset data (shallow copies into unique_ptr managed memory) to * pass it on to the asset system. */ std::unique_ptr metadata = std::make_unique(*datablock_info->asset_data); @@ -3905,8 +3905,9 @@ static void filelist_readjob_all_asset_library(FileListReadJob *job_params, BLI_assert(filelist->asset_library != nullptr); - /* Add assets from asset libraries on disk. */ - filelist->asset_library->foreach_nested([&](asset_system::AssetLibrary &nested_library) { + /* The "All" asset library was loaded, which means all other asset libraries are also loaded. + * Load their assets from disk into the "All" library. */ + asset_system::AssetLibrary::foreach_loaded([&](asset_system::AssetLibrary &nested_library) { StringRefNull root_path = nested_library.root_path(); if (root_path.is_empty()) { return; -- 2.30.2 From af5d225653889bde783f165726ce95786928839a Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 2 Dec 2022 19:19:08 +0100 Subject: [PATCH 05/49] Load catalogs for "All" asset library Merges the catalog definitions from all asset libraries in to the storage of the "All" one, builds the catalog tree and refreshes data as needed. This doesn't allow writing changes back to the catalog definition files, so the UI probably shouldn't allow edits. --- .../blender/asset_system/AS_asset_catalog.hh | 21 +++++++++- .../blender/asset_system/AS_asset_library.hh | 2 + .../asset_system/intern/asset_catalog.cc | 27 ++++++++++--- .../asset_system/intern/asset_library.cc | 4 +- .../intern/asset_library_service.cc | 40 ++++++++++++++----- 5 files changed, 77 insertions(+), 17 deletions(-) diff --git a/source/blender/asset_system/AS_asset_catalog.hh b/source/blender/asset_system/AS_asset_catalog.hh index 87b9425c8c6..a7829778fdf 100644 --- a/source/blender/asset_system/AS_asset_catalog.hh +++ b/source/blender/asset_system/AS_asset_catalog.hh @@ -67,6 +67,12 @@ class AssetCatalogService { /** Load asset catalog definitions from the given file or directory. */ void load_from_disk(const CatalogFilePath &file_or_directory_path); + /** + * Duplicate the catalogs from \a other_service into this one. Does not rebuild the tree, this + * needs to be done by the caller (call #rebuild_tree()!). + */ + void add_from_existing(const AssetCatalogService &other_service); + /** * Write the catalog definitions to disk. * @@ -105,6 +111,15 @@ class AssetCatalogService { */ void reload_catalogs(); + /** + * Make sure the tree is updated to the latest collection of catalogs stored in this service. + * Does not depend on a CDF file being available so this can be called on a service that stores + * catalogs that are not stored in a CDF. + * Most API functions that modify catalog data will trigger this, unless otherwise specified (for + * batch operations). + */ + void rebuild_tree(); + /** Return catalog with the given ID. Return nullptr if not found. */ AssetCatalog *find_catalog(CatalogID catalog_id) const; @@ -222,7 +237,6 @@ class AssetCatalogService { const CatalogFilePath &blend_file_path); std::unique_ptr read_into_tree(); - void rebuild_tree(); /** * For every catalog, ensure that its parent path also has a known catalog. @@ -270,6 +284,11 @@ class AssetCatalogCollection { AssetCatalogCollection(AssetCatalogCollection &&other) noexcept = default; std::unique_ptr deep_copy() const; + /** + * Copy the catalogs from \a other and append them to this collection. Copies no other data + * otherwise (but marks as having unsaved changes). + */ + void add_catalogs_from_existing(const AssetCatalogCollection &other); protected: static OwningAssetCatalogMap copy_catalog_map(const OwningAssetCatalogMap &orig); diff --git a/source/blender/asset_system/AS_asset_library.hh b/source/blender/asset_system/AS_asset_library.hh index d377f2334b8..3f2562aa987 100644 --- a/source/blender/asset_system/AS_asset_library.hh +++ b/source/blender/asset_system/AS_asset_library.hh @@ -56,6 +56,8 @@ class AssetLibrary { */ std::unique_ptr asset_storage_; + std::function on_refresh_; + bCallbackFuncStore on_save_callback_store_{}; public: diff --git a/source/blender/asset_system/intern/asset_catalog.cc b/source/blender/asset_system/intern/asset_catalog.cc index 9b47aca1209..7924ceb862c 100644 --- a/source/blender/asset_system/intern/asset_catalog.cc +++ b/source/blender/asset_system/intern/asset_catalog.cc @@ -323,6 +323,11 @@ void AssetCatalogService::load_from_disk(const CatalogFilePath &file_or_director rebuild_tree(); } +void AssetCatalogService::add_from_existing(const AssetCatalogService &other_service) +{ + catalog_collection_->add_catalogs_from_existing(*other_service.catalog_collection_); +} + void AssetCatalogService::load_directory_recursive(const CatalogFilePath &directory_path) { /* TODO(@sybren): implement proper multi-file support. For now, just load @@ -658,15 +663,25 @@ std::unique_ptr AssetCatalogCollection::deep_copy() cons return copy; } +static void copy_catalog_map_into_existing(const OwningAssetCatalogMap &source, + OwningAssetCatalogMap &dest) +{ + for (const auto &orig_catalog_uptr : source.values()) { + auto copy_catalog_uptr = std::make_unique(*orig_catalog_uptr); + dest.add_new(copy_catalog_uptr->catalog_id, std::move(copy_catalog_uptr)); + } +} + +void AssetCatalogCollection::add_catalogs_from_existing(const AssetCatalogCollection &other) +{ + has_unsaved_changes_ = true; + copy_catalog_map_into_existing(other.catalogs_, catalogs_); +} + OwningAssetCatalogMap AssetCatalogCollection::copy_catalog_map(const OwningAssetCatalogMap &orig) { OwningAssetCatalogMap copy; - - for (const auto &orig_catalog_uptr : orig.values()) { - auto copy_catalog_uptr = std::make_unique(*orig_catalog_uptr); - copy.add_new(copy_catalog_uptr->catalog_id, std::move(copy_catalog_uptr)); - } - + copy_catalog_map_into_existing(orig, copy); return copy; } diff --git a/source/blender/asset_system/intern/asset_library.cc b/source/blender/asset_system/intern/asset_library.cc index b763822c1ac..02c5c0dc4f3 100644 --- a/source/blender/asset_system/intern/asset_library.cc +++ b/source/blender/asset_system/intern/asset_library.cc @@ -160,7 +160,9 @@ void AssetLibrary::load_catalogs() void AssetLibrary::refresh() { - this->catalog_service->reload_catalogs(); + if (on_refresh_) { + on_refresh_(); + } } AssetRepresentation &AssetLibrary::add_external_asset(StringRef relative_asset_path, diff --git a/source/blender/asset_system/intern/asset_library_service.cc b/source/blender/asset_system/intern/asset_library_service.cc index 7f44143fef8..430de903db7 100644 --- a/source/blender/asset_system/intern/asset_library_service.cc +++ b/source/blender/asset_system/intern/asset_library_service.cc @@ -16,6 +16,7 @@ #include "CLG_log.h" +#include "AS_asset_catalog_tree.hh" #include "AS_asset_library.hh" #include "asset_library_service.hh" #include "utils.hh" @@ -107,6 +108,8 @@ AssetLibrary *AssetLibraryService::get_asset_library_on_disk(StringRefNull root_ lib->on_blend_save_handler_register(); lib->load_catalogs(); + /* Reload catalogs on refresh. */ + lib->on_refresh_ = [lib]() { lib->catalog_service->reload_catalogs(); }; on_disk_libraries_.add_new(normalized_root_path, std::move(lib_uptr)); CLOG_INFO(&LOG, 2, "get \"%s\" (loaded)", normalized_root_path.c_str()); @@ -117,6 +120,7 @@ AssetLibrary *AssetLibraryService::get_asset_library_current_file() { if (current_file_library_) { CLOG_INFO(&LOG, 2, "get current file lib (cached)"); + current_file_library_->refresh(); } else { CLOG_INFO(&LOG, 2, "get current file lib (loaded)"); @@ -130,14 +134,7 @@ AssetLibrary *AssetLibraryService::get_asset_library_current_file() AssetLibrary *AssetLibraryService::get_asset_library_all(const Main *bmain) { - if (all_library_) { - CLOG_INFO(&LOG, 2, "get all lib (cached)"); - } - else { - CLOG_INFO(&LOG, 2, "get all lib (loaded)"); - all_library_ = std::make_unique(); - } - + /* (Re-)load all other asset libraries. */ for (AssetLibraryReference &library_ref : all_valid_asset_library_refs()) { /* Skip self :) */ if (library_ref.type == ASSET_LIBRARY_ALL) { @@ -148,7 +145,32 @@ AssetLibrary *AssetLibraryService::get_asset_library_all(const Main *bmain) get_asset_library(bmain, library_ref); } - return all_library_.get(); + if (all_library_) { + CLOG_INFO(&LOG, 2, "get all lib (cached)"); + all_library_->refresh(); + return all_library_.get(); + } + + CLOG_INFO(&LOG, 2, "get all lib (loaded)"); + all_library_ = std::make_unique(); + + AssetLibrary &all_library = *all_library_; + auto build_catalogs_fn = [&all_library]() { + /* Start with empty catalog storage. */ + all_library.catalog_service = std::make_unique(); + + /* (Re-)load catalogs on refresh. */ + AssetLibrary::foreach_loaded([&all_library](AssetLibrary &nested) { + nested.catalog_service->reload_catalogs(); + all_library.catalog_service->add_from_existing(*nested.catalog_service); + }); + all_library.catalog_service->rebuild_tree(); + }; + + build_catalogs_fn(); + all_library.on_refresh_ = build_catalogs_fn; + + return &all_library; } std::string AssetLibraryService::root_path_from_library_ref( -- 2.30.2 From a07a2e2369d0290d08e124aa53d56cfd4600e341 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 2 Dec 2022 19:32:46 +0100 Subject: [PATCH 06/49] Avoid redundant loading of catalogs and "All" library processing --- .../blender/asset_system/AS_asset_library.hh | 3 +-- .../intern/asset_library_service.cc | 19 ++++++++++------ source/blender/editors/space_file/filelist.cc | 22 ++++++++++--------- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/source/blender/asset_system/AS_asset_library.hh b/source/blender/asset_system/AS_asset_library.hh index 3f2562aa987..6526e3e8382 100644 --- a/source/blender/asset_system/AS_asset_library.hh +++ b/source/blender/asset_system/AS_asset_library.hh @@ -84,8 +84,7 @@ class AssetLibrary { * library. This is just a combination of the other ones, so usually * iterating over it is redundant. */ - static void foreach_loaded(FunctionRef fn, - bool include_all_library = true); + static void foreach_loaded(FunctionRef fn, bool include_all_library); void load_catalogs(); diff --git a/source/blender/asset_system/intern/asset_library_service.cc b/source/blender/asset_system/intern/asset_library_service.cc index 430de903db7..fbcda02025c 100644 --- a/source/blender/asset_system/intern/asset_library_service.cc +++ b/source/blender/asset_system/intern/asset_library_service.cc @@ -155,20 +155,25 @@ AssetLibrary *AssetLibraryService::get_asset_library_all(const Main *bmain) all_library_ = std::make_unique(); AssetLibrary &all_library = *all_library_; - auto build_catalogs_fn = [&all_library]() { + auto build_catalogs_fn = [&all_library](const bool is_first_load) { /* Start with empty catalog storage. */ all_library.catalog_service = std::make_unique(); /* (Re-)load catalogs on refresh. */ - AssetLibrary::foreach_loaded([&all_library](AssetLibrary &nested) { - nested.catalog_service->reload_catalogs(); - all_library.catalog_service->add_from_existing(*nested.catalog_service); - }); + AssetLibrary::foreach_loaded( + [&](AssetLibrary &nested) { + /* On first load the catalogs were read just above, no need to reload. */ + if (!is_first_load) { + nested.catalog_service->reload_catalogs(); + } + all_library.catalog_service->add_from_existing(*nested.catalog_service); + }, + false); all_library.catalog_service->rebuild_tree(); }; - build_catalogs_fn(); - all_library.on_refresh_ = build_catalogs_fn; + build_catalogs_fn(true); + all_library.on_refresh_ = [build_catalogs_fn]() { build_catalogs_fn(false); }; return &all_library; } diff --git a/source/blender/editors/space_file/filelist.cc b/source/blender/editors/space_file/filelist.cc index 60c34ceda5f..2bf91efc610 100644 --- a/source/blender/editors/space_file/filelist.cc +++ b/source/blender/editors/space_file/filelist.cc @@ -3907,18 +3907,20 @@ static void filelist_readjob_all_asset_library(FileListReadJob *job_params, /* The "All" asset library was loaded, which means all other asset libraries are also loaded. * Load their assets from disk into the "All" library. */ - asset_system::AssetLibrary::foreach_loaded([&](asset_system::AssetLibrary &nested_library) { - StringRefNull root_path = nested_library.root_path(); - if (root_path.is_empty()) { - return; - } + asset_system::AssetLibrary::foreach_loaded( + [&](asset_system::AssetLibrary &nested_library) { + StringRefNull root_path = nested_library.root_path(); + if (root_path.is_empty()) { + return; + } - /* Override library info to read this library. */ - job_params->load_asset_library = &nested_library; - BLI_strncpy(filelist->filelist.root, root_path.c_str(), sizeof(filelist->filelist.root)); + /* Override library info to read this library. */ + job_params->load_asset_library = &nested_library; + BLI_strncpy(filelist->filelist.root, root_path.c_str(), sizeof(filelist->filelist.root)); - filelist_readjob_recursive_dir_add_items(true, job_params, stop, do_update, progress); - }); + filelist_readjob_recursive_dir_add_items(true, job_params, stop, do_update, progress); + }, + false); } /** -- 2.30.2 From ecc25bc62ec517420ce0aef47d9a6af761643f22 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 2 Dec 2022 20:23:54 +0100 Subject: [PATCH 07/49] Use "All" library for node add menu building Code was manually building the add menu from all asset libraries, this should be simpler now. --- source/blender/editors/asset/ED_asset_list.hh | 12 ++ .../editors/asset/intern/asset_list.cc | 17 +++ .../editors/space_node/add_menu_assets.cc | 117 ++++++++---------- 3 files changed, 78 insertions(+), 68 deletions(-) diff --git a/source/blender/editors/asset/ED_asset_list.hh b/source/blender/editors/asset/ED_asset_list.hh index 541fa315f77..f60c6752d5f 100644 --- a/source/blender/editors/asset/ED_asset_list.hh +++ b/source/blender/editors/asset/ED_asset_list.hh @@ -15,6 +15,18 @@ struct AssetLibraryReference; struct FileDirEntry; struct bContext; +namespace blender::asset_system { +class AssetLibrary; +} + +/** + * Get the asset library being read into an asset-list and identified using \a library_reference. + * \note The asset library may be loaded asynchronously, so this may return null until it becomes + * available. + */ +blender::asset_system::AssetLibrary *ED_assetlist_library_get( + const AssetLibraryReference &library_reference); + /* 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/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc index 64636693a58..e086e5702d1 100644 --- a/source/blender/editors/asset/intern/asset_list.cc +++ b/source/blender/editors/asset/intern/asset_list.cc @@ -114,6 +114,7 @@ class AssetList : NonCopyable { void clear(bContext *C); bool needsRefetch() const; + asset_system::AssetLibrary *asset_library() const; void iterate(AssetListIterFn fn) const; bool listen(const wmNotifier ¬ifier) const; int size() const; @@ -180,6 +181,11 @@ bool AssetList::needsRefetch() const return filelist_needs_force_reset(filelist_) || filelist_needs_reading(filelist_); } +asset_system::AssetLibrary *AssetList::asset_library() const +{ + return reinterpret_cast(filelist_asset_library(filelist_)); +} + void AssetList::iterate(AssetListIterFn fn) const { FileList *files = filelist_; @@ -399,6 +405,7 @@ AssetListStorage::AssetListMap &AssetListStorage::global_storage() /** \name C-API * \{ */ +using namespace blender; using namespace blender::ed::asset; void ED_assetlist_storage_fetch(const AssetLibraryReference *library_reference, const bContext *C) @@ -449,6 +456,16 @@ void ED_assetlist_iterate(const AssetLibraryReference &library_reference, AssetL } } +asset_system::AssetLibrary *ED_assetlist_library_get( + const AssetLibraryReference &library_reference) +{ + const AssetList *list = AssetListStorage::lookup_list(library_reference); + if (!list) { + return nullptr; + } + return list->asset_library(); +} + ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle) { ImBuf *imbuf = filelist_file_getimage(asset_handle->file_data); diff --git a/source/blender/editors/space_node/add_menu_assets.cc b/source/blender/editors/space_node/add_menu_assets.cc index bb5f33f8cf0..2296922e040 100644 --- a/source/blender/editors/space_node/add_menu_assets.cc +++ b/source/blender/editors/space_node/add_menu_assets.cc @@ -49,13 +49,6 @@ struct LibraryAsset { AssetHandle handle; }; -struct LibraryCatalog { - asset_system::AssetLibrary *library; - /* Catalog pointers are not save to store. Use the catalog ID instead and lookup the catalog when - * needed. */ - const asset_system::CatalogID catalog_id; -}; - struct AssetItemTree { asset_system::AssetCatalogTree catalogs; MultiValueMap assets_per_path; @@ -63,14 +56,18 @@ struct AssetItemTree { full_catalog_per_tree_item; }; +static AssetLibraryReference all_library_reference() +{ + AssetLibraryReference all_library_ref{}; + all_library_ref.custom_library_index = -1; + all_library_ref.type = ASSET_LIBRARY_ALL; + return all_library_ref; +} + static bool all_loading_finished() { - for (const AssetLibraryReference &library : asset_system::all_valid_asset_library_refs()) { - if (!ED_assetlist_is_loaded(&library)) { - return false; - } - } - return true; + AssetLibraryReference all_library_ref = all_library_reference(); + return ED_assetlist_is_loaded(&all_library_ref); } static AssetItemTree build_catalog_tree(const bContext &C, const bNodeTree *node_tree) @@ -78,68 +75,52 @@ static AssetItemTree build_catalog_tree(const bContext &C, const bNodeTree *node if (!node_tree) { return {}; } - const Main &bmain = *CTX_data_main(&C); - const Vector all_libraries = asset_system::all_valid_asset_library_refs(); - - /* Merge catalogs from all libraries to deduplicate menu items. Also store the catalog and - * library for each asset ID in order to use them later when retrieving assets and removing - * empty catalogs. */ - Map id_to_catalog_map; - asset_system::AssetCatalogTree catalogs_from_all_libraries; - for (const AssetLibraryReference &library_ref : all_libraries) { - if (asset_system::AssetLibrary *library = AS_asset_library_load(&bmain, library_ref)) { - if (asset_system::AssetCatalogTree *tree = library->catalog_service->get_catalog_tree()) { - tree->foreach_item([&](asset_system::AssetCatalogTreeItem &item) { - const asset_system::CatalogID &id = item.get_catalog_id(); - asset_system::AssetCatalog *catalog = library->catalog_service->find_catalog(id); - catalogs_from_all_libraries.insert_item(*catalog); - id_to_catalog_map.add(item.get_catalog_id(), LibraryCatalog{library, id}); - }); - } - } - } /* Find all the matching node group assets for every catalog path. */ MultiValueMap assets_per_path; - for (const AssetLibraryReference &library_ref : all_libraries) { - AssetFilterSettings type_filter{}; - type_filter.id_types = FILTER_ID_NT; - ED_assetlist_storage_fetch(&library_ref, &C); - ED_assetlist_ensure_previews_job(&library_ref, &C); - ED_assetlist_iterate(library_ref, [&](AssetHandle asset) { - if (!ED_asset_filter_matches_asset(&type_filter, &asset)) { - return true; - } - const AssetMetaData &meta_data = *ED_asset_handle_get_metadata(&asset); - const IDProperty *tree_type = BKE_asset_metadata_idprop_find(&meta_data, "type"); - if (tree_type == nullptr || IDP_Int(tree_type) != node_tree->type) { - return true; - } - if (BLI_uuid_is_nil(meta_data.catalog_id)) { - return true; - } - const LibraryCatalog *library_catalog = id_to_catalog_map.lookup_ptr(meta_data.catalog_id); - if (library_catalog == nullptr) { - return true; - } - const asset_system::AssetCatalog *catalog = - library_catalog->library->catalog_service->find_catalog(library_catalog->catalog_id); - assets_per_path.add(catalog->path, LibraryAsset{library_ref, asset}); - return true; - }); + AssetFilterSettings type_filter{}; + type_filter.id_types = FILTER_ID_NT; + + const AssetLibraryReference all_library_ref = all_library_reference(); + + ED_assetlist_storage_fetch(&all_library_ref, &C); + ED_assetlist_ensure_previews_job(&all_library_ref, &C); + + asset_system::AssetLibrary *all_library = ED_assetlist_library_get(all_library_ref); + if (!all_library) { + return {}; } - /* Build the final tree without any of the catalogs that don't have proper node group assets. */ - asset_system::AssetCatalogTree catalogs_with_node_assets; - catalogs_from_all_libraries.foreach_item([&](asset_system::AssetCatalogTreeItem &item) { - if (!assets_per_path.lookup(item.catalog_path()).is_empty()) { - const asset_system::CatalogID &id = item.get_catalog_id(); - const LibraryCatalog &library_catalog = id_to_catalog_map.lookup(id); - asset_system::AssetCatalog *catalog = library_catalog.library->catalog_service->find_catalog( - id); - catalogs_with_node_assets.insert_item(*catalog); + ED_assetlist_iterate(all_library_ref, [&](AssetHandle asset) { + if (!ED_asset_filter_matches_asset(&type_filter, &asset)) { + return true; } + const AssetMetaData &meta_data = *ED_asset_handle_get_metadata(&asset); + const IDProperty *tree_type = BKE_asset_metadata_idprop_find(&meta_data, "type"); + if (tree_type == nullptr || IDP_Int(tree_type) != node_tree->type) { + return true; + } + if (BLI_uuid_is_nil(meta_data.catalog_id)) { + return true; + } + + const asset_system::AssetCatalog *catalog = all_library->catalog_service->find_catalog( + meta_data.catalog_id); + assets_per_path.add(catalog->path, LibraryAsset{all_library_ref, asset}); + return true; + }); + + /* Build an own tree without any of the catalogs that don't have proper node group assets. */ + asset_system::AssetCatalogTree catalogs_with_node_assets; + asset_system::AssetCatalogTree &catalog_tree = *all_library->catalog_service->get_catalog_tree(); + catalog_tree.foreach_item([&](asset_system::AssetCatalogTreeItem &item) { + if (assets_per_path.lookup(item.catalog_path()).is_empty()) { + return; + } + asset_system::AssetCatalog *catalog = all_library->catalog_service->find_catalog( + item.get_catalog_id()); + catalogs_with_node_assets.insert_item(*catalog); }); /* Build another map storing full asset paths for each tree item, in order to have stable -- 2.30.2 From 11abc1be394ccb148c5cda3ecde6dbe0d5eb4f27 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 2 Dec 2022 20:28:33 +0100 Subject: [PATCH 08/49] Use "All" library for node search menu building Code was manually building the search menu items from all asset libraries, this is simpler now. --- .../editors/space_node/add_node_search.cc | 33 +++++-------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/source/blender/editors/space_node/add_node_search.cc b/source/blender/editors/space_node/add_node_search.cc index ba060ab3925..85c79d5e57d 100644 --- a/source/blender/editors/space_node/add_node_search.cc +++ b/source/blender/editors/space_node/add_node_search.cc @@ -93,12 +93,15 @@ static void search_items_for_asset_metadata(const bNodeTree &node_tree, search_items.append(std::move(item)); } -static void gather_search_items_for_asset_library(const bContext &C, - const bNodeTree &node_tree, - const AssetLibraryReference &library_ref, - Set &r_added_assets, - Vector &search_items) +static void gather_search_items_for_all_assets(const bContext &C, + const bNodeTree &node_tree, + Set &r_added_assets, + Vector &search_items) { + AssetLibraryReference library_ref{}; + library_ref.custom_library_index = -1; + library_ref.type = ASSET_LIBRARY_ALL; + AssetFilterSettings filter_settings{}; filter_settings.id_types = FILTER_ID_NT; @@ -117,26 +120,6 @@ static void gather_search_items_for_asset_library(const bContext &C, }); } -static void gather_search_items_for_all_assets(const bContext &C, - const bNodeTree &node_tree, - Set &r_added_assets, - Vector &search_items) -{ - int i; - LISTBASE_FOREACH_INDEX (const bUserAssetLibrary *, asset_library, &U.asset_libraries, i) { - AssetLibraryReference library_ref{}; - library_ref.custom_library_index = i; - library_ref.type = ASSET_LIBRARY_CUSTOM; - /* Skip local assets to avoid duplicates when the asset is part of the local file library. */ - gather_search_items_for_asset_library(C, node_tree, library_ref, r_added_assets, search_items); - } - - AssetLibraryReference library_ref{}; - library_ref.custom_library_index = -1; - library_ref.type = ASSET_LIBRARY_LOCAL; - gather_search_items_for_asset_library(C, node_tree, library_ref, r_added_assets, search_items); -} - static void gather_search_items_for_node_groups(const bContext &C, const bNodeTree &node_tree, const Set &local_assets, -- 2.30.2 From 747a9ea263ad97a78878705f020e3587f50734ff Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 6 Dec 2022 17:02:05 +0100 Subject: [PATCH 09/49] Make catalogs from "All" library read-only Loading the asset library will create a read-only catalog service. The read-only nature is not dealt with much in the asset catalog code, the using code (e.g. the UI) is responsible for respecting it. --- .../blender/asset_system/AS_asset_catalog.hh | 13 ++++++++ .../asset_system/intern/asset_catalog.cc | 16 ++++++++++ .../intern/asset_library_service.cc | 3 +- .../blender/editors/asset/ED_asset_catalog.hh | 5 ++++ .../editors/asset/intern/asset_catalog.cc | 22 ++++++++++++++ .../blender/editors/asset/intern/asset_ops.cc | 13 +++++++- .../space_file/asset_catalog_tree_view.cc | 30 ++++++++++++++++--- 7 files changed, 96 insertions(+), 6 deletions(-) diff --git a/source/blender/asset_system/AS_asset_catalog.hh b/source/blender/asset_system/AS_asset_catalog.hh index a7829778fdf..1d5401f0b0e 100644 --- a/source/blender/asset_system/AS_asset_catalog.hh +++ b/source/blender/asset_system/AS_asset_catalog.hh @@ -45,11 +45,17 @@ class AssetCatalogService { Vector> undo_snapshots_; Vector> redo_snapshots_; + const bool is_read_only_ = false; + public: static const CatalogFilePath DEFAULT_CATALOG_FILENAME; + struct read_only_tag { + }; + public: AssetCatalogService(); + explicit AssetCatalogService(read_only_tag); explicit AssetCatalogService(const CatalogFilePath &asset_library_root); /** @@ -62,6 +68,13 @@ class AssetCatalogService { void tag_has_unsaved_changes(AssetCatalog *edited_catalog); bool has_unsaved_changes() const; + /** + * Check if this is a read-only service meaning the user shouldn't be able to do edits. This is + * not enforced by internal catalog code, the catalog service user is responsible for it. For + * example the UI should disallow edits. + */ + bool is_read_only() const; + /** Load asset catalog definitions from the files found in the asset library. */ void load_from_disk(); /** Load asset catalog definitions from the given file or directory. */ diff --git a/source/blender/asset_system/intern/asset_catalog.cc b/source/blender/asset_system/intern/asset_catalog.cc index 7924ceb862c..6256b6ee503 100644 --- a/source/blender/asset_system/intern/asset_catalog.cc +++ b/source/blender/asset_system/intern/asset_catalog.cc @@ -44,6 +44,11 @@ AssetCatalogService::AssetCatalogService() { } +AssetCatalogService::AssetCatalogService(read_only_tag) : AssetCatalogService() +{ + const_cast(is_read_only_) = true; +} + AssetCatalogService::AssetCatalogService(const CatalogFilePath &asset_library_root) : AssetCatalogService() { @@ -52,6 +57,8 @@ AssetCatalogService::AssetCatalogService(const CatalogFilePath &asset_library_ro void AssetCatalogService::tag_has_unsaved_changes(AssetCatalog *edited_catalog) { + BLI_assert(!is_read_only_); + if (edited_catalog) { edited_catalog->flags.has_unsaved_changes = true; } @@ -86,6 +93,11 @@ bool AssetCatalogService::has_unsaved_changes() const return catalog_collection_->has_unsaved_changes_; } +bool AssetCatalogService::is_read_only() const +{ + return is_read_only_; +} + void AssetCatalogService::tag_all_catalogs_as_unsaved_changes() { for (auto &catalog : catalog_collection_->catalogs_.values()) { @@ -458,6 +470,8 @@ bool AssetCatalogService::is_catalog_known_with_unsaved_changes(const CatalogID bool AssetCatalogService::write_to_disk(const CatalogFilePath &blend_file_path) { + BLI_assert(!is_read_only_); + if (!write_to_disk_ex(blend_file_path)) { return false; } @@ -631,6 +645,7 @@ void AssetCatalogService::undo() void AssetCatalogService::redo() { + BLI_assert(!is_read_only_); BLI_assert_msg(is_redo_possbile(), "Redo stack is empty"); undo_snapshots_.append(std::move(catalog_collection_)); @@ -640,6 +655,7 @@ void AssetCatalogService::redo() void AssetCatalogService::undo_push() { + BLI_assert(!is_read_only_); std::unique_ptr snapshot = catalog_collection_->deep_copy(); undo_snapshots_.append(std::move(snapshot)); redo_snapshots_.clear(); diff --git a/source/blender/asset_system/intern/asset_library_service.cc b/source/blender/asset_system/intern/asset_library_service.cc index fbcda02025c..80356214e40 100644 --- a/source/blender/asset_system/intern/asset_library_service.cc +++ b/source/blender/asset_system/intern/asset_library_service.cc @@ -157,7 +157,8 @@ AssetLibrary *AssetLibraryService::get_asset_library_all(const Main *bmain) AssetLibrary &all_library = *all_library_; auto build_catalogs_fn = [&all_library](const bool is_first_load) { /* Start with empty catalog storage. */ - all_library.catalog_service = std::make_unique(); + all_library.catalog_service = std::make_unique( + AssetCatalogService::read_only_tag()); /* (Re-)load catalogs on refresh. */ AssetLibrary::foreach_loaded( diff --git a/source/blender/editors/asset/ED_asset_catalog.hh b/source/blender/editors/asset/ED_asset_catalog.hh index 4d61f816085..d236bdec37f 100644 --- a/source/blender/editors/asset/ED_asset_catalog.hh +++ b/source/blender/editors/asset/ED_asset_catalog.hh @@ -6,6 +6,9 @@ * UI/Editor level API for catalog operations, creating richer functionality than the asset system * catalog API provides (which this uses internally). * + * Functions can be expected to not perform any change when #ED_asset_catalogs_read_only() returns + * true. The caller should check. + * * Note that `ED_asset_catalog.h` is part of this API. */ @@ -19,6 +22,8 @@ struct AssetLibrary; +[[nodiscard]] bool ED_asset_catalogs_read_only(const AssetLibrary &library); + blender::asset_system::AssetCatalog *ED_asset_catalog_add( AssetLibrary *library, blender::StringRefNull name, blender::StringRef parent_path = nullptr); void ED_asset_catalog_remove(AssetLibrary *library, diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc index 10aa27a7687..2bfde0aeaae 100644 --- a/source/blender/editors/asset/intern/asset_catalog.cc +++ b/source/blender/editors/asset/intern/asset_catalog.cc @@ -19,6 +19,13 @@ using namespace blender; using namespace blender::asset_system; +bool ED_asset_catalogs_read_only(const ::AssetLibrary &library) +{ + asset_system::AssetCatalogService *catalog_service = AS_asset_library_get_catalog_service( + &library); + return catalog_service->is_read_only(); +} + struct CatalogUniqueNameFnData { const AssetCatalogService &catalog_service; StringRef parent_path; @@ -53,6 +60,9 @@ asset_system::AssetCatalog *ED_asset_catalog_add(::AssetLibrary *library, if (!catalog_service) { return nullptr; } + if (ED_asset_catalogs_read_only(*library)) { + return nullptr; + } std::string unique_name = catalog_name_ensure_unique(*catalog_service, name, parent_path); AssetCatalogPath fullpath = AssetCatalogPath(parent_path) / unique_name; @@ -76,6 +86,9 @@ void ED_asset_catalog_remove(::AssetLibrary *library, const CatalogID &catalog_i BLI_assert_unreachable(); return; } + if (ED_asset_catalogs_read_only(*library)) { + return; + } catalog_service->undo_push(); catalog_service->tag_has_unsaved_changes(nullptr); @@ -93,6 +106,9 @@ void ED_asset_catalog_rename(::AssetLibrary *library, BLI_assert_unreachable(); return; } + if (ED_asset_catalogs_read_only(*library)) { + return; + } AssetCatalog *catalog = catalog_service->find_catalog(catalog_id); @@ -120,6 +136,9 @@ void ED_asset_catalog_move(::AssetLibrary *library, BLI_assert_unreachable(); return; } + if (ED_asset_catalogs_read_only(*library)) { + return; + } AssetCatalog *src_catalog = catalog_service->find_catalog(src_catalog_id); if (!src_catalog) { @@ -161,6 +180,9 @@ void ED_asset_catalogs_save_from_main_path(::AssetLibrary *library, const Main * BLI_assert_unreachable(); return; } + if (ED_asset_catalogs_read_only(*library)) { + return; + } /* Since writing to disk also means loading any on-disk changes, it may be a good idea to store * an undo step. */ diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index da4e0794fe8..4189287bc7e 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -438,7 +438,18 @@ static void ASSET_OT_library_refresh(struct wmOperatorType *ot) static bool asset_catalog_operator_poll(bContext *C) { const SpaceFile *sfile = CTX_wm_space_file(C); - return sfile && ED_fileselect_active_asset_library_get(sfile); + if (!sfile) { + return false; + } + const AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile); + if (!asset_library) { + return false; + } + if (ED_asset_catalogs_read_only(*asset_library)) { + CTX_wm_operator_poll_msg_set(C, "Asset catalogs cannot be edited in this asset library"); + return false; + } + return true; } static int asset_catalog_new_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 19b3f135e24..8b7b19387c2 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -119,6 +119,8 @@ class AssetCatalogDropController : public ui::AbstractViewItemDropController { static AssetCatalog *get_drag_catalog(const wmDrag &drag, const ::AssetLibrary &asset_library); static bool has_droppable_asset(const wmDrag &drag, const char **r_disabled_hint); + static bool can_modify_catalogs(const ::AssetLibrary &asset_library, + const char **r_disabled_hint); static bool drop_assets_into_catalog(struct bContext *C, const AssetCatalogTreeView &tree_view, const wmDrag &drag, @@ -321,7 +323,9 @@ void AssetCatalogTreeViewItem::build_context_menu(bContext &C, uiLayout &column) bool AssetCatalogTreeViewItem::supports_renaming() const { - return true; + const AssetCatalogTreeView &tree_view = static_cast( + get_tree_view()); + return !ED_asset_catalogs_read_only(*tree_view.asset_library_); } bool AssetCatalogTreeViewItem::rename(StringRefNull new_name) @@ -360,7 +364,12 @@ AssetCatalogDropController::AssetCatalogDropController(AssetCatalogTreeView &tre bool AssetCatalogDropController::can_drop(const wmDrag &drag, const char **r_disabled_hint) const { if (drag.type == WM_DRAG_ASSET_CATALOG) { - const AssetCatalog *drag_catalog = get_drag_catalog(drag, get_asset_library()); + const ::AssetLibrary &library = get_asset_library(); + if (!can_modify_catalogs(library, r_disabled_hint)) { + return false; + } + + const AssetCatalog *drag_catalog = get_drag_catalog(drag, library); /* NOTE: Technically it's not an issue to allow this (the catalog will just receive a new * path and the catalog system will generate missing parents from the path). But it does * appear broken to users, so disabling entirely. */ @@ -512,6 +521,16 @@ bool AssetCatalogDropController::has_droppable_asset(const wmDrag &drag, return false; } +bool AssetCatalogDropController::can_modify_catalogs(const ::AssetLibrary &library, + const char **r_disabled_hint) +{ + if (ED_asset_catalogs_read_only(library)) { + *r_disabled_hint = "Catalogs cannot be edited in this asset library"; + return false; + } + return true; +} + ::AssetLibrary &AssetCatalogDropController::get_asset_library() const { return *get_view().asset_library_; @@ -579,9 +598,12 @@ bool AssetCatalogTreeViewAllItem::DropController::can_drop(const wmDrag &drag, if (drag.type != WM_DRAG_ASSET_CATALOG) { return false; } + ::AssetLibrary &library = *get_view().asset_library_; + if (!AssetCatalogDropController::can_modify_catalogs(library, r_disabled_hint)) { + return false; + } - const AssetCatalog *drag_catalog = AssetCatalogDropController::get_drag_catalog( - drag, *get_view().asset_library_); + const AssetCatalog *drag_catalog = AssetCatalogDropController::get_drag_catalog(drag, library); if (drag_catalog->path.parent() == "") { *r_disabled_hint = "Catalog is already placed at the highest level"; return false; -- 2.30.2 From e8575bfd4a3a27e9ad636f682ff198907472bec5 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 7 Dec 2022 18:19:15 +0100 Subject: [PATCH 10/49] General cleanup (comments, remove outdated TODO marks, naming) --- source/blender/asset_system/AS_asset_library.hh | 1 - source/blender/asset_system/intern/asset_library.cc | 1 - source/blender/asset_system/intern/asset_storage.hh | 2 -- source/blender/editors/asset/ED_asset_catalog.h | 2 ++ source/blender/editors/asset/ED_asset_catalog.hh | 4 ++++ source/blender/editors/asset/ED_asset_library.h | 7 +++++-- .../asset/intern/asset_library_reference_enum.cc | 12 ++++++------ source/blender/editors/space_file/file_draw.c | 1 - source/blender/editors/space_file/filelist.cc | 1 - source/blender/makesdna/DNA_space_types.h | 1 - 10 files changed, 17 insertions(+), 15 deletions(-) diff --git a/source/blender/asset_system/AS_asset_library.hh b/source/blender/asset_system/AS_asset_library.hh index 6526e3e8382..14d356c703e 100644 --- a/source/blender/asset_system/AS_asset_library.hh +++ b/source/blender/asset_system/AS_asset_library.hh @@ -109,7 +109,6 @@ class AssetLibrary { /** Remove an asset from the library that was added using #add_external_asset() or * #add_local_id_asset(). Can usually be expected to be constant time complexity (worst case may * differ). - * * \note This is save to call if \a asset is freed (dangling reference), will not perform any * change then. * \return True on success, false if the asset couldn't be found inside the library (also the diff --git a/source/blender/asset_system/intern/asset_library.cc b/source/blender/asset_system/intern/asset_library.cc index 02c5c0dc4f3..f5377734b12 100644 --- a/source/blender/asset_system/intern/asset_library.cc +++ b/source/blender/asset_system/intern/asset_library.cc @@ -257,7 +257,6 @@ StringRefNull AssetLibrary::root_path() const return *root_path_; } -/* TODO get rid of this. */ Vector all_valid_asset_library_refs() { Vector result; diff --git a/source/blender/asset_system/intern/asset_storage.hh b/source/blender/asset_system/intern/asset_storage.hh index b4866fa9382..2b4614abca5 100644 --- a/source/blender/asset_system/intern/asset_storage.hh +++ b/source/blender/asset_system/intern/asset_storage.hh @@ -31,8 +31,6 @@ class AssetStorage { * faster lookups. Not possible until each asset is only represented once in the storage. */ StorageT local_id_assets_; - friend class AssetLibrary; - public: /** See #AssetLibrary::add_external_asset(). */ AssetRepresentation &add_external_asset(AssetIdentifier &&identifier, diff --git a/source/blender/editors/asset/ED_asset_catalog.h b/source/blender/editors/asset/ED_asset_catalog.h index 04df381bec9..9f6850b7266 100644 --- a/source/blender/editors/asset/ED_asset_catalog.h +++ b/source/blender/editors/asset/ED_asset_catalog.h @@ -19,6 +19,8 @@ struct Main; void ED_asset_catalogs_save_from_main_path(struct AssetLibrary *library, const struct Main *bmain); +/** Saving catalog edits when the file is saved is a global option shared for each asset library, + * and as such ignores the per asset library #ED_asset_catalogs_read_only(). */ void ED_asset_catalogs_set_save_catalogs_when_file_is_saved(bool should_save); bool ED_asset_catalogs_get_save_catalogs_when_file_is_saved(void); diff --git a/source/blender/editors/asset/ED_asset_catalog.hh b/source/blender/editors/asset/ED_asset_catalog.hh index d236bdec37f..a423ad6f8ad 100644 --- a/source/blender/editors/asset/ED_asset_catalog.hh +++ b/source/blender/editors/asset/ED_asset_catalog.hh @@ -22,6 +22,10 @@ struct AssetLibrary; +/** + * Returns if the catalogs of \a library are allowed to be editable, or if the UI should forbid + * edits. + */ [[nodiscard]] bool ED_asset_catalogs_read_only(const AssetLibrary &library); blender::asset_system::AssetCatalog *ED_asset_catalog_add( diff --git a/source/blender/editors/asset/ED_asset_library.h b/source/blender/editors/asset/ED_asset_library.h index cc0d97054b6..c4baadc23c8 100644 --- a/source/blender/editors/asset/ED_asset_library.h +++ b/source/blender/editors/asset/ED_asset_library.h @@ -29,10 +29,13 @@ AssetLibraryReference ED_asset_library_reference_from_enum_value(int value); * Since this is meant for UI display, skips non-displayable libraries, that is, libraries with an * empty name or path. * - * \param include_local_library: Whether to include the "Current File" library or not. + * \param include_generated: Whether to include libraries that are generated and thus cannot be + * written to. Setting this to false means only custom libraries will be + * included, since they are stored on disk with a single root directory, + * thus have a well defined location that can be written to. */ const struct EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf( - bool include_local_library); + bool include_generated); #ifdef __cplusplus } 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 f88afdd8487..d20f3205a77 100644 --- a/source/blender/editors/asset/intern/asset_library_reference_enum.cc +++ b/source/blender/editors/asset/intern/asset_library_reference_enum.cc @@ -70,14 +70,13 @@ AssetLibraryReference ED_asset_library_reference_from_enum_value(int value) return library; } -const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf( - const bool include_local_library) +const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(const bool include_generated) { EnumPropertyItem *item = nullptr; int totitem = 0; - if (include_local_library) { - const EnumPropertyItem predefined_items[] = { + if (include_generated) { + const EnumPropertyItem generated_items[] = { {ASSET_LIBRARY_ALL, "ALL", ICON_NONE, @@ -91,8 +90,9 @@ const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf( {0, nullptr, 0, nullptr, nullptr}, }; - /* Add predefined items. */ - RNA_enum_items_add(&item, &totitem, predefined_items); + /* Add predefined libraries that are generated and not simple directories that can be written + * to. */ + RNA_enum_items_add(&item, &totitem, generated_items); } /* Add separator if needed. */ diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 7561cd2f6a2..e85a6cbc0d4 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -555,7 +555,6 @@ static void file_draw_preview(const SpaceFile *sfile, /* path is no more static, cannot give it directly to but... */ else if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS && (file->typeflag & FILE_TYPE_ASSET) != 0) { - /* TODO enable drag & drop support, get path from asset representation. */ char blend_path[FILE_MAX_LIBEXTRA]; if (BLO_library_path_explode(path, blend_path, NULL, NULL)) { diff --git a/source/blender/editors/space_file/filelist.cc b/source/blender/editors/space_file/filelist.cc index 2bf91efc610..234c550d829 100644 --- a/source/blender/editors/space_file/filelist.cc +++ b/source/blender/editors/space_file/filelist.cc @@ -1643,7 +1643,6 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry BLI_thread_queue_push(cache->previews_done, preview); } else { - /* XXX */ if (entry->redirection_path) { BLI_strncpy(preview->filepath, entry->redirection_path, FILE_MAXDIR); } diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index bbb6d3e7de3..54edf795e6c 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -995,7 +995,6 @@ enum eFileDetails { /** File selector types. */ typedef enum eFileSelectType { - FILE_SELECT_TYPE_UNSET = 0, FILE_LOADLIB = 1, FILE_MAIN = 2, /** Load assets from #Main. */ -- 2.30.2 From 3cd93ace247fefff8dec69879bb61ed074d55495 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 7 Dec 2022 18:29:38 +0100 Subject: [PATCH 11/49] Simple progress reporting for all library Progress bar display the file reading (and other operations) is actually broken in master for a while, so this won't actually be reported. Still calculate it for once it's fixed. --- source/blender/editors/space_file/filelist.cc | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_file/filelist.cc b/source/blender/editors/space_file/filelist.cc index 234c550d829..44f846f8595 100644 --- a/source/blender/editors/space_file/filelist.cc +++ b/source/blender/editors/space_file/filelist.cc @@ -3900,10 +3900,14 @@ static void filelist_readjob_all_asset_library(FileListReadJob *job_params, if (job_params->only_main_data) { return; } - /* TODO propertly update progress? */ + + /* Count how many asset libraries need to be loaded, for progress reporting. Not very precise. */ + int library_count = 0; + asset_system::AssetLibrary::foreach_loaded([&library_count](auto &) { library_count++; }, false); BLI_assert(filelist->asset_library != nullptr); + int libraries_done_count = 0; /* The "All" asset library was loaded, which means all other asset libraries are also loaded. * Load their assets from disk into the "All" library. */ asset_system::AssetLibrary::foreach_loaded( @@ -3917,7 +3921,12 @@ static void filelist_readjob_all_asset_library(FileListReadJob *job_params, job_params->load_asset_library = &nested_library; BLI_strncpy(filelist->filelist.root, root_path.c_str(), sizeof(filelist->filelist.root)); - filelist_readjob_recursive_dir_add_items(true, job_params, stop, do_update, progress); + float progress_this = 0.0f; + filelist_readjob_recursive_dir_add_items( + true, job_params, stop, do_update, &progress_this); + + libraries_done_count++; + *progress = float(libraries_done_count) / library_count; }, false); } -- 2.30.2 From d6df32a6f841836c9b83f116bd95440346cc74fe Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 7 Dec 2022 20:00:27 +0100 Subject: [PATCH 12/49] Add basic (empty) asset shelf region --- .../blenloader/intern/versioning_300.cc | 21 +++++++++++++++++++ source/blender/editors/screen/area.c | 3 ++- source/blender/editors/screen/screen_edit.c | 3 ++- .../editors/space_view3d/space_view3d.cc | 17 +++++++++++++++ source/blender/makesdna/DNA_screen_types.h | 3 ++- source/blender/makesrna/intern/rna_screen.c | 1 + 6 files changed, 45 insertions(+), 3 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 85078a9902d..2acc95b7339 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -3732,5 +3732,26 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) image->seam_margin = 8; } } + + /* Add a properties sidebar to the spreadsheet editor. */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_VIEW3D) { + ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : + &sl->regionbase; + ARegion *new_asset_shelf = do_versions_add_region_if_not_found( + regionbase, + RGN_TYPE_ASSET_SHELF, + "asset shelf for view3d (versioning)", + RGN_TYPE_UI); + if (new_asset_shelf != nullptr) { + new_asset_shelf->alignment = RGN_ALIGN_BOTTOM; + new_asset_shelf->flag |= RGN_FLAG_HIDDEN; + } + } + } + } + } } } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index a62e027ba03..3721774842c 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1262,7 +1262,8 @@ bool ED_region_is_overlap(int spacetype, int regiontype) RGN_TYPE_UI, RGN_TYPE_TOOL_PROPS, RGN_TYPE_FOOTER, - RGN_TYPE_TOOL_HEADER)) { + RGN_TYPE_TOOL_HEADER, + RGN_TYPE_ASSET_SHELF)) { return true; } } diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 14ed5987cc7..0acae5b5ea5 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -1435,7 +1435,8 @@ static bScreen *screen_state_to_nonnormal(bContext *C, RGN_TYPE_FOOTER, RGN_TYPE_TOOLS, RGN_TYPE_NAV_BAR, - RGN_TYPE_EXECUTE)) { + RGN_TYPE_EXECUTE, + RGN_TYPE_ASSET_SHELF)) { region->flag |= RGN_FLAG_HIDDEN; } } diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc index 05fb0c6a720..b2da6630b38 100644 --- a/source/blender/editors/space_view3d/space_view3d.cc +++ b/source/blender/editors/space_view3d/space_view3d.cc @@ -289,6 +289,14 @@ static SpaceLink *view3d_create(const ScrArea * /*area*/, const Scene *scene) region->alignment = RGN_ALIGN_RIGHT; region->flag = RGN_FLAG_HIDDEN; + /* asset shelf */ + region = MEM_cnew("asset shelf for view3d"); + + BLI_addtail(&v3d->regionbase, region); + region->regiontype = RGN_TYPE_ASSET_SHELF; + region->alignment = RGN_ALIGN_BOTTOM; + region->flag = RGN_FLAG_HIDDEN; + /* main region */ region = MEM_cnew("main region for view3d"); @@ -2137,6 +2145,15 @@ void ED_spacetype_view3d() art->draw = view3d_header_region_draw; BLI_addhead(&st->regiontypes, art); + /* regions: asset shelf */ + art = MEM_cnew("spacetype view3d asset shelf region"); + art->regionid = RGN_TYPE_ASSET_SHELF; + art->prefsizey = HEADERY * 4; + art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER; + art->init = view3d_header_region_init; + art->draw = view3d_header_region_draw; + BLI_addhead(&st->regiontypes, art); + /* regions: hud */ art = ED_area_type_hud(st->spaceid); BLI_addhead(&st->regiontypes, art); diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 4d4bd9ef775..4feab97b6b7 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -657,8 +657,9 @@ typedef enum eRegion_Type { /* Region type used exclusively by internal code and add-ons to register draw callbacks to the XR * context (surface, mirror view). Does not represent any real region. */ RGN_TYPE_XR = 13, + RGN_TYPE_ASSET_SHELF = 14, -#define RGN_TYPE_NUM (RGN_TYPE_XR + 1) +#define RGN_TYPE_NUM (RGN_TYPE_ASSET_SHELF + 1) } eRegion_Type; /* use for function args */ diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c index a65bd613ecf..4d7354421ed 100644 --- a/source/blender/makesrna/intern/rna_screen.c +++ b/source/blender/makesrna/intern/rna_screen.c @@ -26,6 +26,7 @@ const EnumPropertyItem rna_enum_region_type_items[] = { {RGN_TYPE_UI, "UI", 0, "UI", ""}, {RGN_TYPE_TOOLS, "TOOLS", 0, "Tools", ""}, {RGN_TYPE_TOOL_PROPS, "TOOL_PROPS", 0, "Tool Properties", ""}, + {RGN_TYPE_ASSET_SHELF, "ASSET_SHELF", 0, "Asset Shelf", ""}, {RGN_TYPE_PREVIEW, "PREVIEW", 0, "Preview", ""}, {RGN_TYPE_HUD, "HUD", 0, "Floating Region", ""}, {RGN_TYPE_NAV_BAR, "NAVIGATION_BAR", 0, "Navigation Bar", ""}, -- 2.30.2 From 8ddf492e7cf677f34315dcf5a8726ce45982ab7a Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 17 Jan 2023 15:18:17 +0100 Subject: [PATCH 13/49] Basic asset shelf prototype Adds the necessary bits to be able to show an asset shelf template via the pose library add-on. --- .../blenloader/intern/versioning_300.cc | 8 ++ .../blenloader/intern/versioning_defaults.cc | 3 + source/blender/editors/asset/ED_asset_list.h | 3 +- .../editors/asset/intern/asset_list.cc | 13 +-- source/blender/editors/include/ED_screen.h | 2 + source/blender/editors/include/UI_interface.h | 4 + .../blender/editors/interface/CMakeLists.txt | 1 + .../editors/interface/interface_layout.cc | 7 +- .../interface_template_asset_shelf.cc | 101 ++++++++++++++++++ .../interface_template_asset_view.cc | 5 +- source/blender/editors/screen/area.c | 15 ++- .../editors/space_view3d/space_view3d.cc | 5 +- source/blender/makesdna/DNA_asset_defaults.h | 2 +- source/blender/makesrna/intern/rna_ui_api.c | 19 ++++ 14 files changed, 168 insertions(+), 20 deletions(-) create mode 100644 source/blender/editors/interface/interface_template_asset_shelf.cc diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 19f075241af..7393f4fc171 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -3926,10 +3926,18 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) if (new_asset_shelf != nullptr) { new_asset_shelf->alignment = RGN_ALIGN_BOTTOM; new_asset_shelf->flag |= RGN_FLAG_HIDDEN; + new_asset_shelf->flag = RGN_FLAG_HIDDEN | RGN_FLAG_DYNAMIC_SIZE; } } } } } + + /* Should we really use the "All" library by default? Consider loading time and memory usage. + */ + LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) { + workspace->asset_library_ref.type = ASSET_LIBRARY_ALL; + workspace->asset_library_ref.custom_library_index = -1; + } } } diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index f6fe45ddcdf..fb5599d1d77 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -260,6 +260,9 @@ void BLO_update_defaults_workspace(WorkSpace *workspace, const char *app_templat BKE_workspace_tool_remove(workspace, static_cast(workspace->tools.first)); } + workspace->asset_library_ref.type = ASSET_LIBRARY_ALL; + workspace->asset_library_ref.custom_library_index = -1; + /* For 2D animation template. */ if (STREQ(workspace->id.name + 2, "Drawing")) { workspace->object_mode = OB_MODE_PAINT_GPENCIL; diff --git a/source/blender/editors/asset/ED_asset_list.h b/source/blender/editors/asset/ED_asset_list.h index 635dc3bff32..b33f909c50b 100644 --- a/source/blender/editors/asset/ED_asset_list.h +++ b/source/blender/editors/asset/ED_asset_list.h @@ -55,8 +55,7 @@ struct ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle); /** * \return True if the region needs a UI redraw. */ -bool ED_assetlist_listen(const struct AssetLibraryReference *library_reference, - const struct wmNotifier *notifier); +bool ED_assetlist_listen(const struct wmNotifier *notifier); /** * \return The number of assets stored in the asset list for \a library_reference, or -1 if there * is no list fetched for it. diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc index 64934316413..3b3a434f0cc 100644 --- a/source/blender/editors/asset/intern/asset_list.cc +++ b/source/blender/editors/asset/intern/asset_list.cc @@ -116,7 +116,7 @@ class AssetList : NonCopyable { bool isLoaded() const; asset_system::AssetLibrary *asset_library() const; void iterate(AssetListIterFn fn) const; - bool listen(const wmNotifier ¬ifier) const; + static bool listen(const wmNotifier ¬ifier); int size() const; void tagMainDataDirty() const; void remapID(ID *id_old, ID *id_new) const; @@ -249,7 +249,7 @@ void AssetList::clear(bContext *C) /** * \return True if the asset-list needs a UI redraw. */ -bool AssetList::listen(const wmNotifier ¬ifier) const +bool AssetList::listen(const wmNotifier ¬ifier) { switch (notifier.category) { case NC_ID: { @@ -481,14 +481,9 @@ ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle) return filelist_geticon_image_ex(asset_handle->file_data); } -bool ED_assetlist_listen(const AssetLibraryReference *library_reference, - const wmNotifier *notifier) +bool ED_assetlist_listen(const wmNotifier *notifier) { - AssetList *list = AssetListStorage::lookup_list(*library_reference); - if (list) { - return list->listen(*notifier); - } - return false; + return AssetList::listen(*notifier); } int ED_assetlist_size(const AssetLibraryReference *library_reference) diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 6dfaab663f6..a85acb500c3 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -122,6 +122,8 @@ void ED_region_header(const struct bContext *C, struct ARegion *region); void ED_region_header_layout(const struct bContext *C, struct ARegion *region); void ED_region_header_draw(const struct bContext *C, struct ARegion *region); +void ED_region_asset_shelf_listen(const struct wmRegionListenerParams *params); + void ED_region_cursor_set(struct wmWindow *win, struct ScrArea *area, struct ARegion *region); /** * Exported to all editors, uses fading default. diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 0705052921e..41fe5f88373 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2161,6 +2161,7 @@ int uiLayoutGetAlignment(uiLayout *layout); bool uiLayoutGetFixedSize(uiLayout *layout); bool uiLayoutGetKeepAspect(uiLayout *layout); int uiLayoutGetWidth(uiLayout *layout); +int uiLayoutGetRootHeight(uiLayout *layout); float uiLayoutGetScaleX(uiLayout *layout); float uiLayoutGetScaleY(uiLayout *layout); float uiLayoutGetUnitsX(uiLayout *layout); @@ -2598,6 +2599,9 @@ void uiTemplateAssetView(struct uiLayout *layout, struct PointerRNA *r_activate_op_properties, const char *drag_opname, struct PointerRNA *r_drag_op_properties); +void uiTemplateAssetShelf(uiLayout *layout, + const struct bContext *C, + const struct AssetFilterSettings *filter_settings); /** * \return: A RNA pointer for the operator properties. diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index e3c809b6ccd..e07e2adc9aa 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -60,6 +60,7 @@ set(SRC interface_region_tooltip.cc interface_regions.cc interface_style.cc + interface_template_asset_shelf.cc interface_template_asset_view.cc interface_template_attribute_search.cc interface_template_list.cc diff --git a/source/blender/editors/interface/interface_layout.cc b/source/blender/editors/interface/interface_layout.cc index f4b96899545..f870bb5e434 100644 --- a/source/blender/editors/interface/interface_layout.cc +++ b/source/blender/editors/interface/interface_layout.cc @@ -4088,7 +4088,7 @@ static void ui_litem_layout_box(uiLayout *litem) const uiStyle *style = litem->root->style; int boxspace = style->boxspace; - if (litem->root->type == UI_LAYOUT_HEADER) { + if (litem->root->type == UI_LAYOUT_HEADER && false) { boxspace = 0; } @@ -5117,6 +5117,11 @@ int uiLayoutGetWidth(uiLayout *layout) return layout->w; } +int uiLayoutGetRootHeight(uiLayout *layout) +{ + return layout->root->layout->h; +} + float uiLayoutGetScaleX(uiLayout *layout) { return layout->scale[0]; diff --git a/source/blender/editors/interface/interface_template_asset_shelf.cc b/source/blender/editors/interface/interface_template_asset_shelf.cc new file mode 100644 index 00000000000..26676a9d8e3 --- /dev/null +++ b/source/blender/editors/interface/interface_template_asset_shelf.cc @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + */ + +#include "BKE_context.h" + +#include "DNA_space_types.h" + +#include "ED_asset.h" + +#include "UI_interface.h" +#include "UI_resources.h" +#include "interface_intern.hh" + +/* TODO copy of #asset_view_item_but_drag_set(). */ +static void asset_tile_but_drag_set(uiBut &but, AssetHandle &asset_handle) +{ + ID *id = ED_asset_handle_get_local_id(&asset_handle); + if (id != nullptr) { + UI_but_drag_set_id(&but, id); + return; + } + + char blend_path[FILE_MAX_LIBEXTRA]; + /* Context can be null here, it's only needed for a File Browser specific hack that should go + * away before too long. */ + ED_asset_handle_get_full_library_path(&asset_handle, blend_path); + + if (blend_path[0]) { + ImBuf *imbuf = ED_assetlist_asset_image_get(&asset_handle); + UI_but_drag_set_asset(&but, + &asset_handle, + BLI_strdup(blend_path), + FILE_ASSET_IMPORT_APPEND, + ED_asset_handle_get_preview_icon_id(&asset_handle), + imbuf, + 1.0f); + } +} + +static void asset_tile_draw(uiLayout &layout, + AssetHandle &asset_handle, + const int width, + const int height, + const bool show_names) +{ + uiBlock *block = uiLayoutGetBlock(&layout); + uiBut *but = uiDefIconTextBut(block, + UI_BTYPE_PREVIEW_TILE, + 0, + ED_asset_handle_get_preview_icon_id(&asset_handle), + show_names ? ED_asset_handle_get_name(&asset_handle) : "", + 0, + 0, + width, + height, + nullptr, + 0, + 0, + 0, + 0, + ""); + ui_def_but_icon(but, + ED_asset_handle_get_preview_icon_id(&asset_handle), + /* NOLINTNEXTLINE: bugprone-suspicious-enum-usage */ + UI_HAS_ICON | UI_BUT_ICON_PREVIEW); + asset_tile_but_drag_set(*but, asset_handle); +} + +void uiTemplateAssetShelf(uiLayout *layout, + const bContext *C, + const AssetFilterSettings *filter_settings) +{ + const AssetLibraryReference *library_ref = CTX_wm_asset_library_ref(C); + + ED_assetlist_storage_fetch(library_ref, C); + ED_assetlist_ensure_previews_job(library_ref, C); + uiLayoutSetScaleX(layout, 1.0f); + uiLayoutSetScaleY(layout, 1.0f); + + const bool show_names = true; + const int height = uiLayoutGetRootHeight(layout) - UI_style_get_dpi()->boxspace * 2; + /* Width is derived from the height. It's the height without the space for the name (if there is + * any). */ + const int width = height - (show_names ? 0 : UI_UNIT_Y); + + uiLayout *box = uiLayoutBox(layout); + uiLayout *row = uiLayoutRow(box, false); + + ED_assetlist_iterate(*library_ref, [&](AssetHandle asset) { + if (!ED_asset_filter_matches_asset(filter_settings, &asset)) { + /* Don't do anything else, but return true to continue iterating. */ + return true; + } + + asset_tile_draw(*row, asset, width, height, show_names); + return true; + }); +} diff --git a/source/blender/editors/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc index 3e707031dd2..0909b25e2bf 100644 --- a/source/blender/editors/interface/interface_template_asset_view.cc +++ b/source/blender/editors/interface/interface_template_asset_view.cc @@ -108,9 +108,8 @@ static void asset_view_draw_item(uiList *ui_list, } } -static void asset_view_listener(uiList *ui_list, wmRegionListenerParams *params) +static void asset_view_listener(uiList * /*ui_list*/, wmRegionListenerParams *params) { - AssetViewListData *list_data = (AssetViewListData *)ui_list->dyn_data->customdata; const wmNotifier *notifier = params->notifier; switch (notifier->category) { @@ -122,7 +121,7 @@ static void asset_view_listener(uiList *ui_list, wmRegionListenerParams *params) } } - if (ED_assetlist_listen(&list_data->asset_library_ref, params->notifier)) { + if (ED_assetlist_listen(params->notifier)) { ED_region_tag_redraw(params->region); } } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 5fc817f3067..33ccb91027b 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -33,6 +33,7 @@ #include "WM_toolsystem.h" #include "WM_types.h" +#include "ED_asset.h" #include "ED_buttons.h" #include "ED_screen.h" #include "ED_screen_types.h" @@ -3311,12 +3312,15 @@ void ED_region_header_layout(const bContext *C, ARegion *region) bool region_layout_based = region->flag & RGN_FLAG_DYNAMIC_SIZE; /* Height of buttons and scaling needed to achieve it. */ - const int buttony = min_ii(UI_UNIT_Y, region->winy - 2 * UI_DPI_FAC); + const bool is_fixed_header_height = region->type->prefsizey == HEADERY; + const int buttony = is_fixed_header_height ? UI_UNIT_Y : + region->winy - 2 * UI_DPI_FAC - UI_HEADER_OFFSET; const float buttony_scale = buttony / (float)UI_UNIT_Y; /* Vertically center buttons. */ int xco = UI_HEADER_OFFSET; - int yco = buttony + (region->winy - buttony) / 2; + int yco = is_fixed_header_height ? buttony + (region->winy - buttony) / 2 : + buttony + UI_HEADER_OFFSET / 2; int maxco = xco; /* XXX workaround for 1 px alignment issue. Not sure what causes it... @@ -3422,6 +3426,13 @@ void ED_region_header_init(ARegion *region) UI_view2d_region_reinit(®ion->v2d, V2D_COMMONVIEW_HEADER, region->winx, region->winy); } +void ED_region_asset_shelf_listen(const wmRegionListenerParams *params) +{ + if (ED_assetlist_listen(params->notifier)) { + ED_region_tag_redraw_no_rebuild(params->region); + } +} + int ED_area_headersize(void) { /* Accommodate widget and padding. */ diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc index b2da6630b38..89ef0f432a3 100644 --- a/source/blender/editors/space_view3d/space_view3d.cc +++ b/source/blender/editors/space_view3d/space_view3d.cc @@ -2148,10 +2148,11 @@ void ED_spacetype_view3d() /* regions: asset shelf */ art = MEM_cnew("spacetype view3d asset shelf region"); art->regionid = RGN_TYPE_ASSET_SHELF; - art->prefsizey = HEADERY * 4; + art->prefsizey = HEADERY * 3.5f; art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER; + art->listener = ED_region_asset_shelf_listen; art->init = view3d_header_region_init; - art->draw = view3d_header_region_draw; + art->draw = ED_region_header; BLI_addhead(&st->regiontypes, art); /* regions: hud */ diff --git a/source/blender/makesdna/DNA_asset_defaults.h b/source/blender/makesdna/DNA_asset_defaults.h index 63239f23e4e..14134ee4395 100644 --- a/source/blender/makesdna/DNA_asset_defaults.h +++ b/source/blender/makesdna/DNA_asset_defaults.h @@ -20,7 +20,7 @@ #define _DNA_DEFAULT_AssetLibraryReference \ { \ - .type = ASSET_LIBRARY_LOCAL, \ + .type = ASSET_LIBRARY_ALL, \ /* Not needed really (should be ignored for anything but #ASSET_LIBRARY_CUSTOM), but helps debugging. */ \ .custom_library_index = -1, \ } diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index 9a75be07cf4..34bacf393df 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -714,6 +714,15 @@ static const EnumPropertyItem *rna_uiTemplateAssetView_filter_id_types_itemf( return items; } +static void rna_uiTemplateAssetShelf(uiLayout *layout, bContext *C, int filter_id_types) +{ + AssetFilterSettings filter_settings = { + .id_types = filter_id_types ? filter_id_types : FILTER_ID_ALL, + }; + + uiTemplateAssetShelf(layout, C, &filter_settings); +} + static uiLayout *rna_uiLayoutRowWithHeading( uiLayout *layout, bool align, const char *heading, const char *heading_ctxt, bool translate) { @@ -1958,6 +1967,16 @@ void RNA_api_ui_layout(StructRNA *srna) "Operator properties to fill in for the custom drag operator passed to the template"); RNA_def_parameter_flags(parm, 0, PARM_RNAPTR); RNA_def_function_output(func, parm); + + func = RNA_def_function(srna, "template_asset_shelf", "rna_uiTemplateAssetShelf"); + RNA_def_function_ui_description(func, + "Item. A list of assets in a horizontally scrollable layout. " + "Meant to be placed in a 'ASSET_SHELF' region"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + parm = RNA_def_property(func, "filter_id_types", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(parm, DummyRNA_NULL_items); + RNA_def_property_enum_funcs(parm, NULL, NULL, "rna_uiTemplateAssetView_filter_id_types_itemf"); + RNA_def_property_flag(parm, PROP_ENUM_FLAG); } #endif -- 2.30.2 From b3ee7ad2ccbf9aba225b55d6e46b5d7a73ef26d9 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 17 Jan 2023 16:24:03 +0100 Subject: [PATCH 14/49] Extend asset shelf region with a region for the catalogs & options The new region is empty, except of a dummy button. --- .../blenloader/intern/versioning_300.cc | 30 +++++++---- source/blender/editors/asset/CMakeLists.txt | 2 + source/blender/editors/asset/ED_asset_shelf.h | 28 ++++++++++ .../editors/asset/intern/asset_shelf.cc | 52 +++++++++++++++++++ source/blender/editors/include/ED_screen.h | 3 +- .../editors/interface/interface_handlers.cc | 3 +- source/blender/editors/interface/resources.cc | 20 +++++-- source/blender/editors/screen/area.c | 19 +++---- source/blender/editors/screen/screen_edit.c | 3 +- source/blender/editors/screen/screen_ops.c | 16 +++--- .../editors/space_view3d/CMakeLists.txt | 1 + .../editors/space_view3d/space_view3d.cc | 21 +++++++- source/blender/makesdna/DNA_screen_types.h | 7 +-- source/blender/makesrna/intern/rna_screen.c | 1 + .../windowmanager/intern/wm_event_system.cc | 1 + 15 files changed, 170 insertions(+), 37 deletions(-) create mode 100644 source/blender/editors/asset/ED_asset_shelf.h create mode 100644 source/blender/editors/asset/intern/asset_shelf.cc diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 7393f4fc171..460ee3b8719 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -3918,15 +3918,27 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) if (sl->spacetype == SPACE_VIEW3D) { ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : &sl->regionbase; - ARegion *new_asset_shelf = do_versions_add_region_if_not_found( - regionbase, - RGN_TYPE_ASSET_SHELF, - "asset shelf for view3d (versioning)", - RGN_TYPE_UI); - if (new_asset_shelf != nullptr) { - new_asset_shelf->alignment = RGN_ALIGN_BOTTOM; - new_asset_shelf->flag |= RGN_FLAG_HIDDEN; - new_asset_shelf->flag = RGN_FLAG_HIDDEN | RGN_FLAG_DYNAMIC_SIZE; + { + ARegion *new_asset_shelf_footer = do_versions_add_region_if_not_found( + regionbase, + RGN_TYPE_ASSET_SHELF_FOOTER, + "asset shelf footer for view3d (versioning)", + RGN_TYPE_UI); + if (new_asset_shelf_footer != nullptr) { + new_asset_shelf_footer->alignment = RGN_ALIGN_BOTTOM; + new_asset_shelf_footer->flag |= RGN_FLAG_HIDDEN; + } + } + { + ARegion *new_asset_shelf = do_versions_add_region_if_not_found( + regionbase, + RGN_TYPE_ASSET_SHELF, + "asset shelf for view3d (versioning)", + RGN_TYPE_ASSET_SHELF_FOOTER); + if (new_asset_shelf != nullptr) { + new_asset_shelf->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV; + new_asset_shelf->flag |= RGN_FLAG_DYNAMIC_SIZE; + } } } } diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt index a4d05ebaffe..3e45a0bccdf 100644 --- a/source/blender/editors/asset/CMakeLists.txt +++ b/source/blender/editors/asset/CMakeLists.txt @@ -30,6 +30,7 @@ set(SRC intern/asset_list.cc intern/asset_mark_clear.cc intern/asset_ops.cc + intern/asset_shelf.cc intern/asset_temp_id_consumer.cc intern/asset_type.cc @@ -42,6 +43,7 @@ set(SRC ED_asset_list.h ED_asset_list.hh ED_asset_mark_clear.h + ED_asset_shelf.h ED_asset_temp_id_consumer.h ED_asset_type.h intern/asset_library_reference.hh diff --git a/source/blender/editors/asset/ED_asset_shelf.h b/source/blender/editors/asset/ED_asset_shelf.h new file mode 100644 index 00000000000..5d5a483ef0b --- /dev/null +++ b/source/blender/editors/asset/ED_asset_shelf.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edasset + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct ARegionType; +struct bContext; +struct wmWindowManager; + +void ED_region_asset_shelf_footer_init(struct wmWindowManager *wm, struct ARegion *region); +void ED_region_asset_shelf_footer(const struct bContext *C, struct ARegion *region); + +void ED_region_asset_shelf_listen(const struct wmRegionListenerParams *params); + +void ED_asset_shelf_footer_register(struct ARegionType *region_type, + const char *idname, + const int space_type); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/asset/intern/asset_shelf.cc b/source/blender/editors/asset/intern/asset_shelf.cc new file mode 100644 index 00000000000..42396d5d435 --- /dev/null +++ b/source/blender/editors/asset/intern/asset_shelf.cc @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edasset + */ + +#include "BKE_screen.h" + +#include "DNA_screen_types.h" + +#include "ED_asset_list.h" +#include "ED_screen.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "ED_asset_shelf.h" + +static void asset_shelf_draw(const bContext * /*C*/, Header *header) +{ + uiLayout *layout = header->layout; + uiItemL(layout, "Fooo\n", ICON_ASSET_MANAGER); +} + +void ED_region_asset_shelf_listen(const wmRegionListenerParams *params) +{ + if (ED_assetlist_listen(params->notifier)) { + ED_region_tag_redraw_no_rebuild(params->region); + } +} + +void ED_region_asset_shelf_footer_init(wmWindowManager * /*wm*/, ARegion *region) +{ + ED_region_header_init(region); +} + +void ED_region_asset_shelf_footer(const bContext *C, ARegion *region) +{ + ED_region_header(C, region); +} + +void ED_asset_shelf_footer_register(ARegionType *region_type, + const char *idname, + const int space_type) +{ + HeaderType *ht = MEM_cnew(__func__); + strcpy(ht->idname, idname); + ht->space_type = space_type; + ht->region_type = RGN_TYPE_ASSET_SHELF_FOOTER; + ht->draw = asset_shelf_draw; + BLI_addtail(®ion_type->headertypes, ht); +} diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index a85acb500c3..b71fa39543c 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -122,8 +122,6 @@ void ED_region_header(const struct bContext *C, struct ARegion *region); void ED_region_header_layout(const struct bContext *C, struct ARegion *region); void ED_region_header_draw(const struct bContext *C, struct ARegion *region); -void ED_region_asset_shelf_listen(const struct wmRegionListenerParams *params); - void ED_region_cursor_set(struct wmWindow *win, struct ScrArea *area, struct ARegion *region); /** * Exported to all editors, uses fading default. @@ -703,6 +701,7 @@ enum { ED_KEYMAP_FOOTER = (1 << 9), ED_KEYMAP_GPENCIL = (1 << 10), ED_KEYMAP_NAVBAR = (1 << 11), + ED_KEYMAP_ASSET_SHELF_FOOTER = (1 << 12), }; /** #SCREEN_OT_space_context_cycle direction. */ diff --git a/source/blender/editors/interface/interface_handlers.cc b/source/blender/editors/interface/interface_handlers.cc index 44a14f254c0..c9214bf2082 100644 --- a/source/blender/editors/interface/interface_handlers.cc +++ b/source/blender/editors/interface/interface_handlers.cc @@ -2085,7 +2085,8 @@ static bool ui_but_drag_init(bContext *C, RGN_TYPE_NAV_BAR, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER, - RGN_TYPE_FOOTER)) { + RGN_TYPE_FOOTER, + RGN_TYPE_ASSET_SHELF_FOOTER)) { const int region_alignment = RGN_ALIGN_ENUM_FROM_MASK(data->region->alignment); int lock_axis = -1; diff --git a/source/blender/editors/interface/resources.cc b/source/blender/editors/interface/resources.cc index 606f9d5a11f..dd0b5f5ae84 100644 --- a/source/blender/editors/interface/resources.cc +++ b/source/blender/editors/interface/resources.cc @@ -153,7 +153,10 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) else if (g_theme_state.regionid == RGN_TYPE_CHANNELS) { cp = ts->list; } - else if (ELEM(g_theme_state.regionid, RGN_TYPE_HEADER, RGN_TYPE_FOOTER)) { + else if (ELEM(g_theme_state.regionid, + RGN_TYPE_HEADER, + RGN_TYPE_FOOTER, + RGN_TYPE_ASSET_SHELF_FOOTER)) { cp = ts->header; } else if (g_theme_state.regionid == RGN_TYPE_NAV_BAR) { @@ -187,7 +190,10 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) else if (g_theme_state.regionid == RGN_TYPE_CHANNELS) { cp = ts->list_text; } - else if (ELEM(g_theme_state.regionid, RGN_TYPE_HEADER, RGN_TYPE_FOOTER)) { + else if (ELEM(g_theme_state.regionid, + RGN_TYPE_HEADER, + RGN_TYPE_FOOTER, + RGN_TYPE_ASSET_SHELF_FOOTER)) { cp = ts->header_text; } else { @@ -201,7 +207,10 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) else if (g_theme_state.regionid == RGN_TYPE_CHANNELS) { cp = ts->list_text_hi; } - else if (ELEM(g_theme_state.regionid, RGN_TYPE_HEADER, RGN_TYPE_FOOTER)) { + else if (ELEM(g_theme_state.regionid, + RGN_TYPE_HEADER, + RGN_TYPE_FOOTER, + RGN_TYPE_ASSET_SHELF_FOOTER)) { cp = ts->header_text_hi; } else { @@ -215,7 +224,10 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) else if (g_theme_state.regionid == RGN_TYPE_CHANNELS) { cp = ts->list_title; } - else if (ELEM(g_theme_state.regionid, RGN_TYPE_HEADER, RGN_TYPE_FOOTER)) { + else if (ELEM(g_theme_state.regionid, + RGN_TYPE_HEADER, + RGN_TYPE_FOOTER, + RGN_TYPE_ASSET_SHELF_FOOTER)) { cp = ts->header_title; } else { diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 33ccb91027b..3b6e25118e0 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1264,7 +1264,8 @@ bool ED_region_is_overlap(int spacetype, int regiontype) RGN_TYPE_TOOL_PROPS, RGN_TYPE_FOOTER, RGN_TYPE_TOOL_HEADER, - RGN_TYPE_ASSET_SHELF)) { + RGN_TYPE_ASSET_SHELF, + RGN_TYPE_ASSET_SHELF_FOOTER)) { return true; } } @@ -1339,6 +1340,9 @@ static void region_rect_recursive( else if (region->regiontype == RGN_TYPE_FOOTER) { prefsizey = ED_area_footersize(); } + else if (region->regiontype == RGN_TYPE_ASSET_SHELF_FOOTER) { + prefsizey = ED_area_footersize(); + } else if (ED_area_is_global(area)) { prefsizey = ED_region_global_size_y(); } @@ -1746,6 +1750,11 @@ static void ed_default_handlers( wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Region Context Menu", 0, 0); WM_event_add_keymap_handler(®ion->handlers, keymap); } + if (flag & ED_KEYMAP_ASSET_SHELF_FOOTER) { + /* standard keymap for Navigation bar regions */ + wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Region Context Menu", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + } /* Keep last because of LMB/RMB handling, see: T57527. */ if (flag & ED_KEYMAP_GPENCIL) { @@ -3425,14 +3434,6 @@ void ED_region_header_init(ARegion *region) { UI_view2d_region_reinit(®ion->v2d, V2D_COMMONVIEW_HEADER, region->winx, region->winy); } - -void ED_region_asset_shelf_listen(const wmRegionListenerParams *params) -{ - if (ED_assetlist_listen(params->notifier)) { - ED_region_tag_redraw_no_rebuild(params->region); - } -} - int ED_area_headersize(void) { /* Accommodate widget and padding. */ diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index a62af2b3aef..52cb29c8982 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -1436,7 +1436,8 @@ static bScreen *screen_state_to_nonnormal(bContext *C, RGN_TYPE_TOOLS, RGN_TYPE_NAV_BAR, RGN_TYPE_EXECUTE, - RGN_TYPE_ASSET_SHELF)) { + RGN_TYPE_ASSET_SHELF, + RGN_TYPE_ASSET_SHELF_FOOTER)) { region->flag |= RGN_FLAG_HIDDEN; } } diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index a064f4632b5..20f096277f5 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -2625,15 +2625,19 @@ static int area_max_regionsize(ScrArea *area, ARegion *scale_region, AZEdge edge dist -= region->winx; } else if (scale_region->alignment == RGN_ALIGN_TOP && - (region->alignment == RGN_ALIGN_BOTTOM || - ELEM( - region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER, RGN_TYPE_FOOTER))) { + (region->alignment == RGN_ALIGN_BOTTOM || ELEM(region->regiontype, + RGN_TYPE_HEADER, + RGN_TYPE_TOOL_HEADER, + RGN_TYPE_FOOTER, + RGN_TYPE_ASSET_SHELF_FOOTER))) { dist -= region->winy; } else if (scale_region->alignment == RGN_ALIGN_BOTTOM && - (region->alignment == RGN_ALIGN_TOP || - ELEM( - region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER, RGN_TYPE_FOOTER))) { + (region->alignment == RGN_ALIGN_TOP || ELEM(region->regiontype, + RGN_TYPE_HEADER, + RGN_TYPE_TOOL_HEADER, + RGN_TYPE_FOOTER, + RGN_TYPE_ASSET_SHELF_FOOTER))) { dist -= region->winy; } } diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt index 579e27b9259..b7af4ed20ea 100644 --- a/source/blender/editors/space_view3d/CMakeLists.txt +++ b/source/blender/editors/space_view3d/CMakeLists.txt @@ -2,6 +2,7 @@ set(INC ../include + ../asset ../../blenfont ../../blenkernel ../../blenlib diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc index 89ef0f432a3..7b7a9408135 100644 --- a/source/blender/editors/space_view3d/space_view3d.cc +++ b/source/blender/editors/space_view3d/space_view3d.cc @@ -47,6 +47,7 @@ #include "BKE_viewer_path.h" #include "BKE_workspace.h" +#include "ED_asset_shelf.h" #include "ED_object.h" #include "ED_outliner.h" #include "ED_render.h" @@ -289,13 +290,20 @@ static SpaceLink *view3d_create(const ScrArea * /*area*/, const Scene *scene) region->alignment = RGN_ALIGN_RIGHT; region->flag = RGN_FLAG_HIDDEN; + /* asset shelf footer */ + region = MEM_cnew("asset shelf footer for view3d"); + BLI_addtail(&v3d->regionbase, region); + region->regiontype = RGN_TYPE_ASSET_SHELF_FOOTER; + region->alignment = RGN_ALIGN_BOTTOM; + region->flag = RGN_FLAG_HIDDEN; + /* asset shelf */ region = MEM_cnew("asset shelf for view3d"); BLI_addtail(&v3d->regionbase, region); region->regiontype = RGN_TYPE_ASSET_SHELF; - region->alignment = RGN_ALIGN_BOTTOM; - region->flag = RGN_FLAG_HIDDEN; + region->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV; + region->flag |= RGN_FLAG_DYNAMIC_SIZE; /* main region */ region = MEM_cnew("main region for view3d"); @@ -2154,6 +2162,15 @@ void ED_spacetype_view3d() art->init = view3d_header_region_init; art->draw = ED_region_header; BLI_addhead(&st->regiontypes, art); + /* regions: asset shelf footer */ + art = MEM_cnew("spacetype view3d asset shelf footer region"); + art->regionid = RGN_TYPE_ASSET_SHELF_FOOTER; + art->prefsizey = HEADERY; + art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FOOTER; + art->init = ED_region_asset_shelf_footer_init; + art->draw = ED_region_asset_shelf_footer; + BLI_addhead(&st->regiontypes, art); + ED_asset_shelf_footer_register(art, "VIEW3D_HT_asset_shelf", SPACE_VIEW3D); /* regions: hud */ art = ED_area_type_hud(st->spaceid); diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 4feab97b6b7..0b4e3ec5d15 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -658,8 +658,9 @@ typedef enum eRegion_Type { * context (surface, mirror view). Does not represent any real region. */ RGN_TYPE_XR = 13, RGN_TYPE_ASSET_SHELF = 14, + RGN_TYPE_ASSET_SHELF_FOOTER = 15, -#define RGN_TYPE_NUM (RGN_TYPE_ASSET_SHELF + 1) +#define RGN_TYPE_NUM (RGN_TYPE_ASSET_SHELF_FOOTER + 1) } eRegion_Type; /* use for function args */ @@ -670,8 +671,8 @@ typedef enum eRegion_Type { /* Check for any kind of header region. */ #define RGN_TYPE_IS_HEADER_ANY(regiontype) \ - (((1 << (regiontype)) & \ - ((1 << RGN_TYPE_HEADER) | 1 << (RGN_TYPE_TOOL_HEADER) | (1 << RGN_TYPE_FOOTER))) != 0) + (((1 << (regiontype)) & ((1 << RGN_TYPE_HEADER) | 1 << (RGN_TYPE_TOOL_HEADER) | \ + (1 << RGN_TYPE_FOOTER) | (1 << RGN_TYPE_ASSET_SHELF_FOOTER))) != 0) /** #ARegion.alignment */ enum { diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c index 4d7354421ed..954ba2e1aeb 100644 --- a/source/blender/makesrna/intern/rna_screen.c +++ b/source/blender/makesrna/intern/rna_screen.c @@ -27,6 +27,7 @@ const EnumPropertyItem rna_enum_region_type_items[] = { {RGN_TYPE_TOOLS, "TOOLS", 0, "Tools", ""}, {RGN_TYPE_TOOL_PROPS, "TOOL_PROPS", 0, "Tool Properties", ""}, {RGN_TYPE_ASSET_SHELF, "ASSET_SHELF", 0, "Asset Shelf", ""}, + {RGN_TYPE_ASSET_SHELF_FOOTER, "ASSET_SHELF_FOOTER", 0, "Asset Shelf Footer", ""}, {RGN_TYPE_PREVIEW, "PREVIEW", 0, "Preview", ""}, {RGN_TYPE_HUD, "HUD", 0, "Floating Region", ""}, {RGN_TYPE_NAV_BAR, "NAVIGATION_BAR", 0, "Navigation Bar", ""}, diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index 05fe351c385..53706802818 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -5982,6 +5982,7 @@ void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win) RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER, RGN_TYPE_FOOTER, + RGN_TYPE_ASSET_SHELF_FOOTER, RGN_TYPE_TEMPORARY, RGN_TYPE_HUD)) { return; -- 2.30.2 From 4fa69fbda8871753fcd2d1ae09d62d8dac880a47 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 19 Jan 2023 11:50:45 +0100 Subject: [PATCH 15/49] Popup to select which catalogs are displayed in the asset shelf footer The selected catalogs are currently listed as simple labels in the footer, just for testing. --- source/blender/editors/asset/CMakeLists.txt | 2 + source/blender/editors/asset/ED_asset_shelf.h | 42 +- .../editors/asset/intern/asset_shelf.cc | 389 +++++++++++++++++- source/blender/editors/include/UI_interface.h | 1 + .../interface/interface_region_popup.cc | 5 + .../editors/interface/views/tree_view.cc | 2 +- .../editors/space_view3d/space_view3d.cc | 34 +- source/blender/makesdna/DNA_screen_types.h | 5 + source/blender/makesdna/DNA_view3d_types.h | 2 + source/blender/makesrna/intern/rna_ui.c | 7 + source/blender/windowmanager/WM_types.h | 1 + 11 files changed, 472 insertions(+), 18 deletions(-) diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt index 3e45a0bccdf..bc6b9a474b8 100644 --- a/source/blender/editors/asset/CMakeLists.txt +++ b/source/blender/editors/asset/CMakeLists.txt @@ -13,6 +13,8 @@ set(INC ../../windowmanager ../../../../intern/clog ../../../../intern/guardedalloc + # dna_type_offsets.h + ${CMAKE_CURRENT_BINARY_DIR}/../../makesdna/intern # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna ) diff --git a/source/blender/editors/asset/ED_asset_shelf.h b/source/blender/editors/asset/ED_asset_shelf.h index 5d5a483ef0b..8298cf8f042 100644 --- a/source/blender/editors/asset/ED_asset_shelf.h +++ b/source/blender/editors/asset/ED_asset_shelf.h @@ -11,18 +11,54 @@ extern "C" { #endif struct ARegionType; +struct AssetShelfSettings; struct bContext; +struct bContextDataResult; +struct BlendDataReader; +struct BlendWriter; struct wmWindowManager; -void ED_region_asset_shelf_footer_init(struct wmWindowManager *wm, struct ARegion *region); -void ED_region_asset_shelf_footer(const struct bContext *C, struct ARegion *region); +/* -------------------------------------------------------------------- */ +/* Asset Shelf Regions */ -void ED_region_asset_shelf_listen(const struct wmRegionListenerParams *params); +/** Only needed for #RGN_TYPE_ASSET_SHELF (not #RGN_TYPE_ASSET_SHELF_FOOTER). */ +void ED_asset_shelf_region_listen(const struct wmRegionListenerParams *params); +void ED_asset_shelf_footer_region_init(struct wmWindowManager *wm, struct ARegion *region); +void ED_asset_shelf_footer_region(const struct bContext *C, struct ARegion *region); +void ED_asset_shelf_footer_region_listen(const struct wmRegionListenerParams *params); void ED_asset_shelf_footer_register(struct ARegionType *region_type, const char *idname, const int space_type); +/* -------------------------------------------------------------------- */ +/* Asset Shelf Settings */ + +/** + * Deep-copies \a shelf_settings into newly allocated memory. Must be freed using #MEM_freeN() or + * #MEM_delete(). + */ +AssetShelfSettings *ED_asset_shelf_settings_duplicate(const AssetShelfSettings *shelf_settings); +/** + * Frees the contained data, not \a shelf_settings itself. + */ +void ED_asset_shelf_settings_free(AssetShelfSettings *shelf_settings); + +void ED_asset_shelf_settings_blend_write(struct BlendWriter *writer, + const struct AssetShelfSettings *storage); +void ED_asset_shelf_settings_blend_read_data(struct BlendDataReader *reader, + struct AssetShelfSettings **storage); + +/* -------------------------------------------------------------------- */ + +/** + * Creates an `"asset_shelf_settings"` context member, pointing to \a shelf_settings. + */ +int ED_asset_shelf_context(const struct bContext *C, + const char *member, + struct bContextDataResult *result, + struct AssetShelfSettings *shelf_settings); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/asset/intern/asset_shelf.cc b/source/blender/editors/asset/intern/asset_shelf.cc index 42396d5d435..3ea457a9056 100644 --- a/source/blender/editors/asset/intern/asset_shelf.cc +++ b/source/blender/editors/asset/intern/asset_shelf.cc @@ -1,44 +1,411 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup edasset */ +#include "AS_asset_catalog.hh" +#include "AS_asset_catalog_tree.hh" +#include "AS_asset_library.hh" + +#include "BKE_context.h" #include "BKE_screen.h" +#include "BLO_read_write.h" + +#include "BLT_translation.h" + #include "DNA_screen_types.h" #include "ED_asset_list.h" +#include "ED_asset_list.hh" #include "ED_screen.h" +#include "RNA_prototypes.h" + #include "UI_interface.h" +#include "UI_interface.hh" #include "UI_resources.h" +#include "UI_tree_view.hh" + +#include "WM_api.h" #include "ED_asset_shelf.h" -static void asset_shelf_draw(const bContext * /*C*/, Header *header) -{ - uiLayout *layout = header->layout; - uiItemL(layout, "Fooo\n", ICON_ASSET_MANAGER); -} +using namespace blender; -void ED_region_asset_shelf_listen(const wmRegionListenerParams *params) +/* -------------------------------------------------------------------- */ +/** \name Asset Shelf Regions + * \{ */ + +void ED_asset_shelf_region_listen(const wmRegionListenerParams *params) { if (ED_assetlist_listen(params->notifier)) { ED_region_tag_redraw_no_rebuild(params->region); } } -void ED_region_asset_shelf_footer_init(wmWindowManager * /*wm*/, ARegion *region) +void ED_asset_shelf_footer_region_listen(const wmRegionListenerParams *params) +{ + ARegion *region = params->region; + const wmNotifier *wmn = params->notifier; + + switch (wmn->category) { + case NC_SPACE: + if (wmn->data == ND_SPACE_ASSET_SHELF) { + ED_region_tag_redraw(region); + } + break; + } +} + +void ED_asset_shelf_footer_region_init(wmWindowManager * /*wm*/, ARegion *region) { ED_region_header_init(region); } -void ED_region_asset_shelf_footer(const bContext *C, ARegion *region) +void ED_asset_shelf_footer_region(const bContext *C, ARegion *region) { ED_region_header(C, region); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Asset Shelf Settings + * \{ */ + +AssetShelfSettings *ED_asset_shelf_settings_duplicate(const AssetShelfSettings *shelf_settings) +{ + if (!shelf_settings) { + return nullptr; + } + + static_assert( + std::is_trivial_v, + "AssetShelfSettings needs to be trivial to allow freeing with MEM_freeN() (API promise)"); + AssetShelfSettings *new_settings = MEM_new(__func__, *shelf_settings); + + LISTBASE_FOREACH (LinkData *, catalog_path_item, &shelf_settings->enabled_catalog_paths) { + LinkData *new_path_item = static_cast(MEM_dupallocN(catalog_path_item)); + new_path_item->data = BLI_strdup((char *)catalog_path_item->data); + BLI_addtail(&new_settings->enabled_catalog_paths, new_path_item); + } + + return new_settings; +} + +static void asset_shelf_settings_clear_enabled_catalogs(AssetShelfSettings &shelf_settings) +{ + LISTBASE_FOREACH_MUTABLE (LinkData *, catalog_path_item, &shelf_settings.enabled_catalog_paths) { + MEM_freeN(catalog_path_item->data); + BLI_freelinkN(&shelf_settings.enabled_catalog_paths, catalog_path_item); + } + BLI_assert(BLI_listbase_is_empty(&shelf_settings.enabled_catalog_paths)); +} + +void ED_asset_shelf_settings_free(AssetShelfSettings *shelf_settings) +{ + asset_shelf_settings_clear_enabled_catalogs(*shelf_settings); +} + +void ED_asset_shelf_settings_blend_write(BlendWriter *writer, + const AssetShelfSettings *shelf_settings) +{ + if (!shelf_settings) { + return; + } + + BLO_write_struct(writer, AssetShelfSettings, shelf_settings); + + LISTBASE_FOREACH (LinkData *, catalog_path_item, &shelf_settings->enabled_catalog_paths) { + BLO_write_struct(writer, LinkData, catalog_path_item); + BLO_write_string(writer, (const char *)catalog_path_item->data); + } +} + +void ED_asset_shelf_settings_blend_read_data(BlendDataReader *reader, + AssetShelfSettings **shelf_settings) +{ + if (!*shelf_settings) { + return; + } + + BLO_read_data_address(reader, shelf_settings); + + BLO_read_list(reader, &(*shelf_settings)->enabled_catalog_paths); + LISTBASE_FOREACH (LinkData *, catalog_path_item, &(*shelf_settings)->enabled_catalog_paths) { + BLO_read_data_address(reader, &catalog_path_item->data); + } +} + +static bool asset_shelf_settings_is_catalog_path_enabled( + const AssetShelfSettings &shelf_settings, const asset_system::AssetCatalogPath &path) +{ + LISTBASE_FOREACH (LinkData *, catalog_path_item, &shelf_settings.enabled_catalog_paths) { + if (StringRef((const char *)catalog_path_item->data) == path.str()) { + return true; + } + } + return false; +} + +static void asset_shelf_settings_set_catalog_path_enabled( + AssetShelfSettings &shelf_settings, const asset_system::AssetCatalogPath &path) +{ + char *path_copy = BLI_strdupn(path.c_str(), path.length()); + BLI_addtail(&shelf_settings.enabled_catalog_paths, BLI_genericNodeN(path_copy)); +} + +static void asset_shelf_settings_foreach_enabled_catalog_path( + const AssetShelfSettings &shelf_settings, + FunctionRef fn) +{ + LISTBASE_FOREACH (LinkData *, catalog_path_item, &shelf_settings.enabled_catalog_paths) { + fn(asset_system::AssetCatalogPath((char *)catalog_path_item->data)); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Asset Shelf Context + * \{ */ + +int ED_asset_shelf_context(const bContext *C, + const char *member, + bContextDataResult *result, + AssetShelfSettings *shelf_settings) +{ + static const char *context_dir[] = { + "asset_shelf_settings", + nullptr, + }; + + if (CTX_data_dir(member)) { + CTX_data_dir_set(result, context_dir); + return CTX_RESULT_OK; + } + + bScreen *screen = CTX_wm_screen(C); + + if (CTX_data_equals(member, "asset_shelf_settings")) { + CTX_data_pointer_set(result, &screen->id, &RNA_AssetShelfSettings, shelf_settings); + + return CTX_RESULT_OK; + } + + return CTX_RESULT_MEMBER_NOT_FOUND; +} + +static AssetShelfSettings *get_asset_shelf_settings_from_context(const bContext *C) +{ + PointerRNA shelf_settings_ptr = CTX_data_pointer_get_type( + C, "asset_shelf_settings", &RNA_AssetShelfSettings); + return static_cast(shelf_settings_ptr.data); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Asset Catalog Selector UI + * + * Popup containing a tree-view to select which catalogs to display in the asset shelf footer. + * \{ */ + +class AssetCatalogSelectorTree : public ui::AbstractTreeView { + asset_system::AssetLibrary &library_; + asset_system::AssetCatalogTree *catalog_tree_; + AssetShelfSettings &shelf_settings_; + + public: + class Item; + + AssetCatalogSelectorTree(asset_system::AssetLibrary &library, AssetShelfSettings &shelf_settings) + : library_(library), shelf_settings_(shelf_settings) + { + asset_system::AssetCatalogService *catalog_service = library_.catalog_service.get(); + catalog_tree_ = catalog_service->get_catalog_tree(); + } + + void build_tree() override + { + if (!catalog_tree_) { + return; + } + + catalog_tree_->foreach_root_item([this](asset_system::AssetCatalogTreeItem &catalog_item) { + build_catalog_items_recursive(*this, catalog_item); + }); + } + + Item &build_catalog_items_recursive(ui::TreeViewOrItem &parent_view_item, + asset_system::AssetCatalogTreeItem &catalog_item) const + { + Item &view_item = parent_view_item.add_tree_item(catalog_item, shelf_settings_); + + catalog_item.foreach_child([&view_item, this](asset_system::AssetCatalogTreeItem &child) { + build_catalog_items_recursive(view_item, child); + }); + + return view_item; + } + + void update_shelf_settings_from_enabled_catalogs(const bContext *C); + + class Item : public ui::BasicTreeViewItem { + asset_system::AssetCatalogTreeItem catalog_item_; + /* Is the catalog path enabled in this redraw? Set on construction, updated by the UI (which + * gets a pointer to it). The UI needs it as char. */ + char catalog_path_enabled_ = false; + + public: + Item(asset_system::AssetCatalogTreeItem &catalog_item, AssetShelfSettings &shelf_settings) + : ui::BasicTreeViewItem(catalog_item.get_name()), + catalog_item_(catalog_item), + catalog_path_enabled_(asset_shelf_settings_is_catalog_path_enabled( + shelf_settings, catalog_item.catalog_path())) + { + } + + bool is_catalog_path_enabled() const + { + return catalog_path_enabled_ != 0; + } + + asset_system::AssetCatalogPath catalog_path() const + { + return catalog_item_.catalog_path(); + } + + void build_row(uiLayout &row) override + { + uiBlock *block = uiLayoutGetBlock(&row); + + uiLayoutSetEmboss(&row, UI_EMBOSS); + + if (!is_collapsible()) { + uiItemL(&row, nullptr, ICON_BLANK1); + } + + uiBut *but = uiDefButC(block, + UI_BTYPE_CHECKBOX, + 0, + catalog_item_.get_name().c_str(), + 0, + 0, + UI_UNIT_X * 10, + UI_UNIT_Y, + (char *)&catalog_path_enabled_, + 0, + 0, + 0, + 0, + TIP_("Toggle catalog visibility in the asset shelf")); + UI_but_func_set( + but, + [](bContext *C, void *selector_tree_ptr, void *) { + AssetCatalogSelectorTree &selector_tree = *static_cast( + selector_tree_ptr); + selector_tree.update_shelf_settings_from_enabled_catalogs(C); + }, + &dynamic_cast(get_tree_view()), + nullptr); + UI_but_flag_disable(but, UI_BUT_UNDO); + } + }; +}; + +void AssetCatalogSelectorTree::update_shelf_settings_from_enabled_catalogs(const bContext *C) +{ + asset_shelf_settings_clear_enabled_catalogs(shelf_settings_); + foreach_item([C, this](ui::AbstractTreeViewItem &view_item) { + const auto &selector_tree_item = dynamic_cast(view_item); + if (selector_tree_item.is_catalog_path_enabled()) { + asset_shelf_settings_set_catalog_path_enabled(shelf_settings_, + selector_tree_item.catalog_path()); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_ASSET_SHELF, nullptr); + } + }); +} + +static uiBlock *asset_shelf_catalog_selector_block_draw(bContext *C, + ARegion *region, + void * /*arg1*/) +{ + const AssetLibraryReference *library_ref = CTX_wm_asset_library_ref(C); + asset_system::AssetLibrary *library = ED_assetlist_library_get_once_available(*library_ref); + AssetShelfSettings *shelf_settings = get_asset_shelf_settings_from_context(C); + + uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS); + UI_block_flag_enable(block, + UI_BLOCK_KEEP_OPEN | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPUP_CAN_REFRESH); + + uiLayout *layout = UI_block_layout(block, + UI_LAYOUT_VERTICAL, + UI_LAYOUT_PANEL, + 0, + 0, + UI_UNIT_X * 12, + UI_UNIT_Y, + 0, + UI_style_get()); + + uiItemL(layout, "Enable Catalogs", ICON_NONE); + uiItemS(layout); + + uiLayoutSetEmboss(layout, UI_EMBOSS_NONE); + if (library && shelf_settings) { + ui::AbstractTreeView *tree_view = UI_block_add_view( + *block, + "asset catalog tree view", + std::make_unique(*library, *shelf_settings)); + + ui::TreeViewBuilder builder(*block); + builder.build_tree_view(*tree_view); + } + + UI_block_bounds_set_normal(block, 0.3f * U.widget_unit); + UI_block_direction_set(block, UI_DIR_UP); + + return block; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Asset Shelf Footer + * + * Implemented as HeaderType for #RGN_TYPE_ASSET_SHELF_FOOTER. + * \{ */ + +static void asset_shelf_footer_draw(const bContext *C, Header *header) +{ + uiLayout *layout = header->layout; + uiBlock *block = uiLayoutGetBlock(layout); + const AssetLibraryReference *library_ref = CTX_wm_asset_library_ref(C); + + ED_assetlist_storage_fetch(library_ref, C); + uiDefIconBlockBut(block, + asset_shelf_catalog_selector_block_draw, + nullptr, + 0, + ICON_RIGHTARROW, + 0, + 0, + UI_UNIT_X * 1.5f, + UI_UNIT_Y, + TIP_("Select catalogs to display")); + + AssetShelfSettings *shelf_settings = get_asset_shelf_settings_from_context(C); + if (shelf_settings) { + asset_shelf_settings_foreach_enabled_catalog_path( + *shelf_settings, [layout](const asset_system::AssetCatalogPath &path) { + uiItemL(layout, path.name().c_str(), ICON_NONE); + }); + } +} + void ED_asset_shelf_footer_register(ARegionType *region_type, const char *idname, const int space_type) @@ -47,6 +414,8 @@ void ED_asset_shelf_footer_register(ARegionType *region_type, strcpy(ht->idname, idname); ht->space_type = space_type; ht->region_type = RGN_TYPE_ASSET_SHELF_FOOTER; - ht->draw = asset_shelf_draw; + ht->draw = asset_shelf_footer_draw; BLI_addtail(®ion_type->headertypes, ht); } + +/** \} */ diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 9ad784a55d6..0debb72339b 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -168,6 +168,7 @@ enum { UI_BLOCK_SEARCH_ONLY = 1 << 25, /** Hack for quick setup (splash screen) to draw text centered. */ UI_BLOCK_QUICK_SETUP = 1 << 26, + UI_BLOCK_POPUP_CAN_REFRESH = 1 << 27, }; /** #uiPopupBlockHandle.menuretval */ diff --git a/source/blender/editors/interface/interface_region_popup.cc b/source/blender/editors/interface/interface_region_popup.cc index 520c3e9dc82..88974834847 100644 --- a/source/blender/editors/interface/interface_region_popup.cc +++ b/source/blender/editors/interface/interface_region_popup.cc @@ -593,6 +593,11 @@ uiBlock *ui_popup_block_refresh(bContext *C, block->flag |= UI_BLOCK_LOOP; UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); + /* TODO does this flag need to be checked in more cases? */ + if (block->flag & UI_BLOCK_POPUP_CAN_REFRESH) { + handle->can_refresh = true; + } + /* defer this until blocks are translated (below) */ block->oldblock = nullptr; diff --git a/source/blender/editors/interface/views/tree_view.cc b/source/blender/editors/interface/views/tree_view.cc index 4fc256d0ffa..235ea886df8 100644 --- a/source/blender/editors/interface/views/tree_view.cc +++ b/source/blender/editors/interface/views/tree_view.cc @@ -167,7 +167,7 @@ void AbstractTreeViewItem::collapse_chevron_click_fn(struct bContext *C, * lookup the hovered item via context here. */ const wmWindow *win = CTX_wm_window(C); - const ARegion *region = CTX_wm_region(C); + const ARegion *region = CTX_wm_menu(C) ? CTX_wm_menu(C) : CTX_wm_region(C); uiViewItemHandle *hovered_item_handle = UI_region_views_find_item_at(region, win->eventstate->xy); diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc index 7b7a9408135..371b1d0585b 100644 --- a/source/blender/editors/space_view3d/space_view3d.cc +++ b/source/blender/editors/space_view3d/space_view3d.cc @@ -342,11 +342,21 @@ static void view3d_free(SpaceLink *sl) } BKE_viewer_path_clear(&vd->viewer_path); + + if (vd->asset_shelf) { + ED_asset_shelf_settings_free(vd->asset_shelf); + MEM_SAFE_FREE(vd->asset_shelf); + } } /* spacetype; init callback */ -static void view3d_init(wmWindowManager * /*wm*/, ScrArea * /*area*/) +static void view3d_init(wmWindowManager * /*wm*/, ScrArea *area) { + BLI_assert(area->spacetype == SPACE_VIEW3D); + View3D *v3d = static_cast(area->spacedata.first); + if (!v3d->asset_shelf) { + v3d->asset_shelf = MEM_cnew("AssetShelfSettings"); + } } static void view3d_exit(wmWindowManager * /*wm*/, ScrArea *area) @@ -382,6 +392,8 @@ static SpaceLink *view3d_duplicate(SpaceLink *sl) BKE_viewer_path_copy(&v3dn->viewer_path, &v3do->viewer_path); + v3dn->asset_shelf = ED_asset_shelf_settings_duplicate(v3do->asset_shelf); + /* copy or clear inside new stuff */ return (SpaceLink *)v3dn; @@ -1959,6 +1971,14 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes return CTX_RESULT_MEMBER_NOT_FOUND; } +static int view3d_asset_shelf_context(const bContext *C, + const char *member, + bContextDataResult *result) +{ + View3D *v3d = CTX_wm_view3d(C); + return ED_asset_shelf_context(C, member, result, v3d->asset_shelf); +} + static void view3d_id_remap_v3d_ob_centers(View3D *v3d, const struct IDRemapper *mappings) { if (BKE_id_remapper_apply(mappings, (ID **)&v3d->ob_center, ID_REMAP_APPLY_DEFAULT) == @@ -2034,6 +2054,8 @@ static void view3d_blend_read_data(BlendDataReader *reader, SpaceLink *sl) BKE_screen_view3d_do_versions_250(v3d, &sl->regionbase); BKE_viewer_path_blend_read_data(reader, &v3d->viewer_path); + + ED_asset_shelf_settings_blend_read_data(reader, &v3d->asset_shelf); } static void view3d_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl) @@ -2062,6 +2084,8 @@ static void view3d_blend_write(BlendWriter *writer, SpaceLink *sl) BKE_screen_view3d_shading_blend_write(writer, &v3d->shading); BKE_viewer_path_blend_write(writer, &v3d->viewer_path); + + ED_asset_shelf_settings_blend_write(writer, v3d->asset_shelf); } void ED_spacetype_view3d() @@ -2158,7 +2182,7 @@ void ED_spacetype_view3d() art->regionid = RGN_TYPE_ASSET_SHELF; art->prefsizey = HEADERY * 3.5f; art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER; - art->listener = ED_region_asset_shelf_listen; + art->listener = ED_asset_shelf_region_listen; art->init = view3d_header_region_init; art->draw = ED_region_header; BLI_addhead(&st->regiontypes, art); @@ -2167,8 +2191,10 @@ void ED_spacetype_view3d() art->regionid = RGN_TYPE_ASSET_SHELF_FOOTER; art->prefsizey = HEADERY; art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FOOTER; - art->init = ED_region_asset_shelf_footer_init; - art->draw = ED_region_asset_shelf_footer; + art->init = ED_asset_shelf_footer_region_init; + art->draw = ED_asset_shelf_footer_region; + art->listener = ED_asset_shelf_footer_region_listen; + art->context = view3d_asset_shelf_context; BLI_addhead(&st->regiontypes, art); ED_asset_shelf_footer_register(art, "VIEW3D_HT_asset_shelf", SPACE_VIEW3D); diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 0b4e3ec5d15..2e13cc2ce5c 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -746,6 +746,11 @@ enum { RGN_DRAW_EDITOR_OVERLAYS = 32, }; +typedef struct AssetShelfSettings { + /* TODO make this per mode? (or use a custom identifier?) */ + ListBase enabled_catalog_paths; /* #LinkData */ +} AssetShelfSettings; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 4c5ba4c43f8..47fe5bfbfe4 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -356,6 +356,8 @@ typedef struct View3D { /** Path to the viewer node that is currently previewed. This is retrieved from the workspace. */ ViewerPath viewer_path; + struct AssetShelfSettings *asset_shelf; + /** Runtime evaluation data (keep last). */ View3D_Runtime runtime; } View3D; diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index 9de32c24702..85def7d6579 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -1832,6 +1832,12 @@ static void rna_def_menu(BlenderRNA *brna) RNA_define_verify_sdna(1); } +static void rna_def_asset_shelf_settings(BlenderRNA *brna) +{ + StructRNA *srna = RNA_def_struct(brna, "AssetShelfSettings", NULL); + RNA_def_struct_ui_text(srna, "Asset Shelf Settings", ""); +} + void RNA_def_ui(BlenderRNA *brna) { rna_def_ui_layout(brna); @@ -1839,6 +1845,7 @@ void RNA_def_ui(BlenderRNA *brna) rna_def_uilist(brna); rna_def_header(brna); rna_def_menu(brna); + rna_def_asset_shelf_settings(brna); } #endif /* RNA_RUNTIME */ diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 085ddaac5a5..b9c0b7e5ee0 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -485,6 +485,7 @@ typedef struct wmNotifier { #define ND_SPACE_CLIP (20 << 16) #define ND_SPACE_FILE_PREVIEW (21 << 16) #define ND_SPACE_SPREADSHEET (22 << 16) +#define ND_SPACE_ASSET_SHELF (23 << 16) /* NC_ASSET */ /* Denotes that the AssetList is done reading some previews. NOT that the preview generation of -- 2.30.2 From b4edc40fb8fa4fd74ab57a220d9c2a05f352c805 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 31 Jan 2023 17:17:34 +0100 Subject: [PATCH 16/49] UI: Make uiBut safe for non-trivial construction Essentially, I wanted to use a non-trivially-constructible C++ type (`std::function`) inside `uiBut`. But this would mean we can't use `MEM_cnew()` like allocation anymore. Rather than writing worse code, allow non-trivial construction for `uiBut`. Member-initializing all members is annoying since there are so many, but rather safe than sorry. As we use more C++ types (e.g. convert callbacks to use `std::function`), this should become less since they initialize properly on default construction. Also use proper C++ inheritance for `uiBut` subtypes, the old way to allocate based on size isn't working anymore. Differential Revision: https://developer.blender.org/D17164 --- source/blender/editors/include/UI_interface.h | 2 + .../blender/editors/include/UI_interface.hh | 1 + source/blender/editors/interface/interface.cc | 146 ++++------- .../editors/interface/interface_anim.cc | 32 +-- .../interface/interface_context_menu.cc | 2 +- .../editors/interface/interface_handlers.cc | 15 +- .../editors/interface/interface_intern.hh | 242 ++++++++---------- .../editors/interface/interface_layout.cc | 38 +-- .../editors/interface/interface_ops.cc | 4 +- .../interface_region_color_picker.cc | 14 +- .../interface/interface_region_search.cc | 27 +- .../editors/interface/interface_templates.cc | 14 +- .../editors/interface/interface_widgets.cc | 2 +- .../editors/interface/views/grid_view.cc | 2 +- .../editors/interface/views/tree_view.cc | 6 +- 15 files changed, 244 insertions(+), 303 deletions(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index db3f0dc41fb..cf2c0ea2ebb 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -322,6 +322,8 @@ enum { * - bit 9-15: button type (now 6 bits, 64 types) */ typedef enum { + UI_BUT_POIN_NONE = 0, + UI_BUT_POIN_CHAR = 32, UI_BUT_POIN_SHORT = 64, UI_BUT_POIN_INT = 96, diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh index fc03b0218c0..4d3e40b30fc 100644 --- a/source/blender/editors/include/UI_interface.hh +++ b/source/blender/editors/include/UI_interface.hh @@ -19,6 +19,7 @@ struct GeometryAttributeInfo; struct StructRNA; struct uiBlock; +struct uiBut; struct uiSearchItems; namespace blender::ui { diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index 2a21356b9aa..73f2efb6d19 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -3474,7 +3474,7 @@ static void ui_but_free(const bContext *C, uiBut *but) BLI_assert(UI_butstore_is_registered(but->block, but) == false); - MEM_freeN(but); + MEM_delete(but); } void UI_block_free(const bContext *C, uiBlock *block) @@ -3975,89 +3975,60 @@ void ui_block_cm_to_display_space_v3(uiBlock *block, float pixel[3]) IMB_colormanagement_scene_linear_to_display_v3(pixel, display); } -static void ui_but_alloc_info(const eButType type, - size_t *r_alloc_size, - const char **r_alloc_str, - bool *r_has_custom_type) +/** + * Factory function: Allocate button and set #uiBut.type. + */ +static uiBut *ui_but_new(const eButType type) { - size_t alloc_size; - const char *alloc_str; - bool has_custom_type = true; + uiBut *but = nullptr; + +#define NEW_BUT(type_name) MEM_new(#type_name) switch (type) { case UI_BTYPE_NUM: - alloc_size = sizeof(uiButNumber); - alloc_str = "uiButNumber"; + but = MEM_new("uiButNumber"); break; case UI_BTYPE_COLOR: - alloc_size = sizeof(uiButColor); - alloc_str = "uiButColor"; + but = MEM_new("uiButColor"); break; case UI_BTYPE_DECORATOR: - alloc_size = sizeof(uiButDecorator); - alloc_str = "uiButDecorator"; + but = MEM_new("uiButDecorator"); break; case UI_BTYPE_TAB: - alloc_size = sizeof(uiButTab); - alloc_str = "uiButTab"; + but = MEM_new("uiButTab"); break; case UI_BTYPE_SEARCH_MENU: - alloc_size = sizeof(uiButSearch); - alloc_str = "uiButSearch"; + but = MEM_new("uiButSearch"); break; case UI_BTYPE_PROGRESS_BAR: - alloc_size = sizeof(uiButProgressbar); - alloc_str = "uiButProgressbar"; + but = MEM_new("uiButProgressbar"); break; case UI_BTYPE_HSVCUBE: - alloc_size = sizeof(uiButHSVCube); - alloc_str = "uiButHSVCube"; + but = MEM_new("uiButHSVCube"); break; case UI_BTYPE_COLORBAND: - alloc_size = sizeof(uiButColorBand); - alloc_str = "uiButColorBand"; + but = MEM_new("uiButColorBand"); break; case UI_BTYPE_CURVE: - alloc_size = sizeof(uiButCurveMapping); - alloc_str = "uiButCurveMapping"; + but = MEM_new("uiButCurveMapping"); break; case UI_BTYPE_CURVEPROFILE: - alloc_size = sizeof(uiButCurveProfile); - alloc_str = "uiButCurveProfile"; + but = MEM_new("uiButCurveProfile"); break; case UI_BTYPE_HOTKEY_EVENT: - alloc_size = sizeof(uiButHotkeyEvent); - alloc_str = "uiButHotkeyEvent"; + but = MEM_new("uiButHotkeyEvent"); break; case UI_BTYPE_VIEW_ITEM: - alloc_size = sizeof(uiButViewItem); - alloc_str = "uiButViewItem"; + but = MEM_new("uiButViewItem"); break; default: - alloc_size = sizeof(uiBut); - alloc_str = "uiBut"; - has_custom_type = false; + but = NEW_BUT(uiBut); break; } +#undef NEW_BUT - if (r_alloc_size) { - *r_alloc_size = alloc_size; - } - if (r_alloc_str) { - *r_alloc_str = alloc_str; - } - if (r_has_custom_type) { - *r_has_custom_type = has_custom_type; - } -} - -static uiBut *ui_but_alloc(const eButType type) -{ - size_t alloc_size; - const char *alloc_str; - ui_but_alloc_info(type, &alloc_size, &alloc_str, nullptr); - - return static_cast(MEM_callocN(alloc_size, alloc_str)); + but->type = type; + return but; } uiBut *ui_but_change_type(uiBut *but, eButType new_type) @@ -4067,46 +4038,41 @@ uiBut *ui_but_change_type(uiBut *but, eButType new_type) return but; } - size_t alloc_size; - const char *alloc_str; uiBut *insert_after_but = but->prev; - bool new_has_custom_type, old_has_custom_type; /* Remove old button address */ BLI_remlink(&but->block->buttons, but); - ui_but_alloc_info(but->type, nullptr, nullptr, &old_has_custom_type); - ui_but_alloc_info(new_type, &alloc_size, &alloc_str, &new_has_custom_type); + const uiBut *old_but_ptr = but; + /* Button may have pointer to a member within itself, this will have to be updated. */ + const bool has_str_ptr_to_self = but->str == but->strdata; + const bool has_poin_ptr_to_self = but->poin == (char *)but; - if (new_has_custom_type || old_has_custom_type) { - const uiBut *old_but_ptr = but; - /* Button may have pointer to a member within itself, this will have to be updated. */ - const bool has_str_ptr_to_self = but->str == but->strdata; - const bool has_poin_ptr_to_self = but->poin == (char *)but; - - but = static_cast(MEM_recallocN_id(but, alloc_size, alloc_str)); - but->type = new_type; - if (has_str_ptr_to_self) { - but->str = but->strdata; - } - if (has_poin_ptr_to_self) { - but->poin = (char *)but; - } - - BLI_insertlinkafter(&but->block->buttons, insert_after_but, but); - - if (but->layout) { - const bool found_layout = ui_layout_replace_but_ptr(but->layout, old_but_ptr, but); - BLI_assert(found_layout); - UNUSED_VARS_NDEBUG(found_layout); - ui_button_group_replace_but_ptr(uiLayoutGetBlock(but->layout), old_but_ptr, but); - } -#ifdef WITH_PYTHON - if (UI_editsource_enable_check()) { - UI_editsource_but_replace(old_but_ptr, but); - } -#endif + /* Copy construct button with the new type. */ + but = ui_but_new(new_type); + *but = *old_but_ptr; + if (has_str_ptr_to_self) { + but->str = but->strdata; } + if (has_poin_ptr_to_self) { + but->poin = (char *)but; + } + + BLI_insertlinkafter(&but->block->buttons, insert_after_but, but); + + if (but->layout) { + const bool found_layout = ui_layout_replace_but_ptr(but->layout, old_but_ptr, but); + BLI_assert(found_layout); + UNUSED_VARS_NDEBUG(found_layout); + ui_button_group_replace_but_ptr(uiLayoutGetBlock(but->layout), old_but_ptr, but); + } +#ifdef WITH_PYTHON + if (UI_editsource_enable_check()) { + UI_editsource_but_replace(old_but_ptr, but); + } +#endif + + MEM_delete(old_but_ptr); return but; } @@ -4152,14 +4118,11 @@ static uiBut *ui_def_but(uiBlock *block, } } - uiBut *but = ui_but_alloc((eButType)(type & BUTTYPE)); + uiBut *but = ui_but_new((eButType)(type & BUTTYPE)); - but->type = (eButType)(type & BUTTYPE); but->pointype = (eButPointerType)(type & UI_BUT_POIN_TYPES); but->bit = type & UI_BUT_POIN_BIT; but->bitnr = type & 31; - but->icon = ICON_NONE; - but->iconadd = 0; but->retval = retval; @@ -4180,7 +4143,6 @@ static uiBut *ui_def_but(uiBlock *block, but->disabled_info = block->lockstr; but->emboss = block->emboss; - but->pie_dir = UI_RADIAL_NONE; but->block = block; /* pointer back, used for front-buffer status, and picker. */ @@ -6310,7 +6272,7 @@ void UI_but_func_search_set(uiBut *but, if (search_exec_fn) { #ifdef DEBUG - if (search_but->but.func) { + if (but->func) { /* watch this, can be cause of much confusion, see: T47691 */ printf("%s: warning, overwriting button callback with search function callback!\n", __func__); diff --git a/source/blender/editors/interface/interface_anim.cc b/source/blender/editors/interface/interface_anim.cc index a8a4faf622f..78dccbaf1f8 100644 --- a/source/blender/editors/interface/interface_anim.cc +++ b/source/blender/editors/interface/interface_anim.cc @@ -113,41 +113,37 @@ void ui_but_anim_flag(uiBut *but, const AnimationEvalContext *anim_eval_context) } } -static uiBut *ui_but_anim_decorate_find_attached_button(uiButDecorator *but_decorate) +static uiBut *ui_but_anim_decorate_find_attached_button(uiButDecorator *but) { uiBut *but_iter = nullptr; - BLI_assert(UI_but_is_decorator(&but_decorate->but)); - BLI_assert(but_decorate->rnapoin.data && but_decorate->rnaprop); + BLI_assert(UI_but_is_decorator(but)); + BLI_assert(but->rnapoin.data && but->rnaprop); - LISTBASE_CIRCULAR_BACKWARD_BEGIN ( - uiBut *, &but_decorate->but.block->buttons, but_iter, but_decorate->but.prev) { - if (but_iter != (uiBut *)but_decorate && - ui_but_rna_equals_ex( - but_iter, &but_decorate->rnapoin, but_decorate->rnaprop, but_decorate->rnaindex)) { + LISTBASE_CIRCULAR_BACKWARD_BEGIN (uiBut *, &but->block->buttons, but_iter, but->prev) { + if (but_iter != (uiBut *)but && + ui_but_rna_equals_ex(but_iter, &but->rnapoin, but->rnaprop, but->rnaindex)) { return but_iter; } } - LISTBASE_CIRCULAR_BACKWARD_END( - uiBut *, &but_decorate->but.block->buttons, but_iter, but_decorate->but.prev); + LISTBASE_CIRCULAR_BACKWARD_END(uiBut *, &but->block->buttons, but_iter, but->prev); return nullptr; } -void ui_but_anim_decorate_update_from_flag(uiButDecorator *decorator_but) +void ui_but_anim_decorate_update_from_flag(uiButDecorator *but) { - if (!decorator_but->rnapoin.data || !decorator_but->rnaprop) { + if (!but->rnapoin.data || !but->rnaprop) { /* Nothing to do. */ return; } - const uiBut *but_anim = ui_but_anim_decorate_find_attached_button(decorator_but); - uiBut *but = &decorator_but->but; + const uiBut *but_anim = ui_but_anim_decorate_find_attached_button(but); if (!but_anim) { printf("Could not find button with matching property to decorate (%s.%s)\n", - RNA_struct_identifier(decorator_but->rnapoin.type), - RNA_property_identifier(decorator_but->rnaprop)); + RNA_struct_identifier(but->rnapoin.type), + RNA_property_identifier(but->rnaprop)); return; } @@ -326,7 +322,7 @@ void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void * /*arg_dummy*/) } /* FIXME(@campbellbarton): swapping active pointer is weak. */ - std::swap(but_anim->active, but_decorate->but.active); + std::swap(but_anim->active, but_decorate->active); wm->op_undo_depth++; if (but_anim->flag & UI_BUT_DRIVEN) { @@ -350,6 +346,6 @@ void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void * /*arg_dummy*/) WM_operator_properties_free(&props_ptr); } - std::swap(but_anim->active, but_decorate->but.active); + std::swap(but_anim->active, but_decorate->active); wm->op_undo_depth--; } diff --git a/source/blender/editors/interface/interface_context_menu.cc b/source/blender/editors/interface/interface_context_menu.cc index 5c31194fb92..fb6213e13ab 100644 --- a/source/blender/editors/interface/interface_context_menu.cc +++ b/source/blender/editors/interface/interface_context_menu.cc @@ -934,7 +934,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev uiButViewItem *view_item_but = (uiButViewItem *)ui_view_item_find_mouse_over(region, event->xy); if (view_item_but) { - BLI_assert(view_item_but->but.type == UI_BTYPE_VIEW_ITEM); + BLI_assert(view_item_but->type == UI_BTYPE_VIEW_ITEM); UI_view_item_context_menu_build(C, view_item_but->view_item, uiLayoutColumn(layout, false)); uiItemS(layout); } diff --git a/source/blender/editors/interface/interface_handlers.cc b/source/blender/editors/interface/interface_handlers.cc index 63e415b07aa..22d6ecce398 100644 --- a/source/blender/editors/interface/interface_handlers.cc +++ b/source/blender/editors/interface/interface_handlers.cc @@ -4752,7 +4752,7 @@ static int ui_do_but_VIEW_ITEM(bContext *C, const wmEvent *event) { uiButViewItem *view_item_but = (uiButViewItem *)but; - BLI_assert(view_item_but->but.type == UI_BTYPE_VIEW_ITEM); + BLI_assert(view_item_but->type == UI_BTYPE_VIEW_ITEM); if (data->state == BUTTON_STATE_HIGHLIGHT) { if (event->type == LEFTMOUSE) { @@ -4961,14 +4961,13 @@ static float ui_numedit_apply_snap(int temp, return temp; } -static bool ui_numedit_but_NUM(uiButNumber *number_but, +static bool ui_numedit_but_NUM(uiButNumber *but, uiHandleButtonData *data, int mx, const bool is_motion, const enum eSnapType snap, float fac) { - uiBut *but = &number_but->but; float deler, tempf; int lvalue, temp; bool changed = false; @@ -4990,13 +4989,13 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but, const float log_min = (scale_type == PROP_SCALE_LOG) ? max_ff(max_ff(softmin, UI_PROP_SCALE_LOG_MIN), - powf(10, -number_but->precision) * 0.5f) : + powf(10, -but->precision) * 0.5f) : 0; /* Mouse location isn't screen clamped to the screen so use a linear mapping * 2px == 1-int, or 1px == 1-ClickStep */ if (is_float) { - fac *= 0.01f * number_but->step_size; + fac *= 0.01f * but->step_size; switch (scale_type) { case PROP_SCALE_LINEAR: { tempf = float(data->startvalue) + float(mx - data->dragstartx) * fac; @@ -5170,7 +5169,7 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but, } case PROP_SCALE_LOG: { const float log_min = max_ff(max_ff(softmin, UI_PROP_SCALE_LOG_MIN), - powf(10.0f, -number_but->precision) * 0.5f); + powf(10.0f, -but->precision) * 0.5f); const float base = softmax / log_min; tempf = powf(base, data->dragf) * log_min; if (tempf <= log_min) { @@ -6148,8 +6147,8 @@ static bool ui_numedit_but_UNITVEC( static void ui_palette_set_active(uiButColor *color_but) { if (color_but->is_pallete_color) { - Palette *palette = (Palette *)color_but->but.rnapoin.owner_id; - PaletteColor *color = static_cast(color_but->but.rnapoin.data); + Palette *palette = (Palette *)color_but->rnapoin.owner_id; + PaletteColor *color = static_cast(color_but->rnapoin.data); palette->active_color = BLI_findindex(&palette->colors, color); } } diff --git a/source/blender/editors/interface/interface_intern.hh b/source/blender/editors/interface/interface_intern.hh index fc64500dbaa..e62a19ec146 100644 --- a/source/blender/editors/interface/interface_intern.hh +++ b/source/blender/editors/interface/interface_intern.hh @@ -152,24 +152,25 @@ enum { #define PIE_MAX_ITEMS 8 struct uiBut { - uiBut *next, *prev; + uiBut *next = nullptr, *prev = nullptr; /** Pointer back to the layout item holding this button. */ - uiLayout *layout; - int flag, drawflag; - eButType type; - eButPointerType pointype; - short bit, bitnr, retval, strwidth, alignnr; - short ofs, pos, selsta, selend; + uiLayout *layout = nullptr; + int flag = 0; + int drawflag = 0; + eButType type = eButType(0); + eButPointerType pointype = UI_BUT_POIN_NONE; + short bit = 0, bitnr = 0, retval = 0, strwidth = 0, alignnr = 0; + short ofs = 0, pos = 0, selsta = 0, selend = 0; - char *str; - char strdata[UI_MAX_NAME_STR]; - char drawstr[UI_MAX_DRAW_STR]; + char *str = nullptr; + char strdata[UI_MAX_NAME_STR] = ""; + char drawstr[UI_MAX_DRAW_STR] = ""; - rctf rect; /* block relative coords */ + rctf rect = {}; /* block relative coords */ - char *poin; - float hardmin, hardmax, softmin, softmax; + char *poin = nullptr; + float hardmin = 0, hardmax = 0, softmin = 0, softmax = 0; /* both these values use depends on the button type * (polymorphic struct or union would be nicer for this stuff) */ @@ -180,7 +181,7 @@ struct uiBut { * - UI_BTYPE_SCROLL: Use as scroll size. * - UI_BTYPE_SEARCH_MENU: Use as number or rows. */ - float a1; + float a1 = 0; /** * For #uiBut.type: @@ -188,210 +189,193 @@ struct uiBut { * - UI_BTYPE_LABEL: If `(a1 == 1.0f)` use a2 as a blending factor. * - UI_BTYPE_SEARCH_MENU: Use as number or columns. */ - float a2; + float a2 = 0; - uchar col[4]; + uchar col[4] = {0}; /** See \ref UI_but_func_identity_compare_set(). */ - uiButIdentityCompareFunc identity_cmp_func; + uiButIdentityCompareFunc identity_cmp_func = nullptr; - uiButHandleFunc func; - void *func_arg1; - void *func_arg2; + uiButHandleFunc func = nullptr; + void *func_arg1 = nullptr; + void *func_arg2 = nullptr; - uiButHandleNFunc funcN; - void *func_argN; + uiButHandleNFunc funcN = nullptr; + void *func_argN = nullptr; - bContextStore *context; + bContextStore *context = nullptr; - uiButCompleteFunc autocomplete_func; - void *autofunc_arg; + uiButCompleteFunc autocomplete_func = nullptr; + void *autofunc_arg = nullptr; - uiButHandleRenameFunc rename_func; - void *rename_arg1; - void *rename_orig; + uiButHandleRenameFunc rename_func = nullptr; + void *rename_arg1 = nullptr; + void *rename_orig = nullptr; /** Run an action when holding the button down. */ - uiButHandleHoldFunc hold_func; - void *hold_argN; + uiButHandleHoldFunc hold_func = nullptr; + void *hold_argN = nullptr; - const char *tip; - uiButToolTipFunc tip_func; - void *tip_arg; - uiFreeArgFunc tip_arg_free; + const char *tip = nullptr; + uiButToolTipFunc tip_func = nullptr; + void *tip_arg = nullptr; + uiFreeArgFunc tip_arg_free = nullptr; /** info on why button is disabled, displayed in tooltip */ - const char *disabled_info; + const char *disabled_info = nullptr; - BIFIconID icon; + BIFIconID icon = ICON_NONE; /** Copied from the #uiBlock.emboss */ - eUIEmbossType emboss; + eUIEmbossType emboss = UI_EMBOSS; /** direction in a pie menu, used for collision detection. */ - RadialDirection pie_dir; + RadialDirection pie_dir = UI_RADIAL_NONE; /** could be made into a single flag */ - bool changed; + bool changed = false; /** so buttons can support unit systems which are not RNA */ - uchar unit_type; - short iconadd; + uchar unit_type = 0; + short iconadd = 0; /** #UI_BTYPE_BLOCK data */ - uiBlockCreateFunc block_create_func; + uiBlockCreateFunc block_create_func = nullptr; /** #UI_BTYPE_PULLDOWN / #UI_BTYPE_MENU data */ - uiMenuCreateFunc menu_create_func; + uiMenuCreateFunc menu_create_func = nullptr; - uiMenuStepFunc menu_step_func; + uiMenuStepFunc menu_step_func = nullptr; /* RNA data */ - PointerRNA rnapoin; - PropertyRNA *rnaprop; - int rnaindex; + PointerRNA rnapoin = {}; + PropertyRNA *rnaprop = nullptr; + int rnaindex = -1; /* Operator data */ - wmOperatorType *optype; - PointerRNA *opptr; - wmOperatorCallContext opcontext; + wmOperatorType *optype = nullptr; + PointerRNA *opptr = nullptr; + wmOperatorCallContext opcontext = WM_OP_INVOKE_DEFAULT; /** When non-zero, this is the key used to activate a menu items (`a-z` always lower case). */ - uchar menu_key; + uchar menu_key = 0; - ListBase extra_op_icons; /** #uiButExtraOpIcon */ + ListBase extra_op_icons = {nullptr, nullptr}; /** #uiButExtraOpIcon */ /* Drag-able data, type is WM_DRAG_... */ - char dragtype; - short dragflag; - void *dragpoin; - ImBuf *imb; - float imb_scale; + char dragtype = WM_DRAG_ID; + short dragflag = 0; + void *dragpoin = nullptr; + ImBuf *imb = nullptr; + float imb_scale = 0; /** Active button data (set when the user is hovering or interacting with a button). */ - uiHandleButtonData *active; + uiHandleButtonData *active = nullptr; /** Custom button data (borrowed, not owned). */ - void *custom_data; + void *custom_data = nullptr; - char *editstr; - double *editval; - float *editvec; + char *editstr = nullptr; + double *editval = nullptr; + float *editvec = nullptr; - uiButPushedStateFunc pushed_state_func; - const void *pushed_state_arg; + uiButPushedStateFunc pushed_state_func = nullptr; + const void *pushed_state_arg = nullptr; /** Little indicator (e.g., counter) displayed on top of some icons. */ - IconTextOverlay icon_overlay_text; + IconTextOverlay icon_overlay_text = {}; /* pointer back */ - uiBlock *block; + uiBlock *block = nullptr; + + uiBut() = default; + /** Performs a mostly shallow copy for now. Only contained C++ types are deep copied. */ + uiBut(const uiBut &other) = default; + /** Mostly shallow copy, just like copy constructor above. */ + uiBut &operator=(const uiBut &other) = default; }; /** Derived struct for #UI_BTYPE_NUM */ -struct uiButNumber { - uiBut but; - - float step_size; - float precision; +struct uiButNumber : public uiBut { + float step_size = 0; + float precision = 0; }; /** Derived struct for #UI_BTYPE_COLOR */ -struct uiButColor { - uiBut but; - - bool is_pallete_color; - int palette_color_index; +struct uiButColor : public uiBut { + bool is_pallete_color = false; + int palette_color_index = -1; }; /** Derived struct for #UI_BTYPE_TAB */ -struct uiButTab { - uiBut but; - struct MenuType *menu; +struct uiButTab : public uiBut { + struct MenuType *menu = nullptr; }; /** Derived struct for #UI_BTYPE_SEARCH_MENU */ -struct uiButSearch { - uiBut but; +struct uiButSearch : public uiBut { + uiButSearchCreateFn popup_create_fn = nullptr; + uiButSearchUpdateFn items_update_fn = nullptr; + uiButSearchListenFn listen_fn = nullptr; - uiButSearchCreateFn popup_create_fn; - uiButSearchUpdateFn items_update_fn; - uiButSearchListenFn listen_fn; + void *item_active = nullptr; - void *item_active; + void *arg = nullptr; + uiFreeArgFunc arg_free_fn = nullptr; - void *arg; - uiFreeArgFunc arg_free_fn; + uiButSearchContextMenuFn item_context_menu_fn = nullptr; + uiButSearchTooltipFn item_tooltip_fn = nullptr; - uiButSearchContextMenuFn item_context_menu_fn; - uiButSearchTooltipFn item_tooltip_fn; + const char *item_sep_string = nullptr; - const char *item_sep_string; - - PointerRNA rnasearchpoin; - PropertyRNA *rnasearchprop; + PointerRNA rnasearchpoin = {}; + PropertyRNA *rnasearchprop = nullptr; /** * The search box only provides suggestions, it does not force * the string to match one of the search items when applying. */ - bool results_are_suggestions; + bool results_are_suggestions = false; }; /** Derived struct for #UI_BTYPE_DECORATOR */ -struct uiButDecorator { - uiBut but; - - struct PointerRNA rnapoin; - struct PropertyRNA *rnaprop; - int rnaindex; +struct uiButDecorator : public uiBut { + struct PointerRNA rnapoin = {}; + struct PropertyRNA *rnaprop = nullptr; + int rnaindex = -1; }; /** Derived struct for #UI_BTYPE_PROGRESS_BAR. */ -struct uiButProgressbar { - uiBut but; - +struct uiButProgressbar : public uiBut { /* 0..1 range */ - float progress; + float progress = 0; }; -struct uiButViewItem { - uiBut but; - +struct uiButViewItem : public uiBut { /* C-Handle to the view item this button was created for. */ - uiViewItemHandle *view_item; + uiViewItemHandle *view_item = nullptr; }; /** Derived struct for #UI_BTYPE_HSVCUBE. */ -struct uiButHSVCube { - uiBut but; - - eButGradientType gradient_type; +struct uiButHSVCube : public uiBut { + eButGradientType gradient_type = UI_GRAD_SV; }; /** Derived struct for #UI_BTYPE_COLORBAND. */ -struct uiButColorBand { - uiBut but; - - ColorBand *edit_coba; +struct uiButColorBand : public uiBut { + ColorBand *edit_coba = nullptr; }; /** Derived struct for #UI_BTYPE_CURVEPROFILE. */ -struct uiButCurveProfile { - uiBut but; - - struct CurveProfile *edit_profile; +struct uiButCurveProfile : public uiBut { + struct CurveProfile *edit_profile = nullptr; }; /** Derived struct for #UI_BTYPE_CURVE. */ -struct uiButCurveMapping { - uiBut but; - - struct CurveMapping *edit_cumap; - eButGradientType gradient_type; +struct uiButCurveMapping : public uiBut { + struct CurveMapping *edit_cumap = nullptr; + eButGradientType gradient_type = UI_GRAD_SV; }; /** Derived struct for #UI_BTYPE_HOTKEY_EVENT. */ -struct uiButHotkeyEvent { - uiBut but; - - short modifier_key; +struct uiButHotkeyEvent : public uiBut { + short modifier_key = 0; }; /** diff --git a/source/blender/editors/interface/interface_layout.cc b/source/blender/editors/interface/interface_layout.cc index 04546639039..aa1cb969be6 100644 --- a/source/blender/editors/interface/interface_layout.cc +++ b/source/blender/editors/interface/interface_layout.cc @@ -3139,28 +3139,28 @@ void uiItemDecoratorR_prop(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, /* Loop for the array-case, but only do in case of an expanded array. */ for (int i = 0; i < (is_expand ? RNA_property_array_length(ptr, prop) : 1); i++) { - uiButDecorator *decorator_but = (uiButDecorator *)uiDefIconBut(block, - UI_BTYPE_DECORATOR, - 0, - ICON_DOT, - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y, - nullptr, - 0.0, - 0.0, - 0.0, - 0.0, - TIP_("Animate property")); + uiButDecorator *but = (uiButDecorator *)uiDefIconBut(block, + UI_BTYPE_DECORATOR, + 0, + ICON_DOT, + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y, + nullptr, + 0.0, + 0.0, + 0.0, + 0.0, + TIP_("Animate property")); - UI_but_func_set(&decorator_but->but, ui_but_anim_decorate_cb, decorator_but, nullptr); - decorator_but->but.flag |= UI_BUT_UNDO | UI_BUT_DRAG_LOCK; + UI_but_func_set(but, ui_but_anim_decorate_cb, but, nullptr); + but->flag |= UI_BUT_UNDO | UI_BUT_DRAG_LOCK; /* Reusing RNA search members, setting actual RNA data has many side-effects. */ - decorator_but->rnapoin = *ptr; - decorator_but->rnaprop = prop; + but->rnapoin = *ptr; + but->rnaprop = prop; /* ui_def_but_rna() sets non-array buttons to have a RNA index of 0. */ - decorator_but->rnaindex = (!is_array || is_expand) ? i : index; + but->rnaindex = (!is_array || is_expand) ? i : index; } } diff --git a/source/blender/editors/interface/interface_ops.cc b/source/blender/editors/interface/interface_ops.cc index 8385835d0b6..445f6cc5337 100644 --- a/source/blender/editors/interface/interface_ops.cc +++ b/source/blender/editors/interface/interface_ops.cc @@ -1658,8 +1658,8 @@ static void ui_editsource_active_but_set(uiBut *but) { BLI_assert(ui_editsource_info == nullptr); - ui_editsource_info = MEM_cnew(__func__); - memcpy(&ui_editsource_info->but_orig, but, sizeof(uiBut)); + ui_editsource_info = MEM_new(__func__); + ui_editsource_info->but_orig = *but; ui_editsource_info->hash = BLI_ghash_ptr_new(__func__); } diff --git a/source/blender/editors/interface/interface_region_color_picker.cc b/source/blender/editors/interface/interface_region_color_picker.cc index b1dc306afb8..169116bba2a 100644 --- a/source/blender/editors/interface/interface_region_color_picker.cc +++ b/source/blender/editors/interface/interface_region_color_picker.cc @@ -397,7 +397,7 @@ static void ui_colorpicker_circle(uiBlock *block, 0, "Lightness"); hsv_but->gradient_type = UI_GRAD_L_ALT; - UI_but_func_set(&hsv_but->but, ui_colorpicker_rgba_update_cb, &hsv_but->but, nullptr); + UI_but_func_set(hsv_but, ui_colorpicker_rgba_update_cb, hsv_but, nullptr); } else { hsv_but = (uiButHSVCube *)uiDefButR_prop(block, @@ -417,9 +417,9 @@ static void ui_colorpicker_circle(uiBlock *block, 0, TIP_("Value")); hsv_but->gradient_type = UI_GRAD_V_ALT; - UI_but_func_set(&hsv_but->but, ui_colorpicker_rgba_update_cb, &hsv_but->but, nullptr); + UI_but_func_set(hsv_but, ui_colorpicker_rgba_update_cb, hsv_but, nullptr); } - hsv_but->but.custom_data = cpicker; + hsv_but->custom_data = cpicker; } static void ui_colorpicker_square(uiBlock *block, @@ -450,8 +450,8 @@ static void ui_colorpicker_square(uiBlock *block, 0, TIP_("Color")); hsv_but->gradient_type = type; - UI_but_func_set(&hsv_but->but, ui_colorpicker_rgba_update_cb, &hsv_but->but, nullptr); - hsv_but->but.custom_data = cpicker; + UI_but_func_set(hsv_but, ui_colorpicker_rgba_update_cb, hsv_but, nullptr); + hsv_but->custom_data = cpicker; /* value */ hsv_but = (uiButHSVCube *)uiDefButR_prop(block, @@ -471,8 +471,8 @@ static void ui_colorpicker_square(uiBlock *block, 0, TIP_("Value")); hsv_but->gradient_type = (eButGradientType)(type + 3); - UI_but_func_set(&hsv_but->but, ui_colorpicker_rgba_update_cb, &hsv_but->but, nullptr); - hsv_but->but.custom_data = cpicker; + UI_but_func_set(hsv_but, ui_colorpicker_rgba_update_cb, hsv_but, nullptr); + hsv_but->custom_data = cpicker; } /* a HS circle, V slider, rgb/hsv/hex sliders */ diff --git a/source/blender/editors/interface/interface_region_search.cc b/source/blender/editors/interface/interface_region_search.cc index f4de5b6009d..55511b0cff7 100644 --- a/source/blender/editors/interface/interface_region_search.cc +++ b/source/blender/editors/interface/interface_region_search.cc @@ -433,17 +433,17 @@ bool ui_searchbox_event( /** Wrap #uiButSearchUpdateFn callback. */ static void ui_searchbox_update_fn(bContext *C, - uiButSearch *search_but, + uiButSearch *but, const char *str, uiSearchItems *items) { /* While the button is in text editing mode (searchbox open), remove tooltips on every update. */ - if (search_but->but.editstr) { + if (but->editstr) { wmWindow *win = CTX_wm_window(C); WM_tooltip_clear(C, win); } - const bool is_first_search = !search_but->but.changed; - search_but->items_update_fn(C, search_but->arg, str, items, is_first_search); + const bool is_first_search = !but->changed; + but->items_update_fn(C, but->arg, str, items, is_first_search); } void ui_searchbox_update(bContext *C, ARegion *region, uiBut *but, const bool reset) @@ -464,7 +464,7 @@ void ui_searchbox_update(bContext *C, ARegion *region, uiBut *but, const bool re data->active = -1; /* On init, find and center active item. */ - const bool is_first_search = !search_but->but.changed; + const bool is_first_search = !but->changed; if (is_first_search && search_but->items_update_fn && search_but->item_active) { data->items.active = search_but->item_active; ui_searchbox_update_fn(C, search_but, but->editstr, &data->items); @@ -704,12 +704,11 @@ static void ui_searchbox_region_listen_fn(const wmRegionListenerParams *params) static ARegion *ui_searchbox_create_generic_ex(bContext *C, ARegion *butregion, - uiButSearch *search_but, + uiButSearch *but, const bool use_shortcut_sep) { wmWindow *win = CTX_wm_window(C); const uiStyle *style = UI_style_get(); - uiBut *but = &search_but->but; const float aspect = but->block->aspect; const int margin = UI_POPUP_MARGIN; @@ -726,8 +725,8 @@ static ARegion *ui_searchbox_create_generic_ex(bContext *C, /* Create search-box data. */ uiSearchboxData *data = MEM_cnew(__func__); - data->search_arg = search_but->arg; - data->search_listener = search_but->listen_fn; + data->search_arg = but->arg; + data->search_listener = but->listen_fn; /* Set font, get the bounding-box. */ data->fstyle = style->widget; /* copy struct */ @@ -751,7 +750,7 @@ static ARegion *ui_searchbox_create_generic_ex(bContext *C, if (but->optype != nullptr || use_shortcut_sep) { data->use_shortcut_sep = true; } - data->sep_string = search_but->item_sep_string; + data->sep_string = but->item_sep_string; /* compute position */ if (but->block->flag & UI_BLOCK_SEARCH_MENU) { @@ -1013,10 +1012,8 @@ ARegion *ui_searchbox_create_menu(bContext *C, ARegion *butregion, uiButSearch * return region; } -void ui_but_search_refresh(uiButSearch *search_but) +void ui_but_search_refresh(uiButSearch *but) { - uiBut *but = &search_but->but; - /* possibly very large lists (such as ID datablocks) only * only validate string RNA buts (not pointers) */ if (but->rnaprop && RNA_property_type(but->rnaprop) != PROP_STRING) { @@ -1033,9 +1030,9 @@ void ui_but_search_refresh(uiButSearch *search_but) items->names[i] = (char *)MEM_callocN(but->hardmax + 1, __func__); } - ui_searchbox_update_fn((bContext *)but->block->evil_C, search_but, but->drawstr, items); + ui_searchbox_update_fn((bContext *)but->block->evil_C, but, but->drawstr, items); - if (!search_but->results_are_suggestions) { + if (!but->results_are_suggestions) { /* Only red-alert when we are sure of it, this can miss cases when >10 matches. */ if (items->totitem == 0) { UI_but_flag_enable(but, UI_BUT_REDALERT); diff --git a/source/blender/editors/interface/interface_templates.cc b/source/blender/editors/interface/interface_templates.cc index 79ff2a32503..7b8fb7344b4 100644 --- a/source/blender/editors/interface/interface_templates.cc +++ b/source/blender/editors/interface/interface_templates.cc @@ -1677,12 +1677,12 @@ static void template_ID_tabs(const bContext *C, 0.0f, 0.0f, ""); - UI_but_funcN_set(&tab->but, template_ID_set_property_exec_fn, MEM_dupallocN(template_id), id); - UI_but_drag_set_id(&tab->but, id); - tab->but.custom_data = (void *)id; + UI_but_funcN_set(tab, template_ID_set_property_exec_fn, MEM_dupallocN(template_id), id); + UI_but_drag_set_id(tab, id); + tab->custom_data = (void *)id; tab->menu = mt; - UI_but_drawflag_enable(&tab->but, but_align); + UI_but_drawflag_enable(tab, but_align); } BLI_freelistN(&ordered); @@ -5639,7 +5639,7 @@ void uiTemplateColorPicker(uiLayout *layout, hsv_but->gradient_type = UI_GRAD_HV; break; } - but = &hsv_but->but; + but = hsv_but; break; /* user default */ @@ -5785,7 +5785,7 @@ void uiTemplateColorPicker(uiLayout *layout, break; } - hsv_but->but.custom_data = cpicker; + hsv_but->custom_data = cpicker; } } @@ -6286,7 +6286,7 @@ void uiTemplateRunningJobs(uiLayout *layout, bContext *C) nullptr); but_progress->progress = progress; - UI_but_func_tooltip_set(&but_progress->but, progress_tooltip_func, tip_arg, MEM_freeN); + UI_but_func_tooltip_set(but_progress, progress_tooltip_func, tip_arg, MEM_freeN); } if (!wm->is_interface_locked) { diff --git a/source/blender/editors/interface/interface_widgets.cc b/source/blender/editors/interface/interface_widgets.cc index 54e380526cb..beddc6c7ae7 100644 --- a/source/blender/editors/interface/interface_widgets.cc +++ b/source/blender/editors/interface/interface_widgets.cc @@ -3152,7 +3152,7 @@ void ui_hsvcube_pos_from_vals( case UI_GRAD_V_ALT: x = 0.5f; /* exception only for value strip - use the range set in but->min/max */ - y = (hsv[2] - hsv_but->but.softmin) / (hsv_but->but.softmax - hsv_but->but.softmin); + y = (hsv[2] - hsv_but->softmin) / (hsv_but->softmax - hsv_but->softmin); break; } diff --git a/source/blender/editors/interface/views/grid_view.cc b/source/blender/editors/interface/views/grid_view.cc index 87fe0b4f608..a46e42c9ad7 100644 --- a/source/blender/editors/interface/views/grid_view.cc +++ b/source/blender/editors/interface/views/grid_view.cc @@ -130,7 +130,7 @@ void AbstractGridViewItem::add_grid_tile_button(uiBlock &block) ""); view_item_but_->view_item = reinterpret_cast(this); - UI_but_func_set(&view_item_but_->but, grid_tile_click_fn, view_item_but_, nullptr); + UI_but_func_set(view_item_but_, grid_tile_click_fn, view_item_but_, nullptr); } void AbstractGridViewItem::on_activate() diff --git a/source/blender/editors/interface/views/tree_view.cc b/source/blender/editors/interface/views/tree_view.cc index 4fc256d0ffa..04987fad59b 100644 --- a/source/blender/editors/interface/views/tree_view.cc +++ b/source/blender/editors/interface/views/tree_view.cc @@ -136,7 +136,7 @@ void AbstractTreeViewItem::add_treerow_button(uiBlock &block) &block, UI_BTYPE_VIEW_ITEM, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); view_item_but_->view_item = reinterpret_cast(this); - UI_but_func_set(&view_item_but_->but, tree_row_click_fn, view_item_but_, nullptr); + UI_but_func_set(view_item_but_, tree_row_click_fn, view_item_but_, nullptr); } void AbstractTreeViewItem::add_indent(uiLayout &row) const @@ -322,8 +322,8 @@ bool AbstractTreeViewItem::is_hovered() const /* The new layout hasn't finished construction yet, so the final state of the button is unknown. * Get the matching button from the previous redraw instead. */ uiButViewItem *old_item_but = ui_block_view_find_matching_view_item_but_in_old_block( - view_item_but_->but.block, this_item_handle); - return old_item_but && (old_item_but->but.flag & UI_ACTIVE); + view_item_but_->block, this_item_handle); + return old_item_but && (old_item_but->flag & UI_ACTIVE); } bool AbstractTreeViewItem::is_collapsed() const -- 2.30.2 From 3a34c9144862742e773363628295995531d6f0f8 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 31 Jan 2023 17:32:05 +0100 Subject: [PATCH 17/49] Cleanup: Remove unnecessary macro & unnecessary cast --- source/blender/editors/interface/interface.cc | 5 +---- source/blender/editors/interface/interface_anim.cc | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index 73f2efb6d19..bf1f276d702 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -3982,8 +3982,6 @@ static uiBut *ui_but_new(const eButType type) { uiBut *but = nullptr; -#define NEW_BUT(type_name) MEM_new(#type_name) - switch (type) { case UI_BTYPE_NUM: but = MEM_new("uiButNumber"); @@ -4022,10 +4020,9 @@ static uiBut *ui_but_new(const eButType type) but = MEM_new("uiButViewItem"); break; default: - but = NEW_BUT(uiBut); + but = MEM_new("uiBut"); break; } -#undef NEW_BUT but->type = type; return but; diff --git a/source/blender/editors/interface/interface_anim.cc b/source/blender/editors/interface/interface_anim.cc index 78dccbaf1f8..c0b5f1f46f4 100644 --- a/source/blender/editors/interface/interface_anim.cc +++ b/source/blender/editors/interface/interface_anim.cc @@ -121,7 +121,7 @@ static uiBut *ui_but_anim_decorate_find_attached_button(uiButDecorator *but) BLI_assert(but->rnapoin.data && but->rnaprop); LISTBASE_CIRCULAR_BACKWARD_BEGIN (uiBut *, &but->block->buttons, but_iter, but->prev) { - if (but_iter != (uiBut *)but && + if (but_iter != but && ui_but_rna_equals_ex(but_iter, &but->rnapoin, but->rnaprop, but->rnaindex)) { return but_iter; } -- 2.30.2 From c4176781d3f8a97c1e20399b9d708edae98d73e0 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 31 Jan 2023 17:37:36 +0100 Subject: [PATCH 18/49] Fix assert failures because of unexpected RNA index default --- source/blender/editors/interface/interface_intern.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/interface/interface_intern.hh b/source/blender/editors/interface/interface_intern.hh index e62a19ec146..b19a04145d1 100644 --- a/source/blender/editors/interface/interface_intern.hh +++ b/source/blender/editors/interface/interface_intern.hh @@ -246,7 +246,7 @@ struct uiBut { /* RNA data */ PointerRNA rnapoin = {}; PropertyRNA *rnaprop = nullptr; - int rnaindex = -1; + int rnaindex = 0; /* Operator data */ wmOperatorType *optype = nullptr; -- 2.30.2 From d5c60f912f457a58d45b36f5e9c89a6b0cce9d69 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 1 Feb 2023 12:47:52 +0100 Subject: [PATCH 19/49] Tabs to activate a catalog The tabs should be fully working themselves, however we don't filter the asset shelf contents based on the active catalog (well, catalog path) yet. Includes the changes from D17164. --- .../editors/asset/intern/asset_shelf.cc | 63 ++++++++++++++-- source/blender/editors/include/UI_interface.h | 2 - .../blender/editors/include/UI_interface.hh | 4 ++ source/blender/editors/interface/interface.cc | 15 +++- .../editors/interface/interface_handlers.cc | 72 ++++++++++--------- .../editors/interface/interface_intern.hh | 8 ++- source/blender/makesdna/DNA_screen_types.h | 1 + 7 files changed, 122 insertions(+), 43 deletions(-) diff --git a/source/blender/editors/asset/intern/asset_shelf.cc b/source/blender/editors/asset/intern/asset_shelf.cc index 3ea457a9056..dedbc58f036 100644 --- a/source/blender/editors/asset/intern/asset_shelf.cc +++ b/source/blender/editors/asset/intern/asset_shelf.cc @@ -104,9 +104,23 @@ static void asset_shelf_settings_clear_enabled_catalogs(AssetShelfSettings &shel BLI_assert(BLI_listbase_is_empty(&shelf_settings.enabled_catalog_paths)); } +static void asset_shelf_settings_set_active_catalog(AssetShelfSettings &shelf_settings, + const asset_system::AssetCatalogPath &path) +{ + MEM_delete(shelf_settings.active_catalog_path); + shelf_settings.active_catalog_path = BLI_strdupn(path.c_str(), path.length()); +} + +static bool asset_shelf_settings_is_active_catalog(const AssetShelfSettings &shelf_settings, + const asset_system::AssetCatalogPath &path) +{ + return shelf_settings.active_catalog_path && shelf_settings.active_catalog_path == path.str(); +} + void ED_asset_shelf_settings_free(AssetShelfSettings *shelf_settings) { asset_shelf_settings_clear_enabled_catalogs(*shelf_settings); + MEM_delete(shelf_settings->active_catalog_path); } void ED_asset_shelf_settings_blend_write(BlendWriter *writer, @@ -373,6 +387,50 @@ static uiBlock *asset_shelf_catalog_selector_block_draw(bContext *C, /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Catalog toggle buttons + * \{ */ + +static void add_catalog_toggle_buttons(AssetShelfSettings &shelf_settings, uiLayout &layout) +{ + uiBlock *block = uiLayoutGetBlock(&layout); + const uiStyle *style = UI_style_get_dpi(); + + asset_shelf_settings_foreach_enabled_catalog_path( + shelf_settings, [&shelf_settings, block, style](const asset_system::AssetCatalogPath &path) { + const char *name = path.name().c_str(); + const int string_width = UI_fontstyle_string_width(&style->widget, name); + const int pad_x = UI_UNIT_X * 0.3f; + const int but_width = std::min(string_width + 2 * pad_x, UI_UNIT_X * 8); + + uiBut *but = uiDefBut( + block, + UI_BTYPE_TAB, + 0, + name, + 0, + 0, + but_width, + UI_UNIT_Y, + nullptr, + 0, + 0, + 0, + 0, + "Enable catalog, making contained assets visible in the asset shelf"); + + UI_but_drawflag_enable(but, UI_BUT_ALIGN_TOP); + UI_but_func_set(but, [&shelf_settings, path](bContext &) { + asset_shelf_settings_set_active_catalog(shelf_settings, path); + }); + UI_but_func_pushed_state_set(but, [&shelf_settings, path](const uiBut &) -> bool { + return asset_shelf_settings_is_active_catalog(shelf_settings, path); + }); + }); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Asset Shelf Footer * @@ -399,10 +457,7 @@ static void asset_shelf_footer_draw(const bContext *C, Header *header) AssetShelfSettings *shelf_settings = get_asset_shelf_settings_from_context(C); if (shelf_settings) { - asset_shelf_settings_foreach_enabled_catalog_path( - *shelf_settings, [layout](const asset_system::AssetCatalogPath &path) { - uiItemL(layout, path.name().c_str(), ICON_NONE); - }); + add_catalog_toggle_buttons(*shelf_settings, *layout); } } diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index bd8046fe223..06cb79818a2 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -1748,8 +1748,6 @@ void UI_but_focus_on_enter_event(struct wmWindow *win, uiBut *but); void UI_but_func_hold_set(uiBut *but, uiButHandleHoldFunc func, void *argN); -void UI_but_func_pushed_state_set(uiBut *but, uiButPushedStateFunc func, const void *arg); - struct PointerRNA *UI_but_extra_operator_icon_add(uiBut *but, const char *opname, wmOperatorCallContext opcontext, diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh index 4d3e40b30fc..d5cd30cfc38 100644 --- a/source/blender/editors/include/UI_interface.hh +++ b/source/blender/editors/include/UI_interface.hh @@ -17,6 +17,7 @@ namespace blender::nodes::geo_eval_log { struct GeometryAttributeInfo; } +struct bContext; struct StructRNA; struct uiBlock; struct uiBut; @@ -54,6 +55,9 @@ void attribute_search_add_items(StringRefNull str, } // namespace blender::ui +void UI_but_func_set(uiBut *but, std::function func); +void UI_but_func_pushed_state_set(uiBut *but, std::function func); + /** * Override this for all available view types. */ diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index bf1f276d702..6f2d96b8a47 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -51,6 +51,7 @@ #include "BLT_translation.h" #include "UI_interface.h" +#include "UI_interface.hh" #include "UI_interface_icons.h" #include "UI_view2d.h" @@ -749,6 +750,10 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut) if (but->func != oldbut->func) { return false; } + if (but->apply_func.target() != + oldbut->apply_func.target()) { + return false; + } if (but->funcN != oldbut->funcN) { return false; } @@ -2181,7 +2186,7 @@ int ui_but_is_pushed_ex(uiBut *but, double *value) { int is_push = 0; if (but->pushed_state_func) { - return but->pushed_state_func(but, but->pushed_state_arg); + return but->pushed_state_func(*but); } if (but->bit) { @@ -6001,6 +6006,11 @@ void UI_but_func_set(uiBut *but, uiButHandleFunc func, void *arg1, void *arg2) but->func_arg2 = arg2; } +void UI_but_func_set(uiBut *but, std::function func) +{ + but->apply_func = func; +} + void UI_but_funcN_set(uiBut *but, uiButHandleNFunc funcN, void *argN, void *arg2) { if (but->func_argN) { @@ -6033,10 +6043,9 @@ void UI_but_func_tooltip_set(uiBut *but, uiButToolTipFunc func, void *arg, uiFre but->tip_arg_free = free_arg; } -void UI_but_func_pushed_state_set(uiBut *but, uiButPushedStateFunc func, const void *arg) +void UI_but_func_pushed_state_set(uiBut *but, std::function func) { but->pushed_state_func = func; - but->pushed_state_arg = arg; ui_but_update(but); } diff --git a/source/blender/editors/interface/interface_handlers.cc b/source/blender/editors/interface/interface_handlers.cc index b828a54d9d3..b3a2c116e9d 100644 --- a/source/blender/editors/interface/interface_handlers.cc +++ b/source/blender/editors/interface/interface_handlers.cc @@ -457,45 +457,47 @@ struct uiHandleButtonData { }; struct uiAfterFunc { - uiAfterFunc *next, *prev; + uiAfterFunc *next = nullptr, *prev = nullptr; - uiButHandleFunc func; - void *func_arg1; - void *func_arg2; + uiButHandleFunc func = nullptr; + void *func_arg1 = nullptr; + void *func_arg2 = nullptr; + /** C++ version of #func above, without need for void pointer arguments. */ + std::function apply_func; - uiButHandleNFunc funcN; - void *func_argN; + uiButHandleNFunc funcN = nullptr; + void *func_argN = nullptr; - uiButHandleRenameFunc rename_func; - void *rename_arg1; - void *rename_orig; + uiButHandleRenameFunc rename_func = nullptr; + void *rename_arg1 = nullptr; + void *rename_orig = nullptr; - uiBlockHandleFunc handle_func; - void *handle_func_arg; - int retval; + uiBlockHandleFunc handle_func = nullptr; + void *handle_func_arg = nullptr; + int retval = 0; - uiMenuHandleFunc butm_func; - void *butm_func_arg; - int a2; + uiMenuHandleFunc butm_func = nullptr; + void *butm_func_arg = nullptr; + int a2 = 0; - wmOperator *popup_op; - wmOperatorType *optype; + wmOperator *popup_op = nullptr; + wmOperatorType *optype = nullptr; wmOperatorCallContext opcontext; - PointerRNA *opptr; + PointerRNA *opptr = nullptr; - PointerRNA rnapoin; - PropertyRNA *rnaprop; + PointerRNA rnapoin = {}; + PropertyRNA *rnaprop = nullptr; - void *search_arg; - uiFreeArgFunc search_arg_free_fn; + void *search_arg = nullptr; + uiFreeArgFunc search_arg_free_fn = nullptr; - uiBlockInteraction_CallbackData custom_interaction_callbacks; - uiBlockInteraction_Handle *custom_interaction_handle; + uiBlockInteraction_CallbackData custom_interaction_callbacks = {}; + uiBlockInteraction_Handle *custom_interaction_handle = nullptr; - bContextStore *context; + bContextStore *context = nullptr; - char undostr[BKE_UNDO_STR_MAX]; - char drawstr[UI_MAX_DRAW_STR]; + char undostr[BKE_UNDO_STR_MAX] = ""; + char drawstr[UI_MAX_DRAW_STR] = ""; }; static void button_activate_init(bContext *C, @@ -743,7 +745,7 @@ static ListBase UIAfterFuncs = {nullptr, nullptr}; static uiAfterFunc *ui_afterfunc_new() { - uiAfterFunc *after = MEM_cnew(__func__); + uiAfterFunc *after = MEM_new(__func__); BLI_addtail(&UIAfterFuncs, after); @@ -800,8 +802,9 @@ static void popup_check(bContext *C, wmOperator *op) */ static bool ui_afterfunc_check(const uiBlock *block, const uiBut *but) { - return (but->func || but->funcN || but->rename_func || but->optype || but->rnaprop || - block->handle_func || (but->type == UI_BTYPE_BUT_MENU && block->butm_func) || + return (but->func || but->apply_func || but->funcN || but->rename_func || but->optype || + but->rnaprop || block->handle_func || + (but->type == UI_BTYPE_BUT_MENU && block->butm_func) || (block->handle && block->handle->popup_op)); } @@ -826,10 +829,11 @@ static void ui_apply_but_func(bContext *C, uiBut *but) else { after->func = but->func; } - after->func_arg1 = but->func_arg1; after->func_arg2 = but->func_arg2; + after->apply_func = but->apply_func; + after->funcN = but->funcN; after->func_argN = (but->func_argN) ? MEM_dupallocN(but->func_argN) : nullptr; @@ -998,7 +1002,8 @@ static void ui_apply_but_funcs_after(bContext *C) LISTBASE_FOREACH_MUTABLE (uiAfterFunc *, afterf, &funcs) { uiAfterFunc after = *afterf; /* Copy to avoid memory leak on exit(). */ - BLI_freelinkN(&funcs, afterf); + BLI_remlink(&funcs, afterf); + MEM_delete(afterf); if (after.context) { CTX_store_set(C, after.context); @@ -1040,6 +1045,9 @@ static void ui_apply_but_funcs_after(bContext *C) if (after.func) { after.func(C, after.func_arg1, after.func_arg2); } + if (after.apply_func) { + after.apply_func(*C); + } if (after.funcN) { after.funcN(C, after.func_argN, after.func_arg2); } diff --git a/source/blender/editors/interface/interface_intern.hh b/source/blender/editors/interface/interface_intern.hh index b19a04145d1..28f8389218d 100644 --- a/source/blender/editors/interface/interface_intern.hh +++ b/source/blender/editors/interface/interface_intern.hh @@ -7,6 +7,8 @@ #pragma once +#include + #include "BLI_compiler_attrs.h" #include "BLI_rect.h" #include "BLI_vector.hh" @@ -199,6 +201,9 @@ struct uiBut { uiButHandleFunc func = nullptr; void *func_arg1 = nullptr; void *func_arg2 = nullptr; + /** C++ version of #func above. Allows storing arbitrary data in a type safe way, no void + * pointer arguments.*/ + std::function apply_func; uiButHandleNFunc funcN = nullptr; void *func_argN = nullptr; @@ -275,8 +280,7 @@ struct uiBut { double *editval = nullptr; float *editvec = nullptr; - uiButPushedStateFunc pushed_state_func = nullptr; - const void *pushed_state_arg = nullptr; + std::function pushed_state_func; /** Little indicator (e.g., counter) displayed on top of some icons. */ IconTextOverlay icon_overlay_text = {}; diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 2e13cc2ce5c..8fdd369f5e3 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -749,6 +749,7 @@ enum { typedef struct AssetShelfSettings { /* TODO make this per mode? (or use a custom identifier?) */ ListBase enabled_catalog_paths; /* #LinkData */ + const char *active_catalog_path; } AssetShelfSettings; #ifdef __cplusplus -- 2.30.2 From 56582fbf820a6eccba3b0ba1c641640540a9e7ef Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 1 Feb 2023 16:26:30 +0100 Subject: [PATCH 20/49] Get filtering by asset catalog to work --- .../editors/asset/intern/asset_shelf.cc | 54 +++++++++++-------- .../interface_template_asset_shelf.cc | 42 +++++++++++++++ .../editors/space_view3d/space_view3d.cc | 1 + 3 files changed, 75 insertions(+), 22 deletions(-) diff --git a/source/blender/editors/asset/intern/asset_shelf.cc b/source/blender/editors/asset/intern/asset_shelf.cc index dedbc58f036..8e9f65c4270 100644 --- a/source/blender/editors/asset/intern/asset_shelf.cc +++ b/source/blender/editors/asset/intern/asset_shelf.cc @@ -34,18 +34,16 @@ using namespace blender; +static void asset_shelf_send_redraw_notifier(bContext &C) +{ + WM_event_add_notifier(&C, NC_SPACE | ND_SPACE_ASSET_SHELF, nullptr); +} + /* -------------------------------------------------------------------- */ /** \name Asset Shelf Regions * \{ */ -void ED_asset_shelf_region_listen(const wmRegionListenerParams *params) -{ - if (ED_assetlist_listen(params->notifier)) { - ED_region_tag_redraw_no_rebuild(params->region); - } -} - -void ED_asset_shelf_footer_region_listen(const wmRegionListenerParams *params) +static void asset_shelf_region_listen(const wmRegionListenerParams *params) { ARegion *region = params->region; const wmNotifier *wmn = params->notifier; @@ -59,6 +57,22 @@ void ED_asset_shelf_footer_region_listen(const wmRegionListenerParams *params) } } +void ED_asset_shelf_region_listen(const wmRegionListenerParams *params) +{ + if (ED_assetlist_listen(params->notifier)) { + ED_region_tag_redraw_no_rebuild(params->region); + } + /* If the asset list didn't catch the notifier, let the region itself listen. */ + else { + asset_shelf_region_listen(params); + } +} + +void ED_asset_shelf_footer_region_listen(const wmRegionListenerParams *params) +{ + asset_shelf_region_listen(params); +} + void ED_asset_shelf_footer_region_init(wmWindowManager * /*wm*/, ARegion *region) { ED_region_header_init(region); @@ -265,7 +279,7 @@ class AssetCatalogSelectorTree : public ui::AbstractTreeView { return view_item; } - void update_shelf_settings_from_enabled_catalogs(const bContext *C); + void update_shelf_settings_from_enabled_catalogs(); class Item : public ui::BasicTreeViewItem { asset_system::AssetCatalogTreeItem catalog_item_; @@ -294,6 +308,7 @@ class AssetCatalogSelectorTree : public ui::AbstractTreeView { void build_row(uiLayout &row) override { + AssetCatalogSelectorTree &tree = dynamic_cast(get_tree_view()); uiBlock *block = uiLayoutGetBlock(&row); uiLayoutSetEmboss(&row, UI_EMBOSS); @@ -316,29 +331,23 @@ class AssetCatalogSelectorTree : public ui::AbstractTreeView { 0, 0, TIP_("Toggle catalog visibility in the asset shelf")); - UI_but_func_set( - but, - [](bContext *C, void *selector_tree_ptr, void *) { - AssetCatalogSelectorTree &selector_tree = *static_cast( - selector_tree_ptr); - selector_tree.update_shelf_settings_from_enabled_catalogs(C); - }, - &dynamic_cast(get_tree_view()), - nullptr); + UI_but_func_set(but, [&tree](bContext &C) { + tree.update_shelf_settings_from_enabled_catalogs(); + asset_shelf_send_redraw_notifier(C); + }); UI_but_flag_disable(but, UI_BUT_UNDO); } }; }; -void AssetCatalogSelectorTree::update_shelf_settings_from_enabled_catalogs(const bContext *C) +void AssetCatalogSelectorTree::update_shelf_settings_from_enabled_catalogs() { asset_shelf_settings_clear_enabled_catalogs(shelf_settings_); - foreach_item([C, this](ui::AbstractTreeViewItem &view_item) { + foreach_item([this](ui::AbstractTreeViewItem &view_item) { const auto &selector_tree_item = dynamic_cast(view_item); if (selector_tree_item.is_catalog_path_enabled()) { asset_shelf_settings_set_catalog_path_enabled(shelf_settings_, selector_tree_item.catalog_path()); - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_ASSET_SHELF, nullptr); } }); } @@ -420,8 +429,9 @@ static void add_catalog_toggle_buttons(AssetShelfSettings &shelf_settings, uiLay "Enable catalog, making contained assets visible in the asset shelf"); UI_but_drawflag_enable(but, UI_BUT_ALIGN_TOP); - UI_but_func_set(but, [&shelf_settings, path](bContext &) { + UI_but_func_set(but, [&shelf_settings, path](bContext &C) { asset_shelf_settings_set_active_catalog(shelf_settings, path); + asset_shelf_send_redraw_notifier(C); }); UI_but_func_pushed_state_set(but, [&shelf_settings, path](const uiBut &) -> bool { return asset_shelf_settings_is_active_catalog(shelf_settings, path); diff --git a/source/blender/editors/interface/interface_template_asset_shelf.cc b/source/blender/editors/interface/interface_template_asset_shelf.cc index 26676a9d8e3..abab93c3612 100644 --- a/source/blender/editors/interface/interface_template_asset_shelf.cc +++ b/source/blender/editors/interface/interface_template_asset_shelf.cc @@ -4,8 +4,11 @@ * \ingroup edinterface */ +#include "AS_asset_library.hh" + #include "BKE_context.h" +#include "DNA_screen_types.h" #include "DNA_space_types.h" #include "ED_asset.h" @@ -14,6 +17,10 @@ #include "UI_resources.h" #include "interface_intern.hh" +#include "RNA_prototypes.h" + +using namespace blender; + /* TODO copy of #asset_view_item_but_drag_set(). */ static void asset_tile_but_drag_set(uiBut &but, AssetHandle &asset_handle) { @@ -69,14 +76,44 @@ static void asset_tile_draw(uiLayout &layout, asset_tile_but_drag_set(*but, asset_handle); } +static std::optional catalog_filter_from_shelf_settings( + const AssetShelfSettings *shelf_settings, const asset_system::AssetLibrary *library) +{ + if (!shelf_settings || !shelf_settings->active_catalog_path) { + return {}; + } + + asset_system ::AssetCatalog *active_catalog = library->catalog_service->find_catalog_by_path( + shelf_settings->active_catalog_path); + if (!active_catalog) { + return {}; + } + + return library->catalog_service->create_catalog_filter(active_catalog->catalog_id); +} + void uiTemplateAssetShelf(uiLayout *layout, const bContext *C, const AssetFilterSettings *filter_settings) { const AssetLibraryReference *library_ref = CTX_wm_asset_library_ref(C); + const PointerRNA shelf_settings_ptr = CTX_data_pointer_get_type( + C, "asset_shelf_settings", &RNA_AssetShelfSettings); + const AssetShelfSettings *shelf_settings = static_cast( + shelf_settings_ptr.data); ED_assetlist_storage_fetch(library_ref, C); ED_assetlist_ensure_previews_job(library_ref, C); + + const asset_system::AssetLibrary *library = ED_assetlist_library_get_once_available( + *library_ref); + if (!library) { + return; + } + + std::optional catalog_filter = + catalog_filter_from_shelf_settings(shelf_settings, library); + uiLayoutSetScaleX(layout, 1.0f); uiLayoutSetScaleY(layout, 1.0f); @@ -94,6 +131,11 @@ void uiTemplateAssetShelf(uiLayout *layout, /* Don't do anything else, but return true to continue iterating. */ return true; } + /* Filter by active catalog. */ + const AssetMetaData *asset_data = ED_asset_handle_get_metadata(&asset); + if (catalog_filter && !catalog_filter->contains(asset_data->catalog_id)) { + return true; + } asset_tile_draw(*row, asset, width, height, show_names); return true; diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc index 560a83b0d1f..694792b8a04 100644 --- a/source/blender/editors/space_view3d/space_view3d.cc +++ b/source/blender/editors/space_view3d/space_view3d.cc @@ -2186,6 +2186,7 @@ void ED_spacetype_view3d() art->prefsizey = HEADERY * 3.5f; art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER; art->listener = ED_asset_shelf_region_listen; + art->context = view3d_asset_shelf_context; art->init = view3d_header_region_init; art->draw = ED_region_header; BLI_addhead(&st->regiontypes, art); -- 2.30.2 From 2110b71f1cc8eac5e7494a827e0f83618e99bab2 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 3 Feb 2023 15:40:32 +0100 Subject: [PATCH 21/49] Allow operators to override asset applying on click through the keymap --- .../editors/asset/intern/asset_shelf.cc | 23 +++++++++++++++++++ source/blender/editors/include/ED_screen.h | 2 +- .../interface_template_asset_shelf.cc | 12 ++++++++++ source/blender/editors/screen/area.c | 4 ++-- .../editors/space_view3d/space_view3d.cc | 5 ++-- 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/asset/intern/asset_shelf.cc b/source/blender/editors/asset/intern/asset_shelf.cc index 8e9f65c4270..6974397abfe 100644 --- a/source/blender/editors/asset/intern/asset_shelf.cc +++ b/source/blender/editors/asset/intern/asset_shelf.cc @@ -207,6 +207,7 @@ int ED_asset_shelf_context(const bContext *C, { static const char *context_dir[] = { "asset_shelf_settings", + "active_file", /* XXX yuk... */ nullptr, }; @@ -223,6 +224,28 @@ int ED_asset_shelf_context(const bContext *C, return CTX_RESULT_OK; } + /* XXX hack. Get the asset from the hovered button, but needs to be the file... */ + if (CTX_data_equals(member, "active_file")) { + const uiBut *but = UI_context_active_but_get(C); + if (!but) { + return CTX_RESULT_NO_DATA; + } + + const bContextStore *but_context = UI_but_context_get(but); + if (!but_context) { + return CTX_RESULT_NO_DATA; + } + + const PointerRNA *file_ptr = CTX_store_ptr_lookup( + but_context, "active_file", &RNA_FileSelectEntry); + if (!file_ptr) { + return CTX_RESULT_NO_DATA; + } + + CTX_data_pointer_set_ptr(result, file_ptr); + return CTX_RESULT_OK; + } + return CTX_RESULT_MEMBER_NOT_FOUND; } diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index b71fa39543c..2ad8e68c05d 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -701,7 +701,7 @@ enum { ED_KEYMAP_FOOTER = (1 << 9), ED_KEYMAP_GPENCIL = (1 << 10), ED_KEYMAP_NAVBAR = (1 << 11), - ED_KEYMAP_ASSET_SHELF_FOOTER = (1 << 12), + ED_KEYMAP_ASSET_SHELF = (1 << 12), }; /** #SCREEN_OT_space_context_cycle direction. */ diff --git a/source/blender/editors/interface/interface_template_asset_shelf.cc b/source/blender/editors/interface/interface_template_asset_shelf.cc index abab93c3612..114062716f9 100644 --- a/source/blender/editors/interface/interface_template_asset_shelf.cc +++ b/source/blender/editors/interface/interface_template_asset_shelf.cc @@ -13,6 +13,8 @@ #include "ED_asset.h" +#include "RNA_access.h" + #include "UI_interface.h" #include "UI_resources.h" #include "interface_intern.hh" @@ -53,6 +55,16 @@ static void asset_tile_draw(uiLayout &layout, const int height, const bool show_names) { + PointerRNA file_ptr; + RNA_pointer_create( + nullptr, + &RNA_FileSelectEntry, + /* XXX passing file pointer here, should be asset handle or asset representation. */ + const_cast(asset_handle.file_data), + &file_ptr); + + uiLayoutSetContextPointer(&layout, "active_file", &file_ptr); + uiBlock *block = uiLayoutGetBlock(&layout); uiBut *but = uiDefIconTextBut(block, UI_BTYPE_PREVIEW_TILE, diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 9f77fc80ddd..8fcba51d6f9 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1743,9 +1743,9 @@ static void ed_default_handlers( wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Region Context Menu", 0, 0); WM_event_add_keymap_handler(®ion->handlers, keymap); } - if (flag & ED_KEYMAP_ASSET_SHELF_FOOTER) { + if (flag & ED_KEYMAP_ASSET_SHELF) { /* standard keymap for Navigation bar regions */ - wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Region Context Menu", 0, 0); + wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Asset Shelf", 0, 0); WM_event_add_keymap_handler(®ion->handlers, keymap); } diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc index 694792b8a04..1d27dae2bca 100644 --- a/source/blender/editors/space_view3d/space_view3d.cc +++ b/source/blender/editors/space_view3d/space_view3d.cc @@ -2184,7 +2184,8 @@ void ED_spacetype_view3d() art = MEM_cnew("spacetype view3d asset shelf region"); art->regionid = RGN_TYPE_ASSET_SHELF; art->prefsizey = HEADERY * 3.5f; - art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER; + art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_ASSET_SHELF | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | + ED_KEYMAP_HEADER; art->listener = ED_asset_shelf_region_listen; art->context = view3d_asset_shelf_context; art->init = view3d_header_region_init; @@ -2194,7 +2195,7 @@ void ED_spacetype_view3d() art = MEM_cnew("spacetype view3d asset shelf footer region"); art->regionid = RGN_TYPE_ASSET_SHELF_FOOTER; art->prefsizey = HEADERY; - art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FOOTER; + art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_ASSET_SHELF | ED_KEYMAP_VIEW2D | ED_KEYMAP_FOOTER; art->init = ED_asset_shelf_footer_region_init; art->draw = ED_asset_shelf_footer_region; art->listener = ED_asset_shelf_footer_region_listen; -- 2.30.2 From 002158c26fc78829dea3d77d09bf0df4a857925f Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 3 Feb 2023 15:43:39 +0100 Subject: [PATCH 22/49] Fix asset shelf footer region resizing using wrong coordinates --- source/blender/editors/screen/area.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 8fcba51d6f9..7a21ee5590b 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -944,9 +944,14 @@ static void fullscreen_azone_init(ScrArea *area, ARegion *region) #define AZONEPAD_ICON (0.45f * U.widget_unit) static void region_azone_edge(AZone *az, ARegion *region) { - /* If region is overlapped (transparent background), move #AZone to content. - * Note this is an arbitrary amount that matches nicely with numbers elsewhere. */ - int overlap_padding = (region->overlap) ? (int)(0.4f * U.widget_unit) : 0; + /* If there is no visible region background, users typically expect the #AZone to be closer to + * the content, so move it a bit. Headers-like regions are usually thin and there's not much + * padding around them, so don't touch the #AZone there (also avoids mouse hover conflicts with + * actual contents). + * Note that this is an arbitrary amount that matches nicely with numbers elsewhere. */ + const int overlap_padding = (region->overlap && !RGN_TYPE_IS_HEADER_ANY(region->regiontype)) ? + (int)(0.4f * U.widget_unit) : + 0; switch (az->edge) { case AE_TOP_TO_BOTTOMRIGHT: -- 2.30.2 From baf45e1ac8ab944fead1f41e17cfc499ec422604 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 3 Feb 2023 15:50:22 +0100 Subject: [PATCH 23/49] Show asset name in tooltip, add a small gap before catalog tabs --- source/blender/editors/asset/intern/asset_shelf.cc | 2 ++ .../editors/interface/interface_template_asset_shelf.cc | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/asset/intern/asset_shelf.cc b/source/blender/editors/asset/intern/asset_shelf.cc index 6974397abfe..f770e2e668b 100644 --- a/source/blender/editors/asset/intern/asset_shelf.cc +++ b/source/blender/editors/asset/intern/asset_shelf.cc @@ -488,6 +488,8 @@ static void asset_shelf_footer_draw(const bContext *C, Header *header) UI_UNIT_Y, TIP_("Select catalogs to display")); + uiItemS(layout); + AssetShelfSettings *shelf_settings = get_asset_shelf_settings_from_context(C); if (shelf_settings) { add_catalog_toggle_buttons(*shelf_settings, *layout); diff --git a/source/blender/editors/interface/interface_template_asset_shelf.cc b/source/blender/editors/interface/interface_template_asset_shelf.cc index 114062716f9..d709df573e2 100644 --- a/source/blender/editors/interface/interface_template_asset_shelf.cc +++ b/source/blender/editors/interface/interface_template_asset_shelf.cc @@ -66,11 +66,13 @@ static void asset_tile_draw(uiLayout &layout, uiLayoutSetContextPointer(&layout, "active_file", &file_ptr); uiBlock *block = uiLayoutGetBlock(&layout); + const StringRefNull name = ED_asset_handle_get_name(&asset_handle); + uiBut *but = uiDefIconTextBut(block, UI_BTYPE_PREVIEW_TILE, 0, ED_asset_handle_get_preview_icon_id(&asset_handle), - show_names ? ED_asset_handle_get_name(&asset_handle) : "", + show_names ? name.c_str() : "", 0, 0, width, @@ -80,7 +82,7 @@ static void asset_tile_draw(uiLayout &layout, 0, 0, 0, - ""); + name.c_str()); ui_def_but_icon(but, ED_asset_handle_get_preview_icon_id(&asset_handle), /* NOLINTNEXTLINE: bugprone-suspicious-enum-usage */ -- 2.30.2 From 38833c26e4e3246674bde5c78f334c0735f95a3a Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 20 Feb 2023 17:11:22 +0100 Subject: [PATCH 24/49] Cleanup: Minor correction in comment --- source/blender/editors/screen/area.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 4b6a952ef0a..b7042db445f 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1749,7 +1749,7 @@ static void ed_default_handlers( WM_event_add_keymap_handler(®ion->handlers, keymap); } if (flag & ED_KEYMAP_ASSET_SHELF) { - /* standard keymap for Navigation bar regions */ + /* standard keymap for asset shelf regions */ wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Asset Shelf", 0, 0); WM_event_add_keymap_handler(®ion->handlers, keymap); } -- 2.30.2 From dcb1147eef8b7d283c218694baea58fb215778e6 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 20 Feb 2023 20:46:15 +0100 Subject: [PATCH 25/49] Basic support for registering asset shelf as a type in BPY For example, the pose library add-on can now register an asset shelf like this: ``` class VIEW3D_AST_pose_library(bpy.types.AssetShelf): bl_space_type = "VIEW_3D" @classmethod def poll(cls, context: Context) -> bool: return PoseLibraryPanel.poll(context) ``` Filtering by ID type is not supported yet. This replaces the hack of registering a header type and the asset shelf template to draw into that. --- source/blender/blenkernel/BKE_screen.h | 19 ++ source/blender/editors/asset/ED_asset_shelf.h | 4 + .../editors/asset/intern/asset_filter.cc | 2 +- .../editors/asset/intern/asset_shelf.cc | 52 ++++++ .../editors/space_view3d/space_view3d.cc | 6 +- source/blender/makesdna/DNA_screen_types.h | 7 + source/blender/makesrna/intern/rna_ui.c | 171 ++++++++++++++++++ 7 files changed, 258 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index a86953f35cc..f75669d7da4 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -128,6 +128,9 @@ typedef struct SpaceType { /* region type definitions */ ListBase regiontypes; + /* Asset shelf type definitions */ + ListBase asset_shelf_types; /* AssetShelfType */ + /* read and write... */ /** Default key-maps to add. */ @@ -396,6 +399,22 @@ typedef struct Menu { struct uiLayout *layout; /* runtime for drawing */ } Menu; +/* asset shelf types */ + +typedef struct AssetShelfType { + struct AssetShelfType *next, *prev; + + char idname[BKE_ST_MAXNAME]; /* unique name */ + + int space_type; + + /* Determine if the asset shelf should be visible or not. */ + bool (*poll)(const struct bContext *C, struct AssetShelfType *shelf_type); + + /* RNA integration */ + ExtensionRNA rna_ext; +} AssetShelfType; + /* Space-types. */ struct SpaceType *BKE_spacetype_from_id(int spaceid); diff --git a/source/blender/editors/asset/ED_asset_shelf.h b/source/blender/editors/asset/ED_asset_shelf.h index 8298cf8f042..11b1f4c2808 100644 --- a/source/blender/editors/asset/ED_asset_shelf.h +++ b/source/blender/editors/asset/ED_asset_shelf.h @@ -23,6 +23,10 @@ struct wmWindowManager; /** Only needed for #RGN_TYPE_ASSET_SHELF (not #RGN_TYPE_ASSET_SHELF_FOOTER). */ void ED_asset_shelf_region_listen(const struct wmRegionListenerParams *params); +void ED_asset_shelf_region_draw(const bContext *C, struct ARegion *region); +void ED_asset_shelf_region_register(ARegionType *region_type, + const char *idname, + const int space_type); void ED_asset_shelf_footer_region_init(struct wmWindowManager *wm, struct ARegion *region); void ED_asset_shelf_footer_region(const struct bContext *C, struct ARegion *region); diff --git a/source/blender/editors/asset/intern/asset_filter.cc b/source/blender/editors/asset/intern/asset_filter.cc index 3bcf42ff206..4b8c34090bd 100644 --- a/source/blender/editors/asset/intern/asset_filter.cc +++ b/source/blender/editors/asset/intern/asset_filter.cc @@ -18,7 +18,7 @@ bool ED_asset_filter_matches_asset(const AssetFilterSettings *filter, const Asse ID_Type asset_type = ED_asset_handle_get_id_type(asset); uint64_t asset_id_filter = BKE_idtype_idcode_to_idfilter(asset_type); - if ((filter->id_types & asset_id_filter) == 0) { + if (filter->id_types && (filter->id_types & asset_id_filter) == 0) { return false; } /* Not very efficient (O(n^2)), could be improved quite a bit. */ diff --git a/source/blender/editors/asset/intern/asset_shelf.cc b/source/blender/editors/asset/intern/asset_shelf.cc index f770e2e668b..249d55c172a 100644 --- a/source/blender/editors/asset/intern/asset_shelf.cc +++ b/source/blender/editors/asset/intern/asset_shelf.cc @@ -54,6 +54,12 @@ static void asset_shelf_region_listen(const wmRegionListenerParams *params) ED_region_tag_redraw(region); } break; + case NC_SCENE: + /* Asset shelf polls typically check the mode. */ + if (ELEM(wmn->data, ND_MODE)) { + ED_region_tag_redraw(region); + } + break; } } @@ -68,6 +74,51 @@ void ED_asset_shelf_region_listen(const wmRegionListenerParams *params) } } +/** + * Check if there is any asset shelf type returning true in it's poll. If not, no asset shelf + * region should be displayed. + */ +static bool asset_shelf_region_header_type_poll(const bContext *C, HeaderType * /*header_type*/) +{ + const SpaceLink *space_link = CTX_wm_space_data(C); + const SpaceType *space_type = BKE_spacetype_from_id(space_link->spacetype); + + /* Is there any asset shelf type registered that returns true for it's poll? */ + LISTBASE_FOREACH (AssetShelfType *, shelf_type, &space_type->asset_shelf_types) { + if (shelf_type->poll && shelf_type->poll(C, shelf_type)) { + return true; + } + } + + return false; +} + +void ED_asset_shelf_region_draw(const bContext *C, ARegion *region) +{ + ED_region_header(C, region); +} + +static void asset_shelf_region_draw(const bContext *C, Header *header) +{ + uiLayout *layout = header->layout; + AssetFilterSettings dummy_filter_settings{0}; + + uiTemplateAssetShelf(layout, C, &dummy_filter_settings); +} + +void ED_asset_shelf_region_register(ARegionType *region_type, + const char *idname, + const int space_type) +{ + HeaderType *ht = MEM_cnew(__func__); + strcpy(ht->idname, idname); + ht->space_type = space_type; + ht->region_type = RGN_TYPE_ASSET_SHELF_FOOTER; + ht->draw = asset_shelf_region_draw; + ht->poll = asset_shelf_region_header_type_poll; + BLI_addtail(®ion_type->headertypes, ht); +} + void ED_asset_shelf_footer_region_listen(const wmRegionListenerParams *params) { asset_shelf_region_listen(params); @@ -505,6 +556,7 @@ void ED_asset_shelf_footer_register(ARegionType *region_type, ht->space_type = space_type; ht->region_type = RGN_TYPE_ASSET_SHELF_FOOTER; ht->draw = asset_shelf_footer_draw; + ht->poll = asset_shelf_region_header_type_poll; BLI_addtail(®ion_type->headertypes, ht); } diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc index 219e3919439..ade5cf6d25f 100644 --- a/source/blender/editors/space_view3d/space_view3d.cc +++ b/source/blender/editors/space_view3d/space_view3d.cc @@ -2243,8 +2243,10 @@ void ED_spacetype_view3d() art->listener = ED_asset_shelf_region_listen; art->context = view3d_asset_shelf_context; art->init = view3d_header_region_init; - art->draw = ED_region_header; + art->draw = ED_asset_shelf_region_draw; BLI_addhead(&st->regiontypes, art); + ED_asset_shelf_region_register(art, "VIEW3D_HT_asset_shelf_main", SPACE_VIEW3D); + /* regions: asset shelf footer */ art = MEM_cnew("spacetype view3d asset shelf footer region"); art->regionid = RGN_TYPE_ASSET_SHELF_FOOTER; @@ -2255,7 +2257,7 @@ void ED_spacetype_view3d() art->listener = ED_asset_shelf_footer_region_listen; art->context = view3d_asset_shelf_context; BLI_addhead(&st->regiontypes, art); - ED_asset_shelf_footer_register(art, "VIEW3D_HT_asset_shelf", SPACE_VIEW3D); + ED_asset_shelf_footer_register(art, "VIEW3D_HT_asset_shelf_footer", SPACE_VIEW3D); /* regions: hud */ art = ED_area_type_hud(st->spaceid); diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 2bd5dcd2f56..a212f7f90f4 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -312,6 +312,13 @@ typedef struct uiList { /* some list UI data need to be saved in file */ uiListDyn *dyn_data; } uiList; +typedef struct AssetShelf { + struct AssetShelf *next, *prev; + + /** Runtime. */ + struct AssetShelfType *type; +} AssetShelf; + typedef struct TransformOrientation { struct TransformOrientation *next, *prev; /** MAX_NAME. */ diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index 43fe15736eb..80f3f0a81b1 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -1020,6 +1020,132 @@ static StructRNA *rna_Menu_refine(PointerRNA *mtr) return (menu->type && menu->type->rna_ext.srna) ? menu->type->rna_ext.srna : &RNA_Menu; } +/* Asset Shelf */ + +static bool asset_shelf_poll(const bContext *C, AssetShelfType *shelf_type) +{ + extern FunctionRNA rna_AssetShelf_poll_func; + + PointerRNA ptr; + RNA_pointer_create(NULL, shelf_type->rna_ext.srna, NULL, &ptr); /* dummy */ + FunctionRNA *func = &rna_AssetShelf_poll_func; /* RNA_struct_find_function(&ptr, "poll"); */ + + ParameterList list; + RNA_parameter_list_create(&list, &ptr, func); + RNA_parameter_set_lookup(&list, "context", &C); + shelf_type->rna_ext.call((bContext *)C, &ptr, func, &list); + + void *ret; + RNA_parameter_get_lookup(&list, "visible", &ret); + /* Get the value before freeing. */ + const bool is_visible = *(bool *)ret; + + RNA_parameter_list_free(&list); + + return is_visible; +} + +static void rna_AssetShelf_unregister(Main *UNUSED(bmain), StructRNA *type) +{ + AssetShelfType *shelf_type = RNA_struct_blender_type_get(type); + + if (!shelf_type) { + return; + } + + SpaceType *space_type = BKE_spacetype_from_id(shelf_type->space_type); + if (!space_type) { + return; + } + + RNA_struct_free_extension(type, &shelf_type->rna_ext); + RNA_struct_free(&BLENDER_RNA, type); + + BLI_freelinkN(&space_type->asset_shelf_types, shelf_type); + + /* update while blender is running */ + WM_main_add_notifier(NC_WINDOW, NULL); +} + +static StructRNA *rna_AssetShelf_register(Main *bmain, + ReportList *reports, + void *data, + const char *identifier, + StructValidateFunc validate, + StructCallbackFunc call, + StructFreeFunc free) +{ + AssetShelfType dummy_shelf_type = {NULL}; + AssetShelf dummy_shelf = {NULL}; + PointerRNA dummy_shelf_type_ptr; + + /* setup dummy shelf & shelf type to store static properties in */ + dummy_shelf.type = &dummy_shelf_type; + RNA_pointer_create(NULL, &RNA_AssetShelf, &dummy_shelf, &dummy_shelf_type_ptr); + + int have_function[1]; + + /* validate the python class */ + if (validate(&dummy_shelf_type_ptr, data, have_function) != 0) { + return NULL; + } + + if (strlen(identifier) >= sizeof(dummy_shelf_type.idname)) { + BKE_reportf(reports, + RPT_ERROR, + "Registering asset shelf class: '%s' is too long, maximum length is %d", + identifier, + (int)sizeof(dummy_shelf_type.idname)); + return NULL; + } + + SpaceType *space_type = BKE_spacetype_from_id(dummy_shelf_type.space_type); + if (!space_type) { + return NULL; + } + + /* Check if we have registered this asset shelf type before, and remove it. */ + LISTBASE_FOREACH (AssetShelfType *, iter_shelf_type, &space_type->asset_shelf_types) { + if (STREQ(iter_shelf_type->idname, dummy_shelf_type.idname)) { + if (iter_shelf_type->rna_ext.srna) { + rna_AssetShelf_unregister(bmain, iter_shelf_type->rna_ext.srna); + } + break; + } + } + if (!RNA_struct_available_or_report(reports, dummy_shelf_type.idname)) { + return NULL; + } + if (!RNA_struct_bl_idname_ok_or_report(reports, dummy_shelf_type.idname, "_AST_")) { + return NULL; + } + + /* Create the new shelf type. */ + AssetShelfType *shelf_type = MEM_mallocN(sizeof(*shelf_type), __func__); + memcpy(shelf_type, &dummy_shelf_type, sizeof(*shelf_type)); + + shelf_type->rna_ext.srna = RNA_def_struct_ptr(&BLENDER_RNA, shelf_type->idname, &RNA_AssetShelf); + shelf_type->rna_ext.data = data; + shelf_type->rna_ext.call = call; + shelf_type->rna_ext.free = free; + RNA_struct_blender_type_set(shelf_type->rna_ext.srna, shelf_type); + + shelf_type->poll = have_function[0] ? asset_shelf_poll : NULL; + + BLI_addtail(&space_type->asset_shelf_types, shelf_type); + + /* update while blender is running */ + WM_main_add_notifier(NC_WINDOW, NULL); + + return shelf_type->rna_ext.srna; +} + +static StructRNA *rna_AssetShelf_refine(PointerRNA *shelf_ptr) +{ + AssetShelf *shelf = (AssetShelf *)shelf_ptr->data; + return (shelf->type && shelf->type->rna_ext.srna) ? shelf->type->rna_ext.srna : &RNA_AssetShelf; +} + static void rna_Panel_bl_description_set(PointerRNA *ptr, const char *value) { Panel *data = (Panel *)(ptr->data); @@ -1832,6 +1958,50 @@ static void rna_def_menu(BlenderRNA *brna) RNA_define_verify_sdna(1); } +static void rna_def_asset_shelf(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "AssetShelf", NULL); + RNA_def_struct_ui_text(srna, "Asset Shelf", "Regions for quick access to assets"); + RNA_def_struct_refine_func(srna, "rna_AssetShelf_refine"); + RNA_def_struct_register_funcs( + srna, "rna_AssetShelf_register", "rna_AssetShelf_unregister", NULL); + RNA_def_struct_translation_context(srna, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + RNA_def_struct_flag(srna, STRUCT_PUBLIC_NAMESPACE_INHERIT); + + /* registration */ + + prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "type->idname"); + RNA_def_property_flag(prop, PROP_REGISTER); + RNA_def_property_ui_text(prop, + "ID Name", + "If this is set, the asset gets a custom ID, otherwise it takes the " + "name of the class used to define the menu (for example, if the " + "class name is \"OBJECT_AST_hello\", and bl_idname is not set by the " + "script, then bl_idname = \"OBJECT_AST_hello\")"); + + prop = RNA_def_property(srna, "bl_space_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "type->space_type"); + RNA_def_property_enum_items(prop, rna_enum_space_type_items); + RNA_def_property_flag(prop, PROP_REGISTER); + RNA_def_property_ui_text( + prop, "Space Type", "The space where the asset shelf is going to be used in"); + + PropertyRNA *parm; + FunctionRNA *func; + + func = RNA_def_function(srna, "poll", NULL); + RNA_def_function_ui_description( + func, "If this method returns a non-null output, then the asset shelf will be visible"); + RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_REGISTER_OPTIONAL); + RNA_def_function_return(func, RNA_def_boolean(func, "visible", 1, "", "")); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); +} + static void rna_def_asset_shelf_settings(BlenderRNA *brna) { StructRNA *srna = RNA_def_struct(brna, "AssetShelfSettings", NULL); @@ -1845,6 +2015,7 @@ void RNA_def_ui(BlenderRNA *brna) rna_def_uilist(brna); rna_def_header(brna); rna_def_menu(brna); + rna_def_asset_shelf(brna); rna_def_asset_shelf_settings(brna); } -- 2.30.2 From ac17d0299340da23cccb8b011590972261dac721 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 20 Feb 2023 19:17:03 +0100 Subject: [PATCH 26/49] Nodes: Allow adding multiple search items per type in add menu Add a per node type callback for creating node add search operations, similar to the way link drag search is implemented (11be151d58ec0ca955). Currently the searchable strings have to be separate items in the list. In a separate step, we can look into adding invisible searchable text to search items if that's still necessary. Resolves #102118 Pull Request #104794 --- source/blender/blenkernel/BKE_node.h | 13 ++++ .../editors/space_node/add_node_search.cc | 46 +++++++------- source/blender/nodes/CMakeLists.txt | 2 + source/blender/nodes/NOD_add_node_search.hh | 61 +++++++++++++++++++ .../nodes/composite/node_composite_util.cc | 2 + .../nodes/node_composite_cryptomatte.cc | 1 + .../nodes/node_composite_sepcomb_hsva.cc | 2 + .../nodes/node_composite_sepcomb_rgba.cc | 2 + .../nodes/node_composite_sepcomb_ycca.cc | 2 + .../nodes/node_composite_sepcomb_yuva.cc | 2 + .../nodes/function/node_function_util.cc | 2 + .../nodes/geometry/node_geometry_util.cc | 2 + .../blender/nodes/intern/add_node_search.cc | 25 ++++++++ .../blender/nodes/shader/node_shader_util.cc | 3 + .../nodes/shader/nodes/node_shader_mix.cc | 13 ++++ .../nodes/shader/nodes/node_shader_mix_rgb.cc | 1 + .../shader/nodes/node_shader_sepcomb_hsv.cc | 2 + .../shader/nodes/node_shader_sepcomb_rgb.cc | 2 + .../nodes/shader/nodes/node_shader_squeeze.cc | 1 + 19 files changed, 160 insertions(+), 24 deletions(-) create mode 100644 source/blender/nodes/NOD_add_node_search.hh create mode 100644 source/blender/nodes/intern/add_node_search.cc diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index a0f6405a6ac..d7d17600683 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -98,6 +98,7 @@ class NodeMultiFunctionBuilder; class GeoNodeExecParams; class NodeDeclaration; class NodeDeclarationBuilder; +class GatherAddNodeSearchParams; class GatherLinkSearchOpParams; } // namespace nodes namespace realtime_compositor { @@ -122,6 +123,10 @@ using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket using NodeGatherSocketLinkOperationsFunction = void (*)(blender::nodes::GatherLinkSearchOpParams ¶ms); +/* Adds node add menu operations that are specific to this node type. */ +using NodeGatherAddOperationsFunction = + void (*)(blender::nodes::GatherAddNodeSearchParams ¶ms); + using NodeGetCompositorOperationFunction = blender::realtime_compositor::NodeOperation *(*)(blender::realtime_compositor::Context &context, blender::nodes::DNode node); using NodeGetCompositorShaderNodeFunction = @@ -135,6 +140,7 @@ typedef void *NodeGeometryExecFunction; typedef void *NodeDeclareFunction; typedef void *NodeDeclareDynamicFunction; typedef void *NodeGatherSocketLinkOperationsFunction; +typedef void *NodeGatherAddOperationsFunction; typedef void *SocketGetCPPTypeFunction; typedef void *SocketGetGeometryNodesCPPTypeFunction; typedef void *SocketGetGeometryNodesCPPValueFunction; @@ -353,6 +359,13 @@ typedef struct bNodeType { */ NodeGatherSocketLinkOperationsFunction gather_link_search_ops; + /** + * Add to the list of search items gathered by the add-node search. The default behavior of + * adding a single item with the node name is usually enough, but node types can have any number + * of custom search items. + */ + NodeGatherAddOperationsFunction gather_add_node_search_ops; + /** True when the node cannot be muted. */ bool no_muting; diff --git a/source/blender/editors/space_node/add_node_search.cc b/source/blender/editors/space_node/add_node_search.cc index 16505620e6a..281995f59bc 100644 --- a/source/blender/editors/space_node/add_node_search.cc +++ b/source/blender/editors/space_node/add_node_search.cc @@ -26,6 +26,8 @@ #include "WM_api.h" +#include "NOD_add_node_search.hh" + #include "ED_asset.h" #include "ED_node.h" @@ -36,12 +38,9 @@ struct bContext; namespace blender::ed::space_node { struct AddNodeItem { - std::string ui_name; + nodes::AddNodeInfo info; std::string identifier; - std::string description; std::optional asset; - std::function after_add_fn; - int weight = 0; }; struct AddNodeSearchStorage { @@ -77,11 +76,11 @@ static void search_items_for_asset_metadata(const bNodeTree &node_tree, } AddNodeItem item{}; - item.ui_name = ED_asset_handle_get_name(&asset); + item.info.ui_name = ED_asset_handle_get_name(&asset); item.identifier = node_tree.typeinfo->group_idname; - item.description = asset_data.description == nullptr ? "" : asset_data.description; + item.info.description = asset_data.description == nullptr ? "" : asset_data.description; item.asset = asset; - item.after_add_fn = [asset](const bContext &C, bNodeTree &node_tree, bNode &node) { + item.info.after_add_fn = [asset](const bContext &C, bNodeTree &node_tree, bNode &node) { Main &bmain = *CTX_data_main(&C); node.flag &= ~NODE_OPTIONS; node.id = asset::get_local_id_from_asset_or_append_and_reuse(bmain, asset); @@ -139,9 +138,9 @@ static void gather_search_items_for_node_groups(const bContext &C, continue; } AddNodeItem item{}; - item.ui_name = node_group->id.name + 2; + item.info.ui_name = node_group->id.name + 2; item.identifier = node_tree.typeinfo->group_idname; - item.after_add_fn = [node_group](const bContext &C, bNodeTree &node_tree, bNode &node) { + item.info.after_add_fn = [node_group](const bContext &C, bNodeTree &node_tree, bNode &node) { Main &bmain = *CTX_data_main(&C); node.id = &node_group->id; id_us_plus(node.id); @@ -161,19 +160,18 @@ static void gather_add_node_operations(const bContext &C, if (!(node_type->poll && node_type->poll(node_type, &node_tree, &disabled_hint))) { continue; } - if (StringRefNull(node_tree.typeinfo->group_idname) == node_type->idname) { - /* Skip the empty group type. */ + if (!node_type->gather_add_node_search_ops) { continue; } - if (StringRefNull(node_type->ui_name).endswith("(Legacy)")) { - continue; + Vector info_items; + nodes::GatherAddNodeSearchParams params(*node_type, node_tree, info_items); + node_type->gather_add_node_search_ops(params); + for (nodes::AddNodeInfo &info : info_items) { + AddNodeItem item{}; + item.info = std::move(info); + item.identifier = node_type->idname; + r_search_items.append(item); } - - AddNodeItem item{}; - item.ui_name = IFACE_(node_type->ui_name); - item.identifier = node_type->idname; - item.description = TIP_(node_type->ui_description); - r_search_items.append(std::move(item)); } NODE_TYPES_END; @@ -199,7 +197,7 @@ static void add_node_search_update_fn( StringSearch *search = BLI_string_search_new(); for (AddNodeItem &item : storage.search_add_items) { - BLI_string_search_add(search, item.ui_name.c_str(), &item, item.weight); + BLI_string_search_add(search, item.info.ui_name.c_str(), &item, item.info.weight); } /* Don't filter when the menu is first opened, but still run the search @@ -210,7 +208,7 @@ static void add_node_search_update_fn( for (const int i : IndexRange(filtered_amount)) { AddNodeItem &item = *filtered_items[i]; - if (!UI_search_item_add(items, item.ui_name.c_str(), &item, ICON_NONE, 0, 0)) { + if (!UI_search_item_add(items, item.info.ui_name.c_str(), &item, ICON_NONE, 0, 0)) { break; } } @@ -234,8 +232,8 @@ static void add_node_search_exec_fn(bContext *C, void *arg1, void *arg2) bNode *new_node = nodeAddNode(C, &node_tree, item->identifier.c_str()); BLI_assert(new_node != nullptr); - if (item->after_add_fn) { - item->after_add_fn(*C, node_tree, *new_node); + if (item->info.after_add_fn) { + item->info.after_add_fn(*C, node_tree, *new_node); } new_node->locx = storage.cursor.x / UI_DPI_FAC; @@ -266,7 +264,7 @@ static ARegion *add_node_search_tooltip_fn( uiSearchItemTooltipData tooltip_data{}; BLI_strncpy(tooltip_data.description, - item->asset ? item->description.c_str() : TIP_(item->description.c_str()), + item->asset ? item->info.description.c_str() : TIP_(item->info.description.c_str()), sizeof(tooltip_data.description)); return UI_tooltip_create_from_search_item_generic(C, region, item_rect, &tooltip_data); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index e6c0510e9f5..b20d59ab31f 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -39,6 +39,7 @@ set(INC set(SRC + intern/add_node_search.cc intern/derived_node_tree.cc intern/geometry_nodes_lazy_function.cc intern/geometry_nodes_log.cc @@ -54,6 +55,7 @@ set(SRC intern/node_util.cc intern/socket_search_link.cc + NOD_add_node_search.hh NOD_common.h NOD_composite.h NOD_derived_node_tree.hh diff --git a/source/blender/nodes/NOD_add_node_search.hh b/source/blender/nodes/NOD_add_node_search.hh new file mode 100644 index 00000000000..b5b21cfe0d0 --- /dev/null +++ b/source/blender/nodes/NOD_add_node_search.hh @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include + +#include "BLI_function_ref.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "DNA_node_types.h" /* Necessary for eNodeSocketInOut. */ + +#include "NOD_node_declaration.hh" + +struct bContext; + +namespace blender::nodes { + +struct AddNodeInfo { + using AfterAddFn = std::function; + std::string ui_name; + std::string description; + AfterAddFn after_add_fn; + int weight = 0; +}; + +class GatherAddNodeSearchParams { + const bNodeType &node_type_; + const bNodeTree &node_tree_; + Vector &r_items; + + public: + GatherAddNodeSearchParams(const bNodeType &node_type, + const bNodeTree &node_tree, + Vector &r_items) + : node_type_(node_type), node_tree_(node_tree), r_items(r_items) + { + } + + const bNodeTree &node_tree() const + { + return node_tree_; + } + + const bNodeType &node_type() const + { + return node_type_; + } + + /** + * \param weight: Used to customize the order when multiple search items match. + */ + void add_item(std::string ui_name, + std::string description, + AddNodeInfo::AfterAddFn fn = {}, + int weight = 0); +}; + +void search_node_add_ops_for_basic_node(GatherAddNodeSearchParams ¶ms); + +} // namespace blender::nodes diff --git a/source/blender/nodes/composite/node_composite_util.cc b/source/blender/nodes/composite/node_composite_util.cc index 219976e85c0..4d6656c85e7 100644 --- a/source/blender/nodes/composite/node_composite_util.cc +++ b/source/blender/nodes/composite/node_composite_util.cc @@ -7,6 +7,7 @@ #include "BKE_node_runtime.hh" +#include "NOD_add_node_search.hh" #include "NOD_socket_search_link.hh" #include "node_composite_util.hh" @@ -35,4 +36,5 @@ void cmp_node_type_base(bNodeType *ntype, int type, const char *name, short ncla ntype->updatefunc = cmp_node_update_default; ntype->insert_link = node_insert_link_default; ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node; + ntype->gather_add_node_search_ops = blender::nodes::search_node_add_ops_for_basic_node; } diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc index ad170984cd7..9dc24dee5c2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc @@ -428,6 +428,7 @@ void register_node_type_cmp_cryptomatte_legacy() node_type_storage( &ntype, "NodeCryptomatte", file_ns::node_free_cryptomatte, file_ns::node_copy_cryptomatte); ntype.gather_link_search_ops = nullptr; + ntype.gather_add_node_search_ops = nullptr; ntype.get_compositor_operation = legacy_file_ns::get_compositor_operation; ntype.realtime_compositor_unsupported_message = N_( "Node not supported in the Viewport compositor"); diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc index 3483285793a..2d9f6941e4f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc @@ -58,6 +58,7 @@ void register_node_type_cmp_sephsva() &ntype, CMP_NODE_SEPHSVA_LEGACY, "Separate HSVA (Legacy)", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sephsva_declare; ntype.gather_link_search_ops = nullptr; + ntype.gather_add_node_search_ops = nullptr; ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); @@ -112,6 +113,7 @@ void register_node_type_cmp_combhsva() &ntype, CMP_NODE_COMBHSVA_LEGACY, "Combine HSVA (Legacy)", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combhsva_declare; ntype.gather_link_search_ops = nullptr; + ntype.gather_add_node_search_ops = nullptr; ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc index 9308052454d..4181729a5de 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc @@ -58,6 +58,7 @@ void register_node_type_cmp_seprgba() &ntype, CMP_NODE_SEPRGBA_LEGACY, "Separate RGBA (Legacy)", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_seprgba_declare; ntype.gather_link_search_ops = nullptr; + ntype.gather_add_node_search_ops = nullptr; ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); @@ -112,6 +113,7 @@ void register_node_type_cmp_combrgba() &ntype, CMP_NODE_COMBRGBA_LEGACY, "Combine RGBA (Legacy)", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combrgba_declare; ntype.gather_link_search_ops = nullptr; + ntype.gather_add_node_search_ops = nullptr; ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc index 7c1461b4f6e..a7120027b57 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc @@ -86,6 +86,7 @@ void register_node_type_cmp_sepycca() ntype.declare = file_ns::cmp_node_sepycca_declare; ntype.initfunc = file_ns::node_composit_init_mode_sepycca; ntype.gather_link_search_ops = nullptr; + ntype.gather_add_node_search_ops = nullptr; ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); @@ -174,6 +175,7 @@ void register_node_type_cmp_combycca() ntype.declare = file_ns::cmp_node_combycca_declare; ntype.initfunc = file_ns::node_composit_init_mode_combycca; ntype.gather_link_search_ops = nullptr; + ntype.gather_add_node_search_ops = nullptr; ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc index b12e70ad9b8..551d16ec12b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc @@ -58,6 +58,7 @@ void register_node_type_cmp_sepyuva() &ntype, CMP_NODE_SEPYUVA_LEGACY, "Separate YUVA (Legacy)", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sepyuva_declare; ntype.gather_link_search_ops = nullptr; + ntype.gather_add_node_search_ops = nullptr; ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); @@ -112,6 +113,7 @@ void register_node_type_cmp_combyuva() &ntype, CMP_NODE_COMBYUVA_LEGACY, "Combine YUVA (Legacy)", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combyuva_declare; ntype.gather_link_search_ops = nullptr; + ntype.gather_add_node_search_ops = nullptr; ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/function/node_function_util.cc b/source/blender/nodes/function/node_function_util.cc index 2be0d639bdf..fe55f3c9d52 100644 --- a/source/blender/nodes/function/node_function_util.cc +++ b/source/blender/nodes/function/node_function_util.cc @@ -3,6 +3,7 @@ #include "node_function_util.hh" #include "node_util.h" +#include "NOD_add_node_search.hh" #include "NOD_socket_search_link.hh" static bool fn_node_poll_default(const bNodeType * /*ntype*/, @@ -23,4 +24,5 @@ void fn_node_type_base(bNodeType *ntype, int type, const char *name, short nclas ntype->poll = fn_node_poll_default; ntype->insert_link = node_insert_link_default; ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node; + ntype->gather_add_node_search_ops = blender::nodes::search_node_add_ops_for_basic_node; } diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc index 3c830f978d1..516a1994efc 100644 --- a/source/blender/nodes/geometry/node_geometry_util.cc +++ b/source/blender/nodes/geometry/node_geometry_util.cc @@ -10,6 +10,7 @@ #include "BKE_mesh_runtime.h" #include "BKE_pointcloud.h" +#include "NOD_add_node_search.hh" #include "NOD_socket_search_link.hh" namespace blender::nodes { @@ -58,4 +59,5 @@ void geo_node_type_base(bNodeType *ntype, int type, const char *name, short ncla ntype->poll = geo_node_poll_default; ntype->insert_link = node_insert_link_default; ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node; + ntype->gather_add_node_search_ops = blender::nodes::search_node_add_ops_for_basic_node; } diff --git a/source/blender/nodes/intern/add_node_search.cc b/source/blender/nodes/intern/add_node_search.cc new file mode 100644 index 00000000000..241c6f80cb9 --- /dev/null +++ b/source/blender/nodes/intern/add_node_search.cc @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_node.h" + +#include "BLT_translation.h" + +#include "NOD_add_node_search.hh" +#include "NOD_node_declaration.hh" + +namespace blender::nodes { + +void GatherAddNodeSearchParams::add_item(std::string ui_name, + std::string description, + AddNodeInfo::AfterAddFn fn, + int weight) +{ + r_items.append(AddNodeInfo{std::move(ui_name), std::move(description), std::move(fn), weight}); +} + +void search_node_add_ops_for_basic_node(GatherAddNodeSearchParams ¶ms) +{ + params.add_item(IFACE_(params.node_type().ui_name), TIP_(params.node_type().ui_description)); +} + +} // namespace blender::nodes diff --git a/source/blender/nodes/shader/node_shader_util.cc b/source/blender/nodes/shader/node_shader_util.cc index db32495aba4..5b3a9573055 100644 --- a/source/blender/nodes/shader/node_shader_util.cc +++ b/source/blender/nodes/shader/node_shader_util.cc @@ -11,6 +11,7 @@ #include "node_shader_util.hh" +#include "NOD_add_node_search.hh" #include "NOD_socket_search_link.hh" #include "node_exec.h" @@ -44,6 +45,7 @@ void sh_node_type_base(struct bNodeType *ntype, int type, const char *name, shor ntype->poll = sh_node_poll_default; ntype->insert_link = node_insert_link_default; ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node; + ntype->gather_add_node_search_ops = blender::nodes::search_node_add_ops_for_basic_node; } void sh_fn_node_type_base(bNodeType *ntype, int type, const char *name, short nclass) @@ -51,6 +53,7 @@ void sh_fn_node_type_base(bNodeType *ntype, int type, const char *name, short nc sh_node_type_base(ntype, type, name, nclass); ntype->poll = sh_fn_poll_default; ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node; + ntype->gather_add_node_search_ops = blender::nodes::search_node_add_ops_for_basic_node; } /* ****** */ diff --git a/source/blender/nodes/shader/nodes/node_shader_mix.cc b/source/blender/nodes/shader/nodes/node_shader_mix.cc index 6f79749411f..97c349e15a0 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mix.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mix.cc @@ -12,7 +12,9 @@ #include "node_shader_util.hh" +#include "NOD_add_node_search.hh" #include "NOD_socket_search_link.hh" + #include "RNA_enum_types.h" namespace blender::nodes::node_sh_mix_cc { @@ -223,6 +225,16 @@ static void node_mix_gather_link_searches(GatherLinkSearchOpParams ¶ms) } } +static void gather_add_node_searches(GatherAddNodeSearchParams ¶ms) +{ + params.add_item(IFACE_("Mix"), params.node_type().ui_description); + params.add_item(IFACE_("Mix Color"), + params.node_type().ui_description, + [](const bContext & /*C*/, bNodeTree & /*node_tree*/, bNode &node) { + node_storage(node).data_type = SOCK_RGBA; + }); +} + static void node_mix_init(bNodeTree * /*tree*/, bNode *node) { NodeShaderMix *data = MEM_cnew(__func__); @@ -497,5 +509,6 @@ void register_node_type_sh_mix() ntype.draw_buttons = file_ns::sh_node_mix_layout; ntype.labelfunc = file_ns::sh_node_mix_label; ntype.gather_link_search_ops = file_ns::node_mix_gather_link_searches; + ntype.gather_add_node_search_ops = file_ns::gather_add_node_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc index 9abcc66573b..b072db63ab8 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc @@ -156,5 +156,6 @@ void register_node_type_sh_mix_rgb() ntype.gpu_fn = file_ns::gpu_shader_mix_rgb; ntype.build_multi_function = file_ns::sh_node_mix_rgb_build_multi_function; ntype.gather_link_search_ops = nullptr; + ntype.gather_add_node_search_ops = nullptr; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc index 66ff5f82944..3d33f4489fe 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc @@ -40,6 +40,7 @@ void register_node_type_sh_sephsv() ntype.declare = file_ns::node_declare_sephsv; ntype.gpu_fn = file_ns::gpu_shader_sephsv; ntype.gather_link_search_ops = nullptr; + ntype.gather_add_node_search_ops = nullptr; nodeRegisterType(&ntype); } @@ -77,6 +78,7 @@ void register_node_type_sh_combhsv() ntype.declare = file_ns::node_declare_combhsv; ntype.gpu_fn = file_ns::gpu_shader_combhsv; ntype.gather_link_search_ops = nullptr; + ntype.gather_add_node_search_ops = nullptr; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc index d9347cbf0c4..cd2b6683c4d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc @@ -80,6 +80,7 @@ void register_node_type_sh_seprgb() ntype.gpu_fn = file_ns::gpu_shader_seprgb; ntype.build_multi_function = file_ns::sh_node_seprgb_build_multi_function; ntype.gather_link_search_ops = nullptr; + ntype.gather_add_node_search_ops = nullptr; nodeRegisterType(&ntype); } @@ -125,6 +126,7 @@ void register_node_type_sh_combrgb() ntype.gpu_fn = file_ns::gpu_shader_combrgb; ntype.build_multi_function = file_ns::sh_node_combrgb_build_multi_function; ntype.gather_link_search_ops = nullptr; + ntype.gather_add_node_search_ops = nullptr; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_squeeze.cc b/source/blender/nodes/shader/nodes/node_shader_squeeze.cc index c0fe5a21418..9f2e42444ce 100644 --- a/source/blender/nodes/shader/nodes/node_shader_squeeze.cc +++ b/source/blender/nodes/shader/nodes/node_shader_squeeze.cc @@ -36,6 +36,7 @@ void register_node_type_sh_squeeze() sh_node_type_base(&ntype, SH_NODE_SQUEEZE, "Squeeze Value (Legacy)", NODE_CLASS_CONVERTER); ntype.gather_link_search_ops = nullptr; + ntype.gather_add_node_search_ops = nullptr; ntype.declare = file_ns::node_declare; ntype.gpu_fn = file_ns::gpu_shader_squeeze; -- 2.30.2 From 8ff42046ab718e04a7124de6e8772db5fe223672 Mon Sep 17 00:00:00 2001 From: Leon Schittek Date: Mon, 20 Feb 2023 21:22:11 +0100 Subject: [PATCH 27/49] Fix #82936: Make Geometry Nodes modifier icon blue in outliner In the outliner, the icons for modifiers are tinted blue. This didn't work for the geometry nodes modifier icon. Defining the icon with the macro `DEF_ICON_MODIFIER` also defines the appropriate theme color so it's now tinted blue when drawn in the outliner like the other modifier icons. Pull Request #104957 --- source/blender/editors/include/UI_icons.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index 31c766e95f2..6e0edeace6a 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -164,7 +164,7 @@ DEF_ICON(NLA) DEF_ICON(PREFERENCES) DEF_ICON(TIME) DEF_ICON(NODETREE) -DEF_ICON(GEOMETRY_NODES) +DEF_ICON_MODIFIER(GEOMETRY_NODES) DEF_ICON(CONSOLE) DEF_ICON_BLANK(183) DEF_ICON(TRACKER) -- 2.30.2 From eefaa1f8fafea62f2de1d5e1099730c05ce49e0b Mon Sep 17 00:00:00 2001 From: Chris Blackbourn Date: Sun, 19 Feb 2023 11:18:30 +1300 Subject: [PATCH 28/49] BLI_math: fix parameter aliasing in mul_m3_series and mul_m4_series No functional changes. --- source/blender/blenlib/intern/math_matrix.c | 130 ++++++++++-------- .../blenlib/tests/BLI_math_matrix_test.cc | 34 +++++ 2 files changed, 110 insertions(+), 54 deletions(-) diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c index c74a0322ac5..dd35086ec21 100644 --- a/source/blender/blenlib/intern/math_matrix.c +++ b/source/blender/blenlib/intern/math_matrix.c @@ -504,8 +504,9 @@ void _va_mul_m3_series_4(float r[3][3], const float m2[3][3], const float m3[3][3]) { - mul_m3_m3m3(r, m1, m2); - mul_m3_m3m3(r, r, m3); + float s[3][3]; + mul_m3_m3m3(s, m1, m2); + mul_m3_m3m3(r, s, m3); } void _va_mul_m3_series_5(float r[3][3], const float m1[3][3], @@ -513,9 +514,11 @@ void _va_mul_m3_series_5(float r[3][3], const float m3[3][3], const float m4[3][3]) { - mul_m3_m3m3(r, m1, m2); - mul_m3_m3m3(r, r, m3); - mul_m3_m3m3(r, r, m4); + float s[3][3]; + float t[3][3]; + mul_m3_m3m3(s, m1, m2); + mul_m3_m3m3(t, s, m3); + mul_m3_m3m3(r, t, m4); } void _va_mul_m3_series_6(float r[3][3], const float m1[3][3], @@ -524,10 +527,12 @@ void _va_mul_m3_series_6(float r[3][3], const float m4[3][3], const float m5[3][3]) { - mul_m3_m3m3(r, m1, m2); - mul_m3_m3m3(r, r, m3); - mul_m3_m3m3(r, r, m4); - mul_m3_m3m3(r, r, m5); + float s[3][3]; + float t[3][3]; + mul_m3_m3m3(s, m1, m2); + mul_m3_m3m3(t, s, m3); + mul_m3_m3m3(s, t, m4); + mul_m3_m3m3(r, s, m5); } void _va_mul_m3_series_7(float r[3][3], const float m1[3][3], @@ -537,11 +542,13 @@ void _va_mul_m3_series_7(float r[3][3], const float m5[3][3], const float m6[3][3]) { - mul_m3_m3m3(r, m1, m2); - mul_m3_m3m3(r, r, m3); - mul_m3_m3m3(r, r, m4); - mul_m3_m3m3(r, r, m5); - mul_m3_m3m3(r, r, m6); + float s[3][3]; + float t[3][3]; + mul_m3_m3m3(s, m1, m2); + mul_m3_m3m3(t, s, m3); + mul_m3_m3m3(s, t, m4); + mul_m3_m3m3(t, s, m5); + mul_m3_m3m3(r, t, m6); } void _va_mul_m3_series_8(float r[3][3], const float m1[3][3], @@ -552,12 +559,14 @@ void _va_mul_m3_series_8(float r[3][3], const float m6[3][3], const float m7[3][3]) { - mul_m3_m3m3(r, m1, m2); - mul_m3_m3m3(r, r, m3); - mul_m3_m3m3(r, r, m4); - mul_m3_m3m3(r, r, m5); - mul_m3_m3m3(r, r, m6); - mul_m3_m3m3(r, r, m7); + float s[3][3]; + float t[3][3]; + mul_m3_m3m3(s, m1, m2); + mul_m3_m3m3(t, s, m3); + mul_m3_m3m3(s, t, m4); + mul_m3_m3m3(t, s, m5); + mul_m3_m3m3(s, t, m6); + mul_m3_m3m3(r, s, m7); } void _va_mul_m3_series_9(float r[3][3], const float m1[3][3], @@ -569,13 +578,15 @@ void _va_mul_m3_series_9(float r[3][3], const float m7[3][3], const float m8[3][3]) { - mul_m3_m3m3(r, m1, m2); - mul_m3_m3m3(r, r, m3); - mul_m3_m3m3(r, r, m4); - mul_m3_m3m3(r, r, m5); - mul_m3_m3m3(r, r, m6); - mul_m3_m3m3(r, r, m7); - mul_m3_m3m3(r, r, m8); + float s[3][3]; + float t[3][3]; + mul_m3_m3m3(s, m1, m2); + mul_m3_m3m3(t, s, m3); + mul_m3_m3m3(s, t, m4); + mul_m3_m3m3(t, s, m5); + mul_m3_m3m3(s, t, m6); + mul_m3_m3m3(t, s, m7); + mul_m3_m3m3(r, t, m8); } /** \} */ @@ -593,8 +604,9 @@ void _va_mul_m4_series_4(float r[4][4], const float m2[4][4], const float m3[4][4]) { - mul_m4_m4m4(r, m1, m2); - mul_m4_m4m4(r, r, m3); + float s[4][4]; + mul_m4_m4m4(s, m1, m2); + mul_m4_m4m4(r, s, m3); } void _va_mul_m4_series_5(float r[4][4], const float m1[4][4], @@ -602,9 +614,11 @@ void _va_mul_m4_series_5(float r[4][4], const float m3[4][4], const float m4[4][4]) { - mul_m4_m4m4(r, m1, m2); - mul_m4_m4m4(r, r, m3); - mul_m4_m4m4(r, r, m4); + float s[4][4]; + float t[4][4]; + mul_m4_m4m4(s, m1, m2); + mul_m4_m4m4(t, s, m3); + mul_m4_m4m4(r, t, m4); } void _va_mul_m4_series_6(float r[4][4], const float m1[4][4], @@ -613,10 +627,12 @@ void _va_mul_m4_series_6(float r[4][4], const float m4[4][4], const float m5[4][4]) { - mul_m4_m4m4(r, m1, m2); - mul_m4_m4m4(r, r, m3); - mul_m4_m4m4(r, r, m4); - mul_m4_m4m4(r, r, m5); + float s[4][4]; + float t[4][4]; + mul_m4_m4m4(s, m1, m2); + mul_m4_m4m4(t, s, m3); + mul_m4_m4m4(s, t, m4); + mul_m4_m4m4(r, s, m5); } void _va_mul_m4_series_7(float r[4][4], const float m1[4][4], @@ -626,11 +642,13 @@ void _va_mul_m4_series_7(float r[4][4], const float m5[4][4], const float m6[4][4]) { - mul_m4_m4m4(r, m1, m2); - mul_m4_m4m4(r, r, m3); - mul_m4_m4m4(r, r, m4); - mul_m4_m4m4(r, r, m5); - mul_m4_m4m4(r, r, m6); + float s[4][4]; + float t[4][4]; + mul_m4_m4m4(s, m1, m2); + mul_m4_m4m4(t, s, m3); + mul_m4_m4m4(s, t, m4); + mul_m4_m4m4(t, s, m5); + mul_m4_m4m4(r, t, m6); } void _va_mul_m4_series_8(float r[4][4], const float m1[4][4], @@ -641,12 +659,14 @@ void _va_mul_m4_series_8(float r[4][4], const float m6[4][4], const float m7[4][4]) { - mul_m4_m4m4(r, m1, m2); - mul_m4_m4m4(r, r, m3); - mul_m4_m4m4(r, r, m4); - mul_m4_m4m4(r, r, m5); - mul_m4_m4m4(r, r, m6); - mul_m4_m4m4(r, r, m7); + float s[4][4]; + float t[4][4]; + mul_m4_m4m4(s, m1, m2); + mul_m4_m4m4(t, s, m3); + mul_m4_m4m4(s, t, m4); + mul_m4_m4m4(t, s, m5); + mul_m4_m4m4(s, t, m6); + mul_m4_m4m4(r, s, m7); } void _va_mul_m4_series_9(float r[4][4], const float m1[4][4], @@ -658,13 +678,15 @@ void _va_mul_m4_series_9(float r[4][4], const float m7[4][4], const float m8[4][4]) { - mul_m4_m4m4(r, m1, m2); - mul_m4_m4m4(r, r, m3); - mul_m4_m4m4(r, r, m4); - mul_m4_m4m4(r, r, m5); - mul_m4_m4m4(r, r, m6); - mul_m4_m4m4(r, r, m7); - mul_m4_m4m4(r, r, m8); + float s[4][4]; + float t[4][4]; + mul_m4_m4m4(s, m1, m2); + mul_m4_m4m4(t, s, m3); + mul_m4_m4m4(s, t, m4); + mul_m4_m4m4(t, s, m5); + mul_m4_m4m4(s, t, m6); + mul_m4_m4m4(t, s, m7); + mul_m4_m4m4(r, t, m8); } /** \} */ diff --git a/source/blender/blenlib/tests/BLI_math_matrix_test.cc b/source/blender/blenlib/tests/BLI_math_matrix_test.cc index 4a9f7936444..902f1a7df11 100644 --- a/source/blender/blenlib/tests/BLI_math_matrix_test.cc +++ b/source/blender/blenlib/tests/BLI_math_matrix_test.cc @@ -100,6 +100,40 @@ TEST(math_matrix, interp_m3_m3m3_singularity) EXPECT_M3_NEAR(result, expect, 1e-5); } +TEST(math_matrix, mul_m3_series) +{ + float matrix[3][3] = { + {2.0f, 0.0f, 0.0f}, + {0.0f, 3.0f, 0.0f}, + {0.0f, 0.0f, 5.0f}, + }; + mul_m3_series(matrix, matrix, matrix, matrix); + float expect[3][3] = { + {8.0f, 0.0f, 0.0f}, + {0.0f, 27.0f, 0.0f}, + {0.0f, 0.0f, 125.0f}, + }; + EXPECT_M3_NEAR(matrix, expect, 1e-5); +} + +TEST(math_matrix, mul_m4_series) +{ + float matrix[4][4] = { + {2.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 3.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 5.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 7.0f}, + }; + mul_m4_series(matrix, matrix, matrix, matrix); + float expect[4][4] = { + {8.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 27.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 125.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 343.0f}, + }; + EXPECT_M3_NEAR(matrix, expect, 1e-5); +} + namespace blender::tests { using namespace blender::math; -- 2.30.2 From 5de924a65612b0c92bdd026280039a407524fea7 Mon Sep 17 00:00:00 2001 From: Chris Blackbourn Date: Sun, 19 Feb 2023 15:10:36 +1300 Subject: [PATCH 29/49] Fix test Pull Request #104934 --- source/blender/blenlib/tests/BLI_math_matrix_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenlib/tests/BLI_math_matrix_test.cc b/source/blender/blenlib/tests/BLI_math_matrix_test.cc index 902f1a7df11..e04625c4163 100644 --- a/source/blender/blenlib/tests/BLI_math_matrix_test.cc +++ b/source/blender/blenlib/tests/BLI_math_matrix_test.cc @@ -131,7 +131,7 @@ TEST(math_matrix, mul_m4_series) {0.0f, 0.0f, 125.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 343.0f}, }; - EXPECT_M3_NEAR(matrix, expect, 1e-5); + EXPECT_M4_NEAR(matrix, expect, 1e-5); } namespace blender::tests { -- 2.30.2 From b3a5c8df62064e2fa528db798c1c86321eda601d Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 20 Feb 2023 17:46:45 -0800 Subject: [PATCH 30/49] BMesh: fix invalid existence check in BM_mesh_bm_to_me Remember that the null customdata layer index is -1, not 0. --- source/blender/bmesh/intern/bmesh_mesh_convert.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index ea193cc8d85..1781a99c0ab 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -1191,7 +1191,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh need_edgesel |= BM_ELEM_CD_GET_BOOL(l, edgesel_offset); } } - if (pin_layer_index) { + if (pin_layer_index >= 0) { BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { need_pin |= BM_ELEM_CD_GET_BOOL(l, pin_offset); } -- 2.30.2 From 8f2a9bc116884b2088179ecf743b700bacbccb7e Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 20 Feb 2023 18:09:05 -0800 Subject: [PATCH 31/49] Sculpt: Implement mesh filter cancel Added new function sculpt_mesh_filter_cancel in sculpt_filter_mesh.cc for cancelling mesh filters. It currently is unused pending a revamped modal map for mesh filter (see pull req 104718). --- .../sculpt_paint/sculpt_automasking.cc | 2 +- .../sculpt_paint/sculpt_filter_mesh.cc | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index 83ebabd3c26..d7f1e0def14 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -195,7 +195,7 @@ static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush const int automasking_flags = sculpt_automasking_mode_effective_bits(sd, brush); - if (automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY && + if (automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY && brush && sculpt_automasking_is_constrained_by_radius(brush)) { return true; } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc index c10f4995057..7ce0f822330 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc @@ -796,6 +796,40 @@ static void sculpt_mesh_filter_end(bContext *C, wmOperator * /*op*/) SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); } +static void sculpt_mesh_filter_cancel(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + PBVHNode **nodes; + int nodes_num; + + if (!ss || !ss->pbvh) { + return; + } + + /* Gather all PBVH leaf nodes. */ + BKE_pbvh_search_gather(ss->pbvh, nullptr, nullptr, &nodes, &nodes_num); + + for (int i : IndexRange(nodes_num)) { + PBVHNode *node = nodes[i]; + PBVHVertexIter vd; + + SculptOrigVertData orig_data; + SCULPT_orig_vert_data_init(&orig_data, ob, nodes[i], SCULPT_UNDO_COORDS); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + SCULPT_orig_vert_data_update(&orig_data, &vd); + + copy_v3_v3(vd.co, orig_data.co); + } + BKE_pbvh_vertex_iter_end; + + BKE_pbvh_node_mark_update(node); + } + + BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB); +} + static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *event) { Object *ob = CTX_data_active_object(C); -- 2.30.2 From 615958c449a348b59965193382d05965cabde600 Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Mon, 20 Feb 2023 19:04:34 -0800 Subject: [PATCH 32/49] Tests: Add tests for image format saving and loading This adds saving and loading tests for our supported image formats. **Saving - bf_imbuf_save.py** There are 2 template images which are loaded anew for each file save attempt. One is an 8-bit RGBA image and the other 32-bit. This is required as many formats use a variety of factors to determine which of `ibuf->rect` or `ibuf->rectfloat` to use for processing. The templates are constructed to have alpha transparency as well as values > 1 (or clamped to 1 for the case of the 8-bit template). Test flow: - Load in an appropriate template image - Save it to the desired format with the desired set of options - Compare against the reference image Notes: - 98 references are used totaling ~3.6MB - 10-12 second test runtime - Templates can be reconstructed with the create-templates.blend file **Loading - bf_imbuf_load.py** Test flow: - Load in each of the reference images - Save them back out as .exr - Save additional metadata to a secondary file (alpha mode, colorspace etc) - Compare the saved out .exr with another set of reference .exrs - Compare the saved out file metadata with set of reference metadata Notes: - 98 exr references are used totaling ~10MB - 10-12 second test runtime as well A HTML report is not implemented. The diff output organization is very similar to the other tests so it should be somewhat easy to do in the future if we want. The standard set of environment variables are implemented for both: BLENDER_TEST_UPDATE, BLENDER_VERBOSE, and BLENDER_TEST_COLOR Pull Request #104442 --- tests/python/CMakeLists.txt | 43 ++++ tests/python/bl_imbuf_load.py | 179 +++++++++++++++++ tests/python/bl_imbuf_save.py | 272 ++++++++++++++++++++++++++ tests/python/modules/colored_print.py | 46 +++++ tests/python/modules/imbuf_test.py | 92 +++++++++ tests/python/modules/render_report.py | 42 +--- 6 files changed, 634 insertions(+), 40 deletions(-) create mode 100644 tests/python/bl_imbuf_load.py create mode 100644 tests/python/bl_imbuf_save.py create mode 100644 tests/python/modules/colored_print.py create mode 100644 tests/python/modules/imbuf_test.py diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 20a7e2e000a..55c727673c7 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -933,6 +933,49 @@ if(WITH_CODEC_FFMPEG) ) endif() +if(NOT OPENIMAGEIO_IDIFF) + message(STATUS "Disabling ImBuf image format tests because OIIO idiff does not exist") +else() + SET(OPTIONAL_FORMATS "") + if(WITH_IMAGE_CINEON) + set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} CINEON") + endif() + if(WITH_IMAGE_HDR) + set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} HDR") + endif() + if(WITH_IMAGE_OPENEXR) + set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} OPENEXR") + endif() + if(WITH_IMAGE_OPENJPEG) + set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} OPENJPEG") + endif() + if(WITH_IMAGE_TIFF) + set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} TIFF") + endif() + if(WITH_IMAGE_WEBP) + set(OPTIONAL_FORMATS "${OPTIONAL_FORMATS} WEBP") + endif() + + add_blender_test( + bf_imbuf_save + --python ${CMAKE_CURRENT_LIST_DIR}/bl_imbuf_save.py + -- + -test_dir "${TEST_SRC_DIR}/imbuf_io" + -output_dir "${TEST_OUT_DIR}/imbuf_io/save" + -idiff "${OPENIMAGEIO_IDIFF}" + -optional_formats "${OPTIONAL_FORMATS}" + ) + + add_blender_test( + bf_imbuf_load + --python ${CMAKE_CURRENT_LIST_DIR}/bl_imbuf_load.py + -- + -test_dir "${TEST_SRC_DIR}/imbuf_io" + -output_dir "${TEST_OUT_DIR}/imbuf_io/load" + -idiff "${OPENIMAGEIO_IDIFF}" + -optional_formats "${OPTIONAL_FORMATS}" + ) +endif() # ------------------------------------------------------------------------------ # SEQUENCER RENDER TESTS diff --git a/tests/python/bl_imbuf_load.py b/tests/python/bl_imbuf_load.py new file mode 100644 index 00000000000..5e0559aaef5 --- /dev/null +++ b/tests/python/bl_imbuf_load.py @@ -0,0 +1,179 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import pathlib +import sys +import unittest + +import bpy + +sys.path.append(str(pathlib.Path(__file__).parent.absolute())) +from modules.colored_print import print_message +from modules.imbuf_test import AbstractImBufTest + + +args = None + + +class ImBufTest(AbstractImBufTest): + @classmethod + def setUpClass(cls): + AbstractImBufTest.init(args) + + if cls.update: + os.makedirs(cls.reference_load_dir, exist_ok=True) + + def _get_image_files(self, file_pattern): + return [f for f in pathlib.Path(self.reference_dir).glob(file_pattern)] + + def _validate_metadata(self, img, ref_metadata_path, out_metadata_path): + channels = img.channels + is_float = img.is_float + colorspace = img.colorspace_settings.name + alpha_mode = img.alpha_mode + actual_metadata = f"{channels=} {is_float=} {colorspace=} {alpha_mode=}" + + # Save actual metadata + out_metadata_path.write_text(actual_metadata, encoding="utf-8") + + if ref_metadata_path.exists(): + # Compare with expected + try: + expected_metadata = ref_metadata_path.read_text(encoding="utf-8") + + failed = not (actual_metadata == expected_metadata) + except BaseException as e: + if self.verbose: + print_message(e.output.decode("utf-8", 'ignore')) + failed = True + else: + if not self.update: + return False + + failed = True + + if failed and self.update: + # Update reference if requested. + ref_metadata_path.write_text(actual_metadata, encoding="utf-8") + failed = False + + return not failed + + def _save_exr(self, img, out_exr_path): + scene = bpy.data.scenes[0] + image_settings = scene.render.image_settings + image_settings.file_format = "OPEN_EXR" + image_settings.color_mode = "RGBA" + image_settings.color_depth = "32" + image_settings.exr_codec = "ZIP" + + img.save_render(str(out_exr_path), scene=scene) + + def _validate_pixels(self, img, ref_exr_path, out_exr_path): + self._save_exr(img, out_exr_path) + + return self.call_idiff(ref_exr_path, out_exr_path) + + def check(self, file_pattern): + image_files = self._get_image_files(file_pattern) + if len(image_files) == 0: + self.fail(f"No images found for pattern {file_pattern}") + + for image_path in image_files: + print_message(image_path.name, 'SUCCESS', 'RUN') + + # Load the image under test + bpy.ops.image.open(filepath=str(image_path)) + img = bpy.data.images[image_path.name] + + # Compare the image with our exr/metadata references + exr_filename = image_path.with_suffix(".exr").name + metadata_filename = image_path.with_suffix(".txt").name + + ref_exr_path = self.reference_load_dir.joinpath(exr_filename) + ref_metadata_path = self.reference_load_dir.joinpath(metadata_filename) + out_exr_path = self.output_dir.joinpath(exr_filename) + out_metadata_path = self.output_dir.joinpath(metadata_filename) + + res1 = self._validate_metadata(img, ref_metadata_path, out_metadata_path) + res2 = self._validate_pixels(img, ref_exr_path, out_exr_path) + + if not res1 or not res2: + self.errors += 1 + print_message("Results are different from reference data") + print_message(image_path.name, 'FAILURE', 'FAILED') + else: + print_message(image_path.name, 'SUCCESS', 'OK') + + +class ImBufLoadTest(ImBufTest): + def test_load_bmp(self): + self.check("*.bmp") + + def test_load_png(self): + self.check("*.png") + + def test_load_exr(self): + self.skip_if_format_missing("OPENEXR") + + self.check("*.exr") + + def test_load_hdr(self): + self.skip_if_format_missing("HDR") + + self.check("*.hdr") + + def test_load_targa(self): + self.check("*.tga") + + def test_load_tiff(self): + self.skip_if_format_missing("TIFF") + + self.check("*.tif") + + def test_load_jpeg(self): + self.check("*.jpg") + + def test_load_jpeg2000(self): + self.skip_if_format_missing("OPENJPEG") + + self.check("*.jp2") + self.check("*.j2c") + + def test_load_dpx(self): + self.skip_if_format_missing("CINEON") + + self.check("*.dpx") + + def test_load_cineon(self): + self.skip_if_format_missing("CINEON") + + self.check("*.cin") + + def test_load_webp(self): + self.skip_if_format_missing("WEBP") + + self.check("*.webp") + + +def main(): + global args + import argparse + + if '--' in sys.argv: + argv = [sys.argv[0]] + sys.argv[sys.argv.index('--') + 1:] + else: + argv = sys.argv + + parser = argparse.ArgumentParser() + parser.add_argument('-test_dir', required=True, type=pathlib.Path) + parser.add_argument('-output_dir', required=True, type=pathlib.Path) + parser.add_argument('-idiff', required=True, type=pathlib.Path) + parser.add_argument('-optional_formats', required=True) + args, remaining = parser.parse_known_args(argv) + + unittest.main(argv=remaining) + + +if __name__ == '__main__': + main() diff --git a/tests/python/bl_imbuf_save.py b/tests/python/bl_imbuf_save.py new file mode 100644 index 00000000000..6ae0bfa75e3 --- /dev/null +++ b/tests/python/bl_imbuf_save.py @@ -0,0 +1,272 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +import os +import pathlib +import sys +import unittest + +import bpy + +sys.path.append(str(pathlib.Path(__file__).parent.absolute())) +from modules.colored_print import print_message +from modules.imbuf_test import AbstractImBufTest + + +args = None + +TEMPLATE_RGBA08 = "template-rgba08.png" +TEMPLATE_RGBA32 = "template-rgba32.exr" + + +class ImBufTest(AbstractImBufTest): + @classmethod + def setUpClass(cls): + AbstractImBufTest.init(args) + + if cls.update: + os.makedirs(cls.reference_dir, exist_ok=True) + + def _load_template_image(self, name, template_name): + image_path = str(self.test_dir.joinpath(template_name)) + bpy.ops.image.open(filepath=image_path) + img = bpy.data.images[template_name] + img.name = name + return img + + def _setup_image(self, src, ext, settings): + scene = bpy.data.scenes[0] + image_settings = scene.render.image_settings + + # Make an appropriate filename which embeds all relevant settings and + # set the file output parameters for the exact configuration we want + name = "" + for s in settings: + if s == "color_depth": + name += str(settings[s]).rjust(2, '0') + "-" + else: + name += str(settings[s]) + "-" + + setattr(image_settings, s, settings[s]) + + image_name = name[:-1].lower() + "__from__" + src + "." + ext + + return image_name + + def _save_image(self, src, image_name): + loaders = { + "rgba08": lambda name: self._load_template_image(name, TEMPLATE_RGBA08), + "rgba32": lambda name: self._load_template_image(name, TEMPLATE_RGBA32), + } + + # Load the template image and assign it the image name + img = loaders[src](image_name) + + # Save the image in the desired format with the desired settings + scene = bpy.data.scenes[0] + ref_image_path = self.reference_dir.joinpath(img.name) + out_image_path = self.output_dir.joinpath(img.name) + img.save_render(str(out_image_path), scene=scene) + + # Completely remove image in case it was modified during save + img.user_clear() + bpy.data.images.remove(img) + return ref_image_path, out_image_path + + def _validate(self, ref_image_path, out_image_path): + return self.call_idiff(ref_image_path, out_image_path) + + def check(self, src, ext, settings): + image_name = self._setup_image(src, ext, settings) + print_message(image_name, 'SUCCESS', 'RUN') + + ref_image_path, out_image_path = self._save_image(src, image_name) + + if not self._validate(ref_image_path, out_image_path): + self.errors += 1 + print_message("Save result is different from reference image") + print_message(ref_image_path.name, 'FAILURE', 'FAILED') + else: + print_message(ref_image_path.name, 'SUCCESS', 'OK') + + +# autopep8: off +class ImBufSaveTest(ImBufTest): + def test_save_bmp(self): + self.check(src="rgba08", ext="bmp", settings={"file_format": "BMP", "color_mode": "BW"}) + self.check(src="rgba08", ext="bmp", settings={"file_format": "BMP", "color_mode": "RGB"}) + + self.check(src="rgba32", ext="bmp", settings={"file_format": "BMP", "color_mode": "BW"}) + self.check(src="rgba32", ext="bmp", settings={"file_format": "BMP", "color_mode": "RGB"}) + + def test_save_png(self): + self.check(src="rgba08", ext="png", settings={"file_format": "PNG", "color_mode": "BW", "color_depth": "8", "compression": 15}) + self.check(src="rgba08", ext="png", settings={"file_format": "PNG", "color_mode": "RGB", "color_depth": "8", "compression": 15}) + self.check(src="rgba08", ext="png", settings={"file_format": "PNG", "color_mode": "RGBA", "color_depth": "8", "compression": 15}) + self.check(src="rgba08", ext="png", settings={"file_format": "PNG", "color_mode": "BW", "color_depth": "16", "compression": 25}) + self.check(src="rgba08", ext="png", settings={"file_format": "PNG", "color_mode": "RGB", "color_depth": "16", "compression": 25}) + self.check(src="rgba08", ext="png", settings={"file_format": "PNG", "color_mode": "RGBA", "color_depth": "16", "compression": 25}) + + self.check(src="rgba32", ext="png", settings={"file_format": "PNG", "color_mode": "BW", "color_depth": "8", "compression": 15}) + self.check(src="rgba32", ext="png", settings={"file_format": "PNG", "color_mode": "RGB", "color_depth": "8", "compression": 15}) + self.check(src="rgba32", ext="png", settings={"file_format": "PNG", "color_mode": "RGBA", "color_depth": "8", "compression": 15}) + self.check(src="rgba32", ext="png", settings={"file_format": "PNG", "color_mode": "BW", "color_depth": "16", "compression": 25}) + self.check(src="rgba32", ext="png", settings={"file_format": "PNG", "color_mode": "RGB", "color_depth": "16", "compression": 25}) + self.check(src="rgba32", ext="png", settings={"file_format": "PNG", "color_mode": "RGBA", "color_depth": "16", "compression": 25}) + + def test_save_exr(self): + self.skip_if_format_missing("OPENEXR") + + self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "BW", "color_depth": "16", "exr_codec": "ZIP"}) + self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGB", "color_depth": "16", "exr_codec": "DWAA"}) + self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGBA", "color_depth": "16", "exr_codec": "DWAB"}) + self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "BW", "color_depth": "32", "exr_codec": "DWAB"}) + self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGB", "color_depth": "32", "exr_codec": "DWAA"}) + self.check(src="rgba08", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGBA", "color_depth": "32", "exr_codec": "ZIP"}) + + self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "BW", "color_depth": "16", "exr_codec": "ZIP"}) + self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGB", "color_depth": "16", "exr_codec": "DWAA"}) + self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGBA", "color_depth": "16", "exr_codec": "DWAB"}) + self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "BW", "color_depth": "32", "exr_codec": "DWAB"}) + self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGB", "color_depth": "32", "exr_codec": "DWAA"}) + self.check(src="rgba32", ext="exr", settings={"file_format": "OPEN_EXR", "color_mode": "RGBA", "color_depth": "32", "exr_codec": "ZIP"}) + + def test_save_hdr(self): + self.skip_if_format_missing("HDR") + + self.check(src="rgba08", ext="hdr", settings={"file_format": "HDR", "color_mode": "BW"}) + self.check(src="rgba08", ext="hdr", settings={"file_format": "HDR", "color_mode": "RGB"}) + + self.check(src="rgba32", ext="hdr", settings={"file_format": "HDR", "color_mode": "BW"}) + self.check(src="rgba32", ext="hdr", settings={"file_format": "HDR", "color_mode": "RGB"}) + + def test_save_targa(self): + self.check(src="rgba08", ext="tga", settings={"file_format": "TARGA", "color_mode": "BW"}) + self.check(src="rgba08", ext="tga", settings={"file_format": "TARGA", "color_mode": "RGB"}) + self.check(src="rgba08", ext="tga", settings={"file_format": "TARGA", "color_mode": "RGBA"}) + + self.check(src="rgba32", ext="tga", settings={"file_format": "TARGA", "color_mode": "BW"}) + self.check(src="rgba32", ext="tga", settings={"file_format": "TARGA", "color_mode": "RGB"}) + self.check(src="rgba32", ext="tga", settings={"file_format": "TARGA", "color_mode": "RGBA"}) + + def test_save_targa_raw(self): + self.check(src="rgba08", ext="tga", settings={"file_format": "TARGA_RAW", "color_mode": "BW"}) + self.check(src="rgba08", ext="tga", settings={"file_format": "TARGA_RAW", "color_mode": "RGB"}) + self.check(src="rgba08", ext="tga", settings={"file_format": "TARGA_RAW", "color_mode": "RGBA"}) + + self.check(src="rgba32", ext="tga", settings={"file_format": "TARGA_RAW", "color_mode": "BW"}) + self.check(src="rgba32", ext="tga", settings={"file_format": "TARGA_RAW", "color_mode": "RGB"}) + self.check(src="rgba32", ext="tga", settings={"file_format": "TARGA_RAW", "color_mode": "RGBA"}) + + def test_save_tiff(self): + self.skip_if_format_missing("TIFF") + + self.check(src="rgba08", ext="tif", settings={"file_format": "TIFF", "color_mode": "BW", "color_depth": "8", "tiff_codec": "DEFLATE"}) + self.check(src="rgba08", ext="tif", settings={"file_format": "TIFF", "color_mode": "RGB", "color_depth": "8", "tiff_codec": "LZW"}) + self.check(src="rgba08", ext="tif", settings={"file_format": "TIFF", "color_mode": "RGBA", "color_depth": "8", "tiff_codec": "PACKBITS"}) + self.check(src="rgba08", ext="tif", settings={"file_format": "TIFF", "color_mode": "BW", "color_depth": "16", "tiff_codec": "PACKBITS"}) + self.check(src="rgba08", ext="tif", settings={"file_format": "TIFF", "color_mode": "RGB", "color_depth": "16", "tiff_codec": "LZW"}) + self.check(src="rgba08", ext="tif", settings={"file_format": "TIFF", "color_mode": "RGBA", "color_depth": "16", "tiff_codec": "DEFLATE"}) + + self.check(src="rgba32", ext="tif", settings={"file_format": "TIFF", "color_mode": "BW", "color_depth": "8", "tiff_codec": "DEFLATE"}) + self.check(src="rgba32", ext="tif", settings={"file_format": "TIFF", "color_mode": "RGB", "color_depth": "8", "tiff_codec": "LZW"}) + self.check(src="rgba32", ext="tif", settings={"file_format": "TIFF", "color_mode": "RGBA", "color_depth": "8", "tiff_codec": "PACKBITS"}) + self.check(src="rgba32", ext="tif", settings={"file_format": "TIFF", "color_mode": "BW", "color_depth": "16", "tiff_codec": "PACKBITS"}) + self.check(src="rgba32", ext="tif", settings={"file_format": "TIFF", "color_mode": "RGB", "color_depth": "16", "tiff_codec": "LZW"}) + self.check(src="rgba32", ext="tif", settings={"file_format": "TIFF", "color_mode": "RGBA", "color_depth": "16", "tiff_codec": "DEFLATE"}) + + def test_save_jpeg(self): + self.check(src="rgba08", ext="jpg", settings={"file_format": "JPEG", "color_mode": "BW", "quality": 90}) + self.check(src="rgba08", ext="jpg", settings={"file_format": "JPEG", "color_mode": "RGB", "quality": 90}) + + self.check(src="rgba32", ext="jpg", settings={"file_format": "JPEG", "color_mode": "BW", "quality": 70}) + self.check(src="rgba32", ext="jpg", settings={"file_format": "JPEG", "color_mode": "RGB", "quality": 70}) + + def test_save_jpeg2000(self): + self.skip_if_format_missing("OPENJPEG") + + # Is there a better combination of settings we can use so there's not so many? + # Is this a good mix? + self.check(src="rgba08", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "BW", "color_depth": "8", "jpeg2k_codec": "JP2", "quality": 90}) + self.check(src="rgba08", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "BW", "color_depth": "12", "jpeg2k_codec": "JP2", "quality": 90}) + self.check(src="rgba08", ext="j2c", settings={"file_format": "JPEG2000", "color_mode": "BW", "color_depth": "16", "jpeg2k_codec": "J2K", "quality": 90}) + self.check(src="rgba08", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGB", "color_depth": "8", "jpeg2k_codec": "JP2", "quality": 90}) + self.check(src="rgba08", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGB", "color_depth": "12", "jpeg2k_codec": "JP2", "quality": 90}) + self.check(src="rgba08", ext="j2c", settings={"file_format": "JPEG2000", "color_mode": "RGB", "color_depth": "16", "jpeg2k_codec": "J2K", "quality": 90}) + self.check(src="rgba08", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "8", "jpeg2k_codec": "JP2", "quality": 90}) + self.check(src="rgba08", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "12", "jpeg2k_codec": "JP2", "quality": 90}) + self.check(src="rgba08", ext="j2c", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "16", "jpeg2k_codec": "J2K", "quality": 90}) + + self.check(src="rgba32", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "BW", "color_depth": "8", "jpeg2k_codec": "JP2", "quality": 70}) + self.check(src="rgba32", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "BW", "color_depth": "12", "jpeg2k_codec": "JP2", "quality": 70}) + self.check(src="rgba32", ext="j2c", settings={"file_format": "JPEG2000", "color_mode": "BW", "color_depth": "16", "jpeg2k_codec": "J2K", "quality": 70}) + self.check(src="rgba32", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGB", "color_depth": "8", "jpeg2k_codec": "JP2", "quality": 70}) + self.check(src="rgba32", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGB", "color_depth": "12", "jpeg2k_codec": "JP2", "quality": 70}) + self.check(src="rgba32", ext="j2c", settings={"file_format": "JPEG2000", "color_mode": "RGB", "color_depth": "16", "jpeg2k_codec": "J2K", "quality": 70}) + self.check(src="rgba32", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "8", "jpeg2k_codec": "JP2", "quality": 70}) + self.check(src="rgba32", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "12", "jpeg2k_codec": "JP2", "quality": 70}) + self.check(src="rgba32", ext="j2c", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "16", "jpeg2k_codec": "J2K", "quality": 70}) + + # Note: The 'use_jpeg2k_cinema_preset' option mandates very large images + # self.check(src="rgba08", ext="jpg", settings={"file_format":"JPEG2000", "color_mode":"RGBA", "color_depth":"8", "jpeg2k_codec":"JP2", "use_jpeg2k_cinema_preset":True, "use_jpeg2k_cinema_48":False, "use_jpeg2k_ycc":False, "quality":70}) + # self.check(src="rgba32", ext="jpg", settings={"file_format":"JPEG2000", "color_mode":"RGBA", "color_depth":"8", "jpeg2k_codec":"JP2", "use_jpeg2k_cinema_preset":True, "use_jpeg2k_cinema_48":False, "use_jpeg2k_ycc":False, "quality":70}) + + self.check(src="rgba08", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "12", "jpeg2k_codec": "JP2", "use_jpeg2k_cinema_preset": False, "use_jpeg2k_cinema_48": True, "use_jpeg2k_ycc": False, "quality": 70}) + self.check(src="rgba32", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "12", "jpeg2k_codec": "JP2", "use_jpeg2k_cinema_preset": False, "use_jpeg2k_cinema_48": True, "use_jpeg2k_ycc": False, "quality": 70}) + + self.check(src="rgba08", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "16", "jpeg2k_codec": "JP2", "use_jpeg2k_cinema_preset": False, "use_jpeg2k_cinema_48": False, "use_jpeg2k_ycc": True, "quality": 70}) + self.check(src="rgba32", ext="jp2", settings={"file_format": "JPEG2000", "color_mode": "RGBA", "color_depth": "16", "jpeg2k_codec": "JP2", "use_jpeg2k_cinema_preset": False, "use_jpeg2k_cinema_48": False, "use_jpeg2k_ycc": True, "quality": 70}) + + def test_save_dpx(self): + self.skip_if_format_missing("CINEON") + + self.check(src="rgba08", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGB", "color_depth": "8", "use_cineon_log": False}) + self.check(src="rgba08", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGB", "color_depth": "12", "use_cineon_log": False}) + self.check(src="rgba08", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGB", "color_depth": "16", "use_cineon_log": False}) + self.check(src="rgba08", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGBA", "color_depth": "10", "use_cineon_log": True}) + self.check(src="rgba08", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGBA", "color_depth": "12", "use_cineon_log": True}) + self.check(src="rgba08", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGBA", "color_depth": "16", "use_cineon_log": True}) + + self.check(src="rgba32", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGB", "color_depth": "8", "use_cineon_log": False}) + self.check(src="rgba32", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGB", "color_depth": "12", "use_cineon_log": False}) + self.check(src="rgba32", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGB", "color_depth": "16", "use_cineon_log": False}) + self.check(src="rgba32", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGBA", "color_depth": "10", "use_cineon_log": True}) + self.check(src="rgba32", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGBA", "color_depth": "12", "use_cineon_log": True}) + self.check(src="rgba32", ext="dpx", settings={"file_format": "DPX", "color_mode": "RGBA", "color_depth": "16", "use_cineon_log": True}) + + def test_save_cineon(self): + self.skip_if_format_missing("CINEON") + + self.check(src="rgba08", ext="cin", settings={"file_format": "CINEON", "color_mode": "RGB"}) + self.check(src="rgba32", ext="cin", settings={"file_format": "CINEON", "color_mode": "RGB"}) + + def test_save_webp(self): + self.skip_if_format_missing("WEBP") + + self.check(src="rgba08", ext="webp", settings={"file_format": "WEBP", "color_mode": "RGB", "quality": 90}) + self.check(src="rgba08", ext="webp", settings={"file_format": "WEBP", "color_mode": "RGBA", "quality": 90}) + + self.check(src="rgba32", ext="webp", settings={"file_format": "WEBP", "color_mode": "RGB", "quality": 70}) + self.check(src="rgba32", ext="webp", settings={"file_format": "WEBP", "color_mode": "RGBA", "quality": 70}) +# autopep8: on + + +def main(): + global args + import argparse + + if '--' in sys.argv: + argv = [sys.argv[0]] + sys.argv[sys.argv.index('--') + 1:] + else: + argv = sys.argv + + parser = argparse.ArgumentParser() + parser.add_argument('-test_dir', required=True, type=pathlib.Path) + parser.add_argument('-output_dir', required=True, type=pathlib.Path) + parser.add_argument('-idiff', required=True, type=pathlib.Path) + parser.add_argument('-optional_formats', required=True) + args, remaining = parser.parse_known_args(argv) + + unittest.main(argv=remaining) + + +if __name__ == '__main__': + main() diff --git a/tests/python/modules/colored_print.py b/tests/python/modules/colored_print.py new file mode 100644 index 00000000000..f1b9b56df35 --- /dev/null +++ b/tests/python/modules/colored_print.py @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +import sys + + +class COLORS_ANSI: + RED = '\033[00;31m' + GREEN = '\033[00;32m' + ENDC = '\033[0m' + + +class COLORS_NONE: + RED = '' + GREEN = '' + ENDC = '' + + +COLORS = COLORS_NONE + + +def use_message_colors(): + global COLORS, COLORS_ANSI + COLORS = COLORS_ANSI + + +def print_message(message, type=None, status=''): + if type == 'SUCCESS': + print(COLORS.GREEN, end="") + elif type == 'FAILURE': + print(COLORS.RED, end="") + status_text = ... + if status == 'RUN': + status_text = " RUN " + elif status == 'OK': + status_text = " OK " + elif status == 'PASSED': + status_text = " PASSED " + elif status == 'FAILED': + status_text = " FAILED " + else: + status_text = status + if status_text: + print("[{}]" . format(status_text), end="") + print(COLORS.ENDC, end="") + print(" {}" . format(message)) + sys.stdout.flush() diff --git a/tests/python/modules/imbuf_test.py b/tests/python/modules/imbuf_test.py new file mode 100644 index 00000000000..378c7500cb3 --- /dev/null +++ b/tests/python/modules/imbuf_test.py @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import pathlib +import shutil +import subprocess +import unittest + +from .colored_print import (print_message, use_message_colors) + + +class AbstractImBufTest(unittest.TestCase): + @classmethod + def init(cls, args): + cls.test_dir = pathlib.Path(args.test_dir) + cls.reference_dir = pathlib.Path(args.test_dir).joinpath("reference") + cls.reference_load_dir = pathlib.Path(args.test_dir).joinpath("reference_load") + cls.output_dir = pathlib.Path(args.output_dir) + cls.diff_dir = pathlib.Path(args.output_dir).joinpath("diff") + cls.idiff = pathlib.Path(args.idiff) + cls.optional_formats = args.optional_formats + + os.makedirs(cls.diff_dir, exist_ok=True) + + cls.errors = 0 + cls.fail_threshold = 0.001 + cls.fail_percent = 1 + cls.verbose = os.environ.get("BLENDER_VERBOSE") is not None + cls.update = os.getenv('BLENDER_TEST_UPDATE') is not None + if os.environ.get("BLENDER_TEST_COLOR") is not None: + use_message_colors() + + def setUp(self): + self.errors = 0 + print_message("") + + def tearDown(self): + if self.errors > 0: + self.fail("{} errors encountered" . format(self.errors)) + + def skip_if_format_missing(self, format): + if self.optional_formats.find(format) < 0: + self.skipTest("format not available") + + def call_idiff(self, ref_path, out_path): + ref_filepath = str(ref_path) + out_filepath = str(out_path) + out_name = out_path.name + if os.path.exists(ref_filepath): + # Diff images test with threshold. + command = ( + str(self.idiff), + "-fail", str(self.fail_threshold), + "-failpercent", str(self.fail_percent), + ref_filepath, + out_filepath, + ) + try: + subprocess.check_output(command) + failed = False + except subprocess.CalledProcessError as e: + if self.verbose: + print_message(e.output.decode("utf-8", 'ignore')) + failed = e.returncode != 1 + else: + if not self.update: + return False + + failed = True + + if failed and self.update: + # Update reference image if requested. + shutil.copy(out_filepath, ref_filepath) + failed = False + + # Generate diff image. + diff_img = str(self.diff_dir.joinpath(out_name + ".diff.png")) + command = ( + str(self.idiff), + "-o", diff_img, + "-abs", "-scale", "16", + ref_filepath, + out_filepath + ) + + try: + subprocess.check_output(command) + except subprocess.CalledProcessError as e: + if self.verbose: + print_message(e.output.decode("utf-8", 'ignore')) + + return not failed diff --git a/tests/python/modules/render_report.py b/tests/python/modules/render_report.py index 5dcb73b65cc..d206f1338c5 100755 --- a/tests/python/modules/render_report.py +++ b/tests/python/modules/render_report.py @@ -14,44 +14,7 @@ import sys import time from . import global_report - - -class COLORS_ANSI: - RED = '\033[00;31m' - GREEN = '\033[00;32m' - ENDC = '\033[0m' - - -class COLORS_DUMMY: - RED = '' - GREEN = '' - ENDC = '' - - -COLORS = COLORS_DUMMY - - -def print_message(message, type=None, status=''): - if type == 'SUCCESS': - print(COLORS.GREEN, end="") - elif type == 'FAILURE': - print(COLORS.RED, end="") - status_text = ... - if status == 'RUN': - status_text = " RUN " - elif status == 'OK': - status_text = " OK " - elif status == 'PASSED': - status_text = " PASSED " - elif status == 'FAILED': - status_text = " FAILED " - else: - status_text = status - if status_text: - print("[{}]" . format(status_text), end="") - print(COLORS.ENDC, end="") - print(" {}" . format(message)) - sys.stdout.flush() +from .colored_print import (print_message, use_message_colors) def blend_list(dirpath, device, blacklist): @@ -151,8 +114,7 @@ class Report: self.update = os.getenv('BLENDER_TEST_UPDATE') is not None if os.environ.get("BLENDER_TEST_COLOR") is not None: - global COLORS, COLORS_ANSI - COLORS = COLORS_ANSI + use_message_colors() self.failed_tests = "" self.passed_tests = "" -- 2.30.2 From e21911a0c3093fef0a81bb5b0e4bbb8c8d89aa2c Mon Sep 17 00:00:00 2001 From: Chris Blackbourn Date: Tue, 21 Feb 2023 17:07:02 +1300 Subject: [PATCH 33/49] Cleanup: format --- source/blender/blenlib/intern/storage_apple.mm | 11 ++++++----- source/blender/blentranslation/BLT_translation.h | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/source/blender/blenlib/intern/storage_apple.mm b/source/blender/blenlib/intern/storage_apple.mm index bc071872c6d..4e39f0b6874 100644 --- a/source/blender/blenlib/intern/storage_apple.mm +++ b/source/blender/blenlib/intern/storage_apple.mm @@ -15,7 +15,6 @@ #include "BLI_path_util.h" #include "BLI_string.h" - /* Extended file attribute used by OneDrive to mark placeholder files. */ static const char *ONEDRIVE_RECALLONOPEN_ATTRIBUTE = "com.microsoft.OneDrive.RecallOnOpen"; @@ -188,7 +187,8 @@ const char *BLI_expand_tilde(const char *path_with_tilde) return path_expanded; } -char *BLI_current_working_dir(char *dir, const size_t maxncpy) { +char *BLI_current_working_dir(char *dir, const size_t maxncpy) +{ /* Can't just copy to the *dir pointer, as [path getCString gets grumpy.*/ static char path_expanded[PATH_MAX]; @autoreleasepool { @@ -200,10 +200,11 @@ char *BLI_current_working_dir(char *dir, const size_t maxncpy) { } } -bool BLI_change_working_dir(const char* dir) { +bool BLI_change_working_dir(const char *dir) +{ @autoreleasepool { - NSString* path = [[NSString alloc] initWithUTF8String: dir]; - if ([[NSFileManager defaultManager] changeCurrentDirectoryPath: path] == YES) { + NSString *path = [[NSString alloc] initWithUTF8String:dir]; + if ([[NSFileManager defaultManager] changeCurrentDirectoryPath:path] == YES) { return false; } else { diff --git a/source/blender/blentranslation/BLT_translation.h b/source/blender/blentranslation/BLT_translation.h index f788da8d429..1ee40b1d890 100644 --- a/source/blender/blentranslation/BLT_translation.h +++ b/source/blender/blentranslation/BLT_translation.h @@ -126,7 +126,7 @@ const char *BLT_translate_do_new_dataname(const char *msgctxt, const char *msgid #define BLT_I18NCONTEXT_EDITOR_VIEW3D "View3D" #define BLT_I18NCONTEXT_EDITOR_FILEBROWSER "File browser" - /* Generic contexts. */ +/* Generic contexts. */ #define BLT_I18NCONTEXT_VIRTUAL_REALITY "Virtual reality" #define BLT_I18NCONTEXT_CONSTRAINT "Constraint" @@ -194,7 +194,7 @@ typedef struct { BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "id_windowmanager"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_EDITOR_VIEW3D, "editor_view3d"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_EDITOR_FILEBROWSER, "editor_filebrowser"), \ - BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_VIRTUAL_REALITY, "virtual_reality"), \ + BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_VIRTUAL_REALITY, "virtual_reality"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_CONSTRAINT, "constraint"), \ { \ NULL, NULL, NULL \ -- 2.30.2 From 57705d9f98732f298b68b872a264f0599cb01f51 Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Mon, 20 Feb 2023 21:36:19 -0800 Subject: [PATCH 34/49] Tests: Address imbuf_save failures on ARM64 builds This does 2 things to address the ARM64 failures: - Increases the threshold to be inline with what Cycles uses - Disables the 2 problematic WebP variations (#105006 will track) --- tests/python/bl_imbuf_save.py | 5 +++-- tests/python/modules/imbuf_test.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/python/bl_imbuf_save.py b/tests/python/bl_imbuf_save.py index 6ae0bfa75e3..33c249365ae 100644 --- a/tests/python/bl_imbuf_save.py +++ b/tests/python/bl_imbuf_save.py @@ -244,8 +244,9 @@ class ImBufSaveTest(ImBufTest): self.check(src="rgba08", ext="webp", settings={"file_format": "WEBP", "color_mode": "RGB", "quality": 90}) self.check(src="rgba08", ext="webp", settings={"file_format": "WEBP", "color_mode": "RGBA", "quality": 90}) - self.check(src="rgba32", ext="webp", settings={"file_format": "WEBP", "color_mode": "RGB", "quality": 70}) - self.check(src="rgba32", ext="webp", settings={"file_format": "WEBP", "color_mode": "RGBA", "quality": 70}) + # Note: These 2 variations are problematic on MacOS ARM64 (#105006) + # self.check(src="rgba32", ext="webp", settings={"file_format": "WEBP", "color_mode": "RGB", "quality": 70}) + # self.check(src="rgba32", ext="webp", settings={"file_format": "WEBP", "color_mode": "RGBA", "quality": 70}) # autopep8: on diff --git a/tests/python/modules/imbuf_test.py b/tests/python/modules/imbuf_test.py index 378c7500cb3..55d59b5c302 100644 --- a/tests/python/modules/imbuf_test.py +++ b/tests/python/modules/imbuf_test.py @@ -23,7 +23,7 @@ class AbstractImBufTest(unittest.TestCase): os.makedirs(cls.diff_dir, exist_ok=True) cls.errors = 0 - cls.fail_threshold = 0.001 + cls.fail_threshold = 0.016 cls.fail_percent = 1 cls.verbose = os.environ.get("BLENDER_VERBOSE") is not None cls.update = os.getenv('BLENDER_TEST_UPDATE') is not None -- 2.30.2 From ffebf8685f9017c0fdbd4592cc8b2a6de8c8cb9d Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Tue, 21 Feb 2023 09:23:59 +0100 Subject: [PATCH 35/49] Fix: Channel clamping when markers are used Previously when markers were used, the newly introduced clamping code (#104516) would stop the last channel from being shown. This patch fixes that by modifying the `v2d->tot.ymin` calculation. This is a bit counterintuitive since the `v2d->tot` height is calculated in `action_draw.c`. But the advantage of doing it there is that it also works for the channels region. Pull Request #104892 --- source/blender/editors/space_action/action_draw.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c index 5e740f897fc..47f82ffe999 100644 --- a/source/blender/editors/space_action/action_draw.c +++ b/source/blender/editors/space_action/action_draw.c @@ -61,7 +61,8 @@ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *region) items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); const int height = ANIM_UI_get_channels_total_height(v2d, items); - v2d->tot.ymin = -height; + const float pad_bottom = BLI_listbase_is_empty(ac->markers) ? 0 : UI_MARKER_MARGIN_Y; + v2d->tot.ymin = -(height + pad_bottom); /* need to do a view-sync here, so that the keys area doesn't jump around (it must copy this) */ UI_view2d_sync(NULL, ac->area, v2d, V2D_LOCK_COPY); @@ -195,7 +196,8 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region size_t items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); const int height = ANIM_UI_get_channels_total_height(v2d, items); - v2d->tot.ymin = -height; + const float pad_bottom = BLI_listbase_is_empty(ac->markers) ? 0 : UI_MARKER_MARGIN_Y; + v2d->tot.ymin = -(height + pad_bottom); /* Draw the manual frame ranges for actions in the background of the dopesheet. * The action editor has already drawn the range for its action so it's not needed. */ -- 2.30.2 From 37a2e817472afd315ab5d7c9268b58326786cdcf Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Tue, 21 Feb 2023 10:03:54 +0100 Subject: [PATCH 36/49] Fix #104979: GPencil Dot-hash only affects first frame with Time mod The active frame must be recovered using `BKE_gpencil_frame_retime_get` --- source/blender/gpencil_modifiers/intern/MOD_gpencildash.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c index 2ff2a0eb1fa..dfb859542f5 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c @@ -17,6 +17,7 @@ #include "DNA_gpencil_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" +#include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "BKE_gpencil.h" @@ -42,6 +43,7 @@ #include "MOD_gpencil_util.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "WM_api.h" @@ -254,11 +256,11 @@ static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams)) /* Generic "generateStrokes" callback */ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Object *ob) { + Scene *scene = DEG_get_evaluated_scene(depsgraph); bGPdata *gpd = ob->data; LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { - BKE_gpencil_frame_active_set(depsgraph, gpd); - bGPDframe *gpf = gpl->actframe; + bGPDframe *gpf = BKE_gpencil_frame_retime_get(depsgraph, scene, ob, gpl); if (gpf == NULL) { continue; } -- 2.30.2 From 7fe04f68a0f302fb50dd9ee6fc1487a671e910e5 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 20 Feb 2023 15:59:03 +0100 Subject: [PATCH 37/49] Cleanup: Mark overriden virtual call as such Fixes the `-Winconsistent-missing-override` warning. In theory the `virtual` is redundant in such case, but this is how it is done in may other areas of USD code. Pull Request #104977 --- source/blender/io/usd/intern/usd_reader_shape.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/io/usd/intern/usd_reader_shape.h b/source/blender/io/usd/intern/usd_reader_shape.h index b6135f31b2f..0af79a22d93 100644 --- a/source/blender/io/usd/intern/usd_reader_shape.h +++ b/source/blender/io/usd/intern/usd_reader_shape.h @@ -53,7 +53,8 @@ class USDShapeReader : public USDGeomReader { const char ** /*err_str*/) override; bool is_time_varying(); - virtual bool topology_changed(const Mesh * /*existing_mesh*/, double /*motionSampleTime*/) + virtual bool topology_changed(const Mesh * /*existing_mesh*/, + double /*motionSampleTime*/) override { return false; }; -- 2.30.2 From 2d8b1455bcfd76d3ac65c4e966596b8564d5f140 Mon Sep 17 00:00:00 2001 From: Falk David Date: Tue, 21 Feb 2023 11:01:30 +0100 Subject: [PATCH 38/49] Curves: Fix proportional editing not working This adds proper support for proportional editing for the Curves object. Co-authored-by: Hans Goudey Pull Request #104620 --- .../editors/curves/intern/curves_selection.cc | 13 +- source/blender/editors/include/ED_curves.h | 1 + .../editors/transform/transform_convert.c | 4 +- .../transform/transform_convert_curves.cc | 135 +++++++++++++++--- 4 files changed, 129 insertions(+), 24 deletions(-) diff --git a/source/blender/editors/curves/intern/curves_selection.cc b/source/blender/editors/curves/intern/curves_selection.cc index 086662cc85a..82d8ab7c7aa 100644 --- a/source/blender/editors/curves/intern/curves_selection.cc +++ b/source/blender/editors/curves/intern/curves_selection.cc @@ -123,7 +123,7 @@ void fill_selection_true(GMutableSpan selection) } } -static bool contains(const VArray &varray, const bool value) +static bool contains(const VArray &varray, const IndexRange range_to_check, const bool value) { const CommonVArrayInfo info = varray.common_info(); if (info.type == CommonVArrayInfo::Type::Single) { @@ -132,7 +132,7 @@ static bool contains(const VArray &varray, const bool value) if (info.type == CommonVArrayInfo::Type::Span) { const Span span(static_cast(info.data), varray.size()); return threading::parallel_reduce( - span.index_range(), + range_to_check, 4096, false, [&](const IndexRange range, const bool init) { @@ -141,7 +141,7 @@ static bool contains(const VArray &varray, const bool value) [&](const bool a, const bool b) { return a || b; }); } return threading::parallel_reduce( - varray.index_range(), + range_to_check, 2048, false, [&](const IndexRange range, const bool init) { @@ -159,10 +159,15 @@ static bool contains(const VArray &varray, const bool value) [&](const bool a, const bool b) { return a || b; }); } +bool has_anything_selected(const VArray &varray, const IndexRange range_to_check) +{ + return contains(varray, range_to_check, true); +} + bool has_anything_selected(const bke::CurvesGeometry &curves) { const VArray selection = curves.attributes().lookup(".selection"); - return !selection || contains(selection, true); + return !selection || contains(selection, curves.curves_range(), true); } bool has_anything_selected(const GSpan selection) diff --git a/source/blender/editors/include/ED_curves.h b/source/blender/editors/include/ED_curves.h index 178ec7a5b19..788b80a51a2 100644 --- a/source/blender/editors/include/ED_curves.h +++ b/source/blender/editors/include/ED_curves.h @@ -93,6 +93,7 @@ bool has_anything_selected(const bke::CurvesGeometry &curves); * Return true if any element in the span is selected, on either domain with either type. */ bool has_anything_selected(GSpan selection); +bool has_anything_selected(const VArray &varray, IndexRange range_to_check); /** * Find curves that have any point selected (a selection factor greater than zero), diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 211b72c0e77..966c067eb00 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -758,8 +758,8 @@ static void init_proportional_edit(TransInfo *t) else if (t->data_type == &TransConvertType_MeshUV && t->flag & T_PROP_CONNECTED) { /* Already calculated by uv_set_connectivity_distance. */ } - else if (t->data_type == &TransConvertType_Curve) { - BLI_assert(t->obedit_type == OB_CURVES_LEGACY); + else if (ELEM(t->data_type, &TransConvertType_Curve, &TransConvertType_Curves)) { + BLI_assert(t->obedit_type == OB_CURVES_LEGACY || t->obedit_type == OB_CURVES); set_prop_dist(t, false); } else { diff --git a/source/blender/editors/transform/transform_convert_curves.cc b/source/blender/editors/transform/transform_convert_curves.cc index a1c489f3d87..6dd2968ec6f 100644 --- a/source/blender/editors/transform/transform_convert_curves.cc +++ b/source/blender/editors/transform/transform_convert_curves.cc @@ -6,6 +6,7 @@ #include "BLI_array.hh" #include "BLI_index_mask_ops.hh" +#include "BLI_inplace_priority_queue.hh" #include "BLI_span.hh" #include "BKE_curves.hh" @@ -23,11 +24,46 @@ namespace blender::ed::transform::curves { +static void calculate_curve_point_distances_for_proportional_editing( + const Span positions, MutableSpan r_distances) +{ + Array visited(positions.size(), false); + + InplacePriorityQueue> queue(r_distances); + while (!queue.is_empty()) { + int64_t index = queue.pop_index(); + if (visited[index]) { + continue; + } + visited[index] = true; + + /* TODO(Falk): Handle cyclic curves here. */ + if (index > 0 && !visited[index - 1]) { + int adjacent = index - 1; + float dist = r_distances[index] + math::distance(positions[index], positions[adjacent]); + if (dist < r_distances[adjacent]) { + r_distances[adjacent] = dist; + queue.priority_changed(adjacent); + } + } + if (index < positions.size() - 1 && !visited[index + 1]) { + int adjacent = index + 1; + float dist = r_distances[index] + math::distance(positions[index], positions[adjacent]); + if (dist < r_distances[adjacent]) { + r_distances[adjacent] = dist; + queue.priority_changed(adjacent); + } + } + } +} + static void createTransCurvesVerts(bContext * /*C*/, TransInfo *t) { MutableSpan trans_data_contrainers(t->data_container, t->data_container_len); Array> selected_indices_per_object(t->data_container_len); Array selection_per_object(t->data_container_len); + const bool use_proportional_edit = (t->flag & T_PROP_EDIT_ALL) != 0; + const bool use_connected_only = (t->flag & T_PROP_CONNECTED) != 0; /* Count selected elements per object and create TransData structs. */ for (const int i : trans_data_contrainers.index_range()) { @@ -35,10 +71,15 @@ static void createTransCurvesVerts(bContext * /*C*/, TransInfo *t) Curves *curves_id = static_cast(tc.obedit->data); bke::CurvesGeometry &curves = curves_id->geometry.wrap(); - selection_per_object[i] = ed::curves::retrieve_selected_points(curves, - selected_indices_per_object[i]); + if (use_proportional_edit) { + tc.data_len = curves.point_num; + } + else { + selection_per_object[i] = ed::curves::retrieve_selected_points( + curves, selected_indices_per_object[i]); + tc.data_len = selection_per_object[i].size(); + } - tc.data_len = selection_per_object[i].size(); if (tc.data_len > 0) { tc.data = MEM_cnew_array(tc.data_len, __func__); } @@ -52,34 +93,92 @@ static void createTransCurvesVerts(bContext * /*C*/, TransInfo *t) } Curves *curves_id = static_cast(tc.obedit->data); bke::CurvesGeometry &curves = curves_id->geometry.wrap(); - IndexMask selected_indices = selection_per_object[i]; float mtx[3][3], smtx[3][3]; copy_m3_m4(mtx, tc.obedit->object_to_world); pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); MutableSpan positions = curves.positions_for_write(); - threading::parallel_for(selected_indices.index_range(), 1024, [&](const IndexRange range) { - for (const int selection_i : range) { - TransData *td = &tc.data[selection_i]; - float *elem = reinterpret_cast(&positions[selected_indices[selection_i]]); - copy_v3_v3(td->iloc, elem); - copy_v3_v3(td->center, td->iloc); - td->loc = elem; + if (use_proportional_edit) { + const OffsetIndices points_by_curve = curves.points_by_curve(); + const VArray selection = curves.attributes().lookup_or_default( + ".selection", ATTR_DOMAIN_POINT, true); + threading::parallel_for(curves.curves_range(), 512, [&](const IndexRange range) { + Vector closest_distances; + for (const int curve_i : range) { + const IndexRange points = points_by_curve[curve_i]; + const bool has_any_selected = ed::curves::has_anything_selected(selection, points); + if (!has_any_selected) { + for (const int point_i : points) { + TransData &td = tc.data[point_i]; + td.flag |= TD_NOTCONNECTED; + td.dist = FLT_MAX; + } + if (use_connected_only) { + continue; + } + } - td->flag = TD_SELECTED; - td->ext = nullptr; + closest_distances.reinitialize(points.size()); + closest_distances.fill(std::numeric_limits::max()); - copy_m3_m3(td->smtx, smtx); - copy_m3_m3(td->mtx, mtx); - } - }); + for (const int i : IndexRange(points.size())) { + const int point_i = points[i]; + TransData &td = tc.data[point_i]; + float3 *elem = &positions[point_i]; + + copy_v3_v3(td.iloc, *elem); + copy_v3_v3(td.center, td.iloc); + td.loc = *elem; + + td.flag = 0; + if (selection[point_i]) { + closest_distances[i] = 0.0f; + td.flag = TD_SELECTED; + } + + td.ext = nullptr; + + copy_m3_m3(td.smtx, smtx); + copy_m3_m3(td.mtx, mtx); + } + + if (use_connected_only) { + calculate_curve_point_distances_for_proportional_editing( + positions.slice(points), closest_distances.as_mutable_span()); + for (const int i : IndexRange(points.size())) { + TransData &td = tc.data[points[i]]; + td.dist = closest_distances[i]; + } + } + } + }); + } + else { + const IndexMask selected_indices = selection_per_object[i]; + threading::parallel_for(selected_indices.index_range(), 1024, [&](const IndexRange range) { + for (const int selection_i : range) { + TransData *td = &tc.data[selection_i]; + float3 *elem = &positions[selected_indices[selection_i]]; + + copy_v3_v3(td->iloc, *elem); + copy_v3_v3(td->center, td->iloc); + td->loc = *elem; + + td->flag = TD_SELECTED; + td->ext = nullptr; + + copy_m3_m3(td->smtx, smtx); + copy_m3_m3(td->mtx, mtx); + } + }); + } } } static void recalcData_curves(TransInfo *t) { - Span trans_data_contrainers(t->data_container, t->data_container_len); + const Span trans_data_contrainers(t->data_container, t->data_container_len); for (const TransDataContainer &tc : trans_data_contrainers) { Curves *curves_id = static_cast(tc.obedit->data); bke::CurvesGeometry &curves = curves_id->geometry.wrap(); -- 2.30.2 From b6a2d0cebd0729d085c2e3e10731b69bc7f913e3 Mon Sep 17 00:00:00 2001 From: Falk David Date: Tue, 21 Feb 2023 11:04:40 +0100 Subject: [PATCH 39/49] Curves: Add cursor snapping support This adds support for cursor snapping for the new curves object. It implements a function `transverts_from_curves_positions_create` (to separate the logic from the `Curves` object type). That function is then C wrapped by `ED_curves_transverts_create` and finally used in `ED_transverts_create_from_obedit`. Pull Request #104967 --- .../editors/curves/intern/curves_data.cc | 36 +++++++++++++++++++ source/blender/editors/include/ED_curves.h | 13 +++++++ source/blender/editors/util/ed_transverts.c | 16 +++++++-- 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/curves/intern/curves_data.cc b/source/blender/editors/curves/intern/curves_data.cc index c11879a18f9..d178ee60890 100644 --- a/source/blender/editors/curves/intern/curves_data.cc +++ b/source/blender/editors/curves/intern/curves_data.cc @@ -3,7 +3,36 @@ #include "BKE_curves.hh" #include "BKE_geometry_fields.hh" +#include "BLI_task.hh" + +#include "DNA_object_types.h" + #include "ED_curves.h" +#include "ED_transverts.h" + +namespace blender::ed::curves { + +void transverts_from_curves_positions_create(bke::CurvesGeometry &curves, TransVertStore *tvs) +{ + Vector selected_indices; + IndexMask selection = retrieve_selected_points(curves, selected_indices); + MutableSpan positions = curves.positions_for_write(); + + tvs->transverts = static_cast( + MEM_calloc_arrayN(selection.size(), sizeof(TransVert), __func__)); + tvs->transverts_tot = selection.size(); + + threading::parallel_for(selection.index_range(), 1024, [&](const IndexRange selection_range) { + for (const int point_i : selection_range) { + TransVert &tv = tvs->transverts[point_i]; + tv.loc = positions[selection[point_i]]; + tv.flag = SELECT; + copy_v3_v3(tv.oldloc, tv.loc); + } + }); +} + +} // namespace blender::ed::curves float (*ED_curves_point_normals_array_create(const Curves *curves_id))[3] { @@ -21,3 +50,10 @@ float (*ED_curves_point_normals_array_create(const Curves *curves_id))[3] return reinterpret_cast(data); } + +void ED_curves_transverts_create(Curves *curves_id, TransVertStore *tvs) +{ + using namespace blender; + bke::CurvesGeometry &curves = curves_id->geometry.wrap(); + ed::curves::transverts_from_curves_positions_create(curves, tvs); +} diff --git a/source/blender/editors/include/ED_curves.h b/source/blender/editors/include/ED_curves.h index 788b80a51a2..dba87d7434a 100644 --- a/source/blender/editors/include/ED_curves.h +++ b/source/blender/editors/include/ED_curves.h @@ -12,6 +12,7 @@ struct UndoType; struct SelectPick_Params; struct ViewContext; struct rcti; +struct TransVertStore; #ifdef __cplusplus extern "C" { @@ -32,6 +33,11 @@ void ED_keymap_curves(struct wmKeyConfig *keyconf); */ float (*ED_curves_point_normals_array_create(const struct Curves *curves_id))[3]; +/** + * Wrapper for `transverts_from_curves_positions_create`. + */ +void ED_curves_transverts_create(struct Curves *curves_id, struct TransVertStore *tvs); + /** \} */ #ifdef __cplusplus @@ -56,6 +62,13 @@ bke::CurvesGeometry primitive_random_sphere(int curves_size, int points_per_curv VectorSet get_unique_editable_curves(const bContext &C); void ensure_surface_deformation_node_exists(bContext &C, Object &curves_ob); +/** + * Allocate an array of `TransVert` for cursor/selection snapping (See + * `ED_transverts_create_from_obedit` in `view3d_snap.c`). + * \note: the `TransVert` elements in \a tvs are expected to write to the positions of \a curves. + */ +void transverts_from_curves_positions_create(bke::CurvesGeometry &curves, TransVertStore *tvs); + /* -------------------------------------------------------------------- */ /** \name Poll Functions * \{ */ diff --git a/source/blender/editors/util/ed_transverts.c b/source/blender/editors/util/ed_transverts.c index e91242cb9e6..b85f9ff9c12 100644 --- a/source/blender/editors/util/ed_transverts.c +++ b/source/blender/editors/util/ed_transverts.c @@ -9,6 +9,7 @@ #include "DNA_armature_types.h" #include "DNA_curve_types.h" +#include "DNA_curves_types.h" #include "DNA_lattice_types.h" #include "DNA_meta_types.h" #include "DNA_object_types.h" @@ -30,6 +31,7 @@ #include "DEG_depsgraph.h" #include "ED_armature.h" +#include "ED_curves.h" #include "ED_transverts.h" /* own include */ @@ -181,8 +183,14 @@ static void set_mapped_co(void *vuserdata, int index, const float co[3], const f bool ED_transverts_check_obedit(const Object *obedit) { - return ( - ELEM(obedit->type, OB_ARMATURE, OB_LATTICE, OB_MESH, OB_SURF, OB_CURVES_LEGACY, OB_MBALL)); + return (ELEM(obedit->type, + OB_ARMATURE, + OB_LATTICE, + OB_MESH, + OB_SURF, + OB_CURVES_LEGACY, + OB_MBALL, + OB_CURVES)); } void ED_transverts_create_from_obedit(TransVertStore *tvs, const Object *obedit, const int mode) @@ -481,6 +489,10 @@ void ED_transverts_create_from_obedit(TransVertStore *tvs, const Object *obedit, bp++; } } + else if (obedit->type == OB_CURVES) { + Curves *curves_id = obedit->data; + ED_curves_transverts_create(curves_id, tvs); + } if (!tvs->transverts_tot && tvs->transverts) { /* Prevent memory leak. happens for curves/lattices due to -- 2.30.2 From 451458bad9e1652e35d9b8a082f274bbef9abb47 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 21 Feb 2023 11:13:22 +0100 Subject: [PATCH 40/49] Gitea: update for new scoped label syntax --- .gitea/issue_template/bug.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitea/issue_template/bug.yaml b/.gitea/issue_template/bug.yaml index 4e3c550dae9..71cb6bebd55 100644 --- a/.gitea/issue_template/bug.yaml +++ b/.gitea/issue_template/bug.yaml @@ -1,9 +1,9 @@ name: Bug Report about: File a bug report labels: - - "type::Report" - - "status::Needs Triage" - - "priority::Normal" + - "Type/Report" + - "Status/Needs Triage" + - "Priority/Normal" body: - type: markdown attributes: -- 2.30.2 From bbf7dacbb086ee8f4f067d3971124ea966fbcfee Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 21 Feb 2023 11:16:52 +0100 Subject: [PATCH 41/49] Gitea: more updates for new scoped label syntax --- .gitea/issue_template/design.yaml | 2 +- .gitea/issue_template/todo.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/issue_template/design.yaml b/.gitea/issue_template/design.yaml index a1dcd8b0eda..77195a8c79a 100644 --- a/.gitea/issue_template/design.yaml +++ b/.gitea/issue_template/design.yaml @@ -1,7 +1,7 @@ name: Design about: Create a design task (for developers only) labels: - - "type::Design" + - "Type/Design" body: - type: textarea id: body diff --git a/.gitea/issue_template/todo.yaml b/.gitea/issue_template/todo.yaml index 58e848c3e18..325238afc20 100644 --- a/.gitea/issue_template/todo.yaml +++ b/.gitea/issue_template/todo.yaml @@ -1,7 +1,7 @@ name: To Do about: Create a to do task (for developers only) labels: - - "type::To Do" + - "Type/To Do" body: - type: textarea id: body -- 2.30.2 From e24ea1483c8778383440bcf7594a1f363e1930c1 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 21 Feb 2023 12:03:21 +0100 Subject: [PATCH 42/49] Fix Cycles MetalRT access of macOS 11 features when unavailable After recent changes in 2d994de. Pull Request #104976 --- intern/cycles/device/metal/device_impl.mm | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/intern/cycles/device/metal/device_impl.mm b/intern/cycles/device/metal/device_impl.mm index 0d836e7c2d5..9f418b0425a 100644 --- a/intern/cycles/device/metal/device_impl.mm +++ b/intern/cycles/device/metal/device_impl.mm @@ -225,11 +225,15 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile mtlAncillaryArgEncoder = [mtlDevice newArgumentEncoderWithArguments:ancillary_desc]; // preparing the blas arg encoder - MTLArgumentDescriptor *arg_desc_blas = [[MTLArgumentDescriptor alloc] init]; - arg_desc_blas.dataType = MTLDataTypeInstanceAccelerationStructure; - arg_desc_blas.access = MTLArgumentAccessReadOnly; - mtlBlasArgEncoder = [mtlDevice newArgumentEncoderWithArguments:@[ arg_desc_blas ]]; - [arg_desc_blas release]; + if (@available(macos 11.0, *)) { + if (use_metalrt) { + MTLArgumentDescriptor *arg_desc_blas = [[MTLArgumentDescriptor alloc] init]; + arg_desc_blas.dataType = MTLDataTypeInstanceAccelerationStructure; + arg_desc_blas.access = MTLArgumentAccessReadOnly; + mtlBlasArgEncoder = [mtlDevice newArgumentEncoderWithArguments:@[ arg_desc_blas ]]; + [arg_desc_blas release]; + } + } for (int i = 0; i < ancillary_desc.count; i++) { [ancillary_desc[i] release]; -- 2.30.2 From de27f63ac47022b220b7cc68c6d74223533c0188 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 21 Feb 2023 12:03:26 +0100 Subject: [PATCH 43/49] Brush: Add writing and reading ID preview for Brushes. Mandatory change for the Brush Assets project, from quick test does not seem to break anything (more) in existing 'old' brushes... Re. #101908. Pull Request #105016 --- source/blender/blenkernel/intern/brush.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 9a66b6b7d96..cd2be70530b 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -263,6 +263,8 @@ static void brush_blend_write(BlendWriter *writer, ID *id, const void *id_addres if (brush->gradient) { BLO_write_struct(writer, ColorBand, brush->gradient); } + + BKE_previewimg_blend_write(writer, brush->preview); } static void brush_blend_read_data(BlendDataReader *reader, ID *id) @@ -348,7 +350,9 @@ static void brush_blend_read_data(BlendDataReader *reader, ID *id) } } - brush->preview = nullptr; + BLO_read_data_address(reader, &brush->preview); + BKE_previewimg_blend_read(reader, brush->preview); + brush->icon_imbuf = nullptr; } -- 2.30.2 From 3a3b444c8e313bc48394d2ca64bda9fbbdcad2dd Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 21 Feb 2023 12:05:54 +0100 Subject: [PATCH 44/49] Fix new essentials asset library not being covered in library path query --- source/blender/asset_system/intern/asset_library_service.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/asset_system/intern/asset_library_service.cc b/source/blender/asset_system/intern/asset_library_service.cc index cf52f9d1d76..45d8264e926 100644 --- a/source/blender/asset_system/intern/asset_library_service.cc +++ b/source/blender/asset_system/intern/asset_library_service.cc @@ -215,6 +215,10 @@ std::string AssetLibraryService::root_path_from_library_ref( return ""; } + if (ELEM(library_reference.type, ASSET_LIBRARY_ESSENTIALS)) { + return essentials_directory_path(); + } + bUserAssetLibrary *custom_library = find_custom_asset_library_from_library_ref( library_reference); if (!custom_library || !custom_library->path[0]) { -- 2.30.2 From 2cbf647d9c833f64a3354e8e7f06d5f98f5c5da6 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 21 Feb 2023 18:47:11 +0100 Subject: [PATCH 45/49] Temporary design to support filtering assets by type Python defined asset shelfs can now define an `asset_poll__()` function to determine if an asset should be visible or not, based on type information. This isn't great and can probably be a performance issue in bigger libraries. A proper solution would be to provide a set of asset traits to filter by, but this is a bit tricky to implement. This is a temporary solution so the brush assets project isn't held up by this. --- source/blender/blenkernel/BKE_screen.h | 6 ++- .../interface_template_asset_shelf.cc | 31 +++++++++++++++ source/blender/makesrna/intern/rna_ui.c | 39 ++++++++++++++++++- source/blender/makesrna/intern/rna_ui_api.c | 19 --------- 4 files changed, 73 insertions(+), 22 deletions(-) diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index f75669d7da4..dca1252e270 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -409,7 +409,11 @@ typedef struct AssetShelfType { int space_type; /* Determine if the asset shelf should be visible or not. */ - bool (*poll)(const struct bContext *C, struct AssetShelfType *shelf_type); + bool (*poll)(const struct bContext *C, const struct AssetShelfType *shelf_type); + + /* Determine if an individual asset should be visible or not. May be a temporary design, + * visibility should be first and foremost controlled by asset traits. */ + bool (*asset_poll)(const struct AssetShelfType *shelf_type, const struct AssetHandle *asset); /* RNA integration */ ExtensionRNA rna_ext; diff --git a/source/blender/editors/interface/interface_template_asset_shelf.cc b/source/blender/editors/interface/interface_template_asset_shelf.cc index d709df573e2..02a989b2662 100644 --- a/source/blender/editors/interface/interface_template_asset_shelf.cc +++ b/source/blender/editors/interface/interface_template_asset_shelf.cc @@ -7,6 +7,7 @@ #include "AS_asset_library.hh" #include "BKE_context.h" +#include "BKE_screen.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" @@ -106,6 +107,29 @@ static std::optional catalog_filter_from_shelf return library->catalog_service->create_catalog_filter(active_catalog->catalog_id); } +/* TODO calling a (.py defined) callback for every asset isn't exactly great. Should be a temporary +solution until there is proper filtering by asset traits. */ +/** + * Returns true if the asset should be visible. That is, if any of the visible asset shelves has no + * poll function (all assets should be displayed), or its #AssetShelfType.asset_poll function + * returns true. + */ +static bool asset_shelf_asset_poll(const SpaceType &space_type, + const bContext &C, + const AssetHandle &asset) +{ + LISTBASE_FOREACH (AssetShelfType *, shelf_type, &space_type.asset_shelf_types) { + if (!shelf_type->poll || !shelf_type->poll(&C, shelf_type)) { + continue; + } + if (!shelf_type->asset_poll || shelf_type->asset_poll(shelf_type, &asset)) { + return true; + } + } + + return false; +} + void uiTemplateAssetShelf(uiLayout *layout, const bContext *C, const AssetFilterSettings *filter_settings) @@ -116,6 +140,8 @@ void uiTemplateAssetShelf(uiLayout *layout, const AssetShelfSettings *shelf_settings = static_cast( shelf_settings_ptr.data); + Vector asset_polls; + ED_assetlist_storage_fetch(library_ref, C); ED_assetlist_ensure_previews_job(library_ref, C); @@ -139,8 +165,13 @@ void uiTemplateAssetShelf(uiLayout *layout, uiLayout *box = uiLayoutBox(layout); uiLayout *row = uiLayoutRow(box, false); + const SpaceLink *space_link = CTX_wm_space_data(C); + const SpaceType *space_type = BKE_spacetype_from_id(space_link->spacetype); ED_assetlist_iterate(*library_ref, [&](AssetHandle asset) { + if (!asset_shelf_asset_poll(*space_type, *C, asset)) { + return true; + } if (!ED_asset_filter_matches_asset(filter_settings, &asset)) { /* Don't do anything else, but return true to continue iterating. */ return true; diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index 80f3f0a81b1..364b00c1e70 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -1022,7 +1022,30 @@ static StructRNA *rna_Menu_refine(PointerRNA *mtr) /* Asset Shelf */ -static bool asset_shelf_poll(const bContext *C, AssetShelfType *shelf_type) +static bool asset_shelf_asset_poll(const AssetShelfType *shelf_type, const AssetHandle *asset) +{ + extern FunctionRNA rna_AssetShelf_asset_poll___func; + + PointerRNA ptr; + RNA_pointer_create(NULL, shelf_type->rna_ext.srna, NULL, &ptr); /* dummy */ + FunctionRNA *func = &rna_AssetShelf_asset_poll___func; + + ParameterList list; + RNA_parameter_list_create(&list, &ptr, func); + RNA_parameter_set_lookup(&list, "asset_handle", &asset); + shelf_type->rna_ext.call(NULL, &ptr, func, &list); + + void *ret; + RNA_parameter_get_lookup(&list, "visible", &ret); + /* Get the value before freeing. */ + const bool is_visible = *(bool *)ret; + + RNA_parameter_list_free(&list); + + return is_visible; +} + +static bool asset_shelf_poll(const bContext *C, const AssetShelfType *shelf_type) { extern FunctionRNA rna_AssetShelf_poll_func; @@ -1083,7 +1106,7 @@ static StructRNA *rna_AssetShelf_register(Main *bmain, dummy_shelf.type = &dummy_shelf_type; RNA_pointer_create(NULL, &RNA_AssetShelf, &dummy_shelf, &dummy_shelf_type_ptr); - int have_function[1]; + int have_function[2]; /* validate the python class */ if (validate(&dummy_shelf_type_ptr, data, have_function) != 0) { @@ -1131,6 +1154,7 @@ static StructRNA *rna_AssetShelf_register(Main *bmain, RNA_struct_blender_type_set(shelf_type->rna_ext.srna, shelf_type); shelf_type->poll = have_function[0] ? asset_shelf_poll : NULL; + shelf_type->asset_poll = have_function[1] ? asset_shelf_asset_poll : NULL; BLI_addtail(&space_type->asset_shelf_types, shelf_type); @@ -2000,6 +2024,17 @@ static void rna_def_asset_shelf(BlenderRNA *brna) RNA_def_function_return(func, RNA_def_boolean(func, "visible", 1, "", "")); parm = RNA_def_pointer(func, "context", "Context", "", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + + func = RNA_def_function(srna, "asset_poll__", NULL); + RNA_def_function_ui_description( + func, + "TEMPORARY DESIGN; Expect compatibility breakage. Determine if an asset should be visible " + "in the asset shelf. If this method returns a non-null output, then the asset shelf will be " + "visible"); + RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_REGISTER_OPTIONAL); + RNA_def_function_return(func, RNA_def_boolean(func, "visible", 1, "", "")); + parm = RNA_def_pointer(func, "asset_handle", "AssetHandle", "", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); } static void rna_def_asset_shelf_settings(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index 34bacf393df..9a75be07cf4 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -714,15 +714,6 @@ static const EnumPropertyItem *rna_uiTemplateAssetView_filter_id_types_itemf( return items; } -static void rna_uiTemplateAssetShelf(uiLayout *layout, bContext *C, int filter_id_types) -{ - AssetFilterSettings filter_settings = { - .id_types = filter_id_types ? filter_id_types : FILTER_ID_ALL, - }; - - uiTemplateAssetShelf(layout, C, &filter_settings); -} - static uiLayout *rna_uiLayoutRowWithHeading( uiLayout *layout, bool align, const char *heading, const char *heading_ctxt, bool translate) { @@ -1967,16 +1958,6 @@ void RNA_api_ui_layout(StructRNA *srna) "Operator properties to fill in for the custom drag operator passed to the template"); RNA_def_parameter_flags(parm, 0, PARM_RNAPTR); RNA_def_function_output(func, parm); - - func = RNA_def_function(srna, "template_asset_shelf", "rna_uiTemplateAssetShelf"); - RNA_def_function_ui_description(func, - "Item. A list of assets in a horizontally scrollable layout. " - "Meant to be placed in a 'ASSET_SHELF' region"); - RNA_def_function_flag(func, FUNC_USE_CONTEXT); - parm = RNA_def_property(func, "filter_id_types", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(parm, DummyRNA_NULL_items); - RNA_def_property_enum_funcs(parm, NULL, NULL, "rna_uiTemplateAssetView_filter_id_types_itemf"); - RNA_def_property_flag(parm, PROP_ENUM_FLAG); } #endif -- 2.30.2 From 1e31d65986dd42d99e3305a362ccebf9a6dc50a4 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 22 Feb 2023 14:27:16 +0100 Subject: [PATCH 46/49] Cleanup: Minor comment formatting change --- .../blender/editors/interface/interface_template_asset_shelf.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/interface/interface_template_asset_shelf.cc b/source/blender/editors/interface/interface_template_asset_shelf.cc index 02a989b2662..03fab3e9c21 100644 --- a/source/blender/editors/interface/interface_template_asset_shelf.cc +++ b/source/blender/editors/interface/interface_template_asset_shelf.cc @@ -108,7 +108,7 @@ static std::optional catalog_filter_from_shelf } /* TODO calling a (.py defined) callback for every asset isn't exactly great. Should be a temporary -solution until there is proper filtering by asset traits. */ + * solution until there is proper filtering by asset traits. */ /** * Returns true if the asset should be visible. That is, if any of the visible asset shelves has no * poll function (all assets should be displayed), or its #AssetShelfType.asset_poll function -- 2.30.2 From 16702ea2748d7eef613a0da572860e1cc590b75f Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 27 Feb 2023 11:49:52 +0100 Subject: [PATCH 47/49] Fix typo from merge conflict resolving --- source/blender/editors/screen/area.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/screen/area.cc b/source/blender/editors/screen/area.cc index e8f6dde0b53..09d725d1096 100644 --- a/source/blender/editors/screen/area.cc +++ b/source/blender/editors/screen/area.cc @@ -938,7 +938,7 @@ static void region_azone_edge(AZone *az, ARegion *region) * actual contents). * Note that this is an arbitrary amount that matches nicely with numbers elsewhere. */ const int overlap_padding = (region->overlap && !RGN_TYPE_IS_HEADER_ANY(region->regiontype)) ? - in)(0.4f * U.widget_unit) : + int(0.4f * U.widget_unit) : 0; switch (az->edge) { -- 2.30.2 From d64078a28f8c350b90733a3125b9ceabbe57bff9 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 27 Feb 2023 12:24:58 +0100 Subject: [PATCH 48/49] Fix build error --- source/blender/editors/screen/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/editors/screen/CMakeLists.txt b/source/blender/editors/screen/CMakeLists.txt index 77ea72357a8..caa4a93cd76 100644 --- a/source/blender/editors/screen/CMakeLists.txt +++ b/source/blender/editors/screen/CMakeLists.txt @@ -2,6 +2,7 @@ set(INC ../include + ../../asset_system ../../blenfont ../../blenkernel ../../blenlib -- 2.30.2 From a0b5fda5aff7f1ffc90bb7e8473be4c965f45112 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 27 Feb 2023 12:25:40 +0100 Subject: [PATCH 49/49] Fix build error --- source/blender/editors/screen/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/editors/screen/CMakeLists.txt b/source/blender/editors/screen/CMakeLists.txt index 77ea72357a8..caa4a93cd76 100644 --- a/source/blender/editors/screen/CMakeLists.txt +++ b/source/blender/editors/screen/CMakeLists.txt @@ -2,6 +2,7 @@ set(INC ../include + ../../asset_system ../../blenfont ../../blenkernel ../../blenlib -- 2.30.2