Asset Catalogs: add catalog filter for the asset browser
Given an "active catalog" (i.e. the one selected in the UI), construct an `AssetCatalogFilter` instance. This filter can determine whether an asset should be shown or not. It returns `true` when The asset's catalog ID is: - the active catalog, - an alias of the active catalog (so different UUID that maps to the same path), - a sub-catalog of the active catalog. Not yet hooked up to the UI.
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_uuid.h"
|
||||
#include "BLI_vector.hh"
|
||||
@@ -48,6 +49,7 @@ using CatalogFilePath = std::string;
|
||||
class AssetCatalog;
|
||||
class AssetCatalogDefinitionFile;
|
||||
class AssetCatalogTree;
|
||||
class AssetCatalogFilter;
|
||||
|
||||
/* Manages the asset catalogs of a single asset library (i.e. of catalogs defined in a single
|
||||
* directory hierarchy). */
|
||||
@@ -100,6 +102,14 @@ class AssetCatalogService {
|
||||
* efficient call as it's just a linear search over the catalogs. */
|
||||
AssetCatalog *find_catalog_by_path(const AssetCatalogPath &path) const;
|
||||
|
||||
/**
|
||||
* Create a filter object that can be used to determine whether an asset belongs to the given
|
||||
* catalog, or any of the catalogs in the sub-tree rooted at the given catalog.
|
||||
*
|
||||
* \see #AssetCatalogFilter
|
||||
*/
|
||||
AssetCatalogFilter create_catalog_filter(CatalogID active_catalog_id) const;
|
||||
|
||||
/** Create a catalog with some sensible auto-generated catalog ID.
|
||||
* The catalog will be saved to the default catalog file.*/
|
||||
AssetCatalog *create_catalog(const AssetCatalogPath &catalog_path);
|
||||
@@ -331,4 +341,20 @@ struct AssetCatalogPathCmp {
|
||||
* Being a set, duplicates are removed. The catalog's simple name is ignored in this. */
|
||||
using AssetCatalogOrderedSet = std::set<const AssetCatalog *, AssetCatalogPathCmp>;
|
||||
|
||||
/**
|
||||
* Filter that can determine whether an asset should be visible or not, based on its catalog ID.
|
||||
*
|
||||
* \see AssetCatalogService::create_filter()
|
||||
*/
|
||||
class AssetCatalogFilter {
|
||||
public:
|
||||
bool contains(CatalogID asset_catalog_id) const;
|
||||
|
||||
protected:
|
||||
friend AssetCatalogService;
|
||||
const Set<CatalogID> matching_catalog_ids;
|
||||
|
||||
explicit AssetCatalogFilter(Set<CatalogID> &&matching_catalog_ids);
|
||||
};
|
||||
|
||||
} // namespace blender::bke
|
||||
|
@@ -85,6 +85,33 @@ AssetCatalog *AssetCatalogService::find_catalog_by_path(const AssetCatalogPath &
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AssetCatalogFilter AssetCatalogService::create_catalog_filter(
|
||||
const CatalogID active_catalog_id) const
|
||||
{
|
||||
Set<CatalogID> matching_catalog_ids;
|
||||
matching_catalog_ids.add(active_catalog_id);
|
||||
|
||||
const AssetCatalog *active_catalog = find_catalog(active_catalog_id);
|
||||
if (!active_catalog) {
|
||||
/* If the UUID is unknown (i.e. not mapped to an actual Catalog), it is impossible to determine
|
||||
* its children. The filter can still work on the given UUID. */
|
||||
return AssetCatalogFilter(std::move(matching_catalog_ids));
|
||||
}
|
||||
|
||||
/* This cannot just iterate over tree items to get all the required data, because tree items only
|
||||
* represent single UUIDs. It could be used to get the main UUIDs of the children, though, and
|
||||
* then only do an exact match on the path (instead of the more complex `is_contained_in()`
|
||||
* call). Without an extra indexed-by-path acceleration structure, this is still going to require
|
||||
* a linear search, though. */
|
||||
for (const auto &catalog_uptr : this->catalogs_.values()) {
|
||||
if (catalog_uptr->path.is_contained_in(active_catalog->path)) {
|
||||
matching_catalog_ids.add(catalog_uptr->catalog_id);
|
||||
}
|
||||
}
|
||||
|
||||
return AssetCatalogFilter(std::move(matching_catalog_ids));
|
||||
}
|
||||
|
||||
void AssetCatalogService::delete_catalog(CatalogID catalog_id)
|
||||
{
|
||||
std::unique_ptr<AssetCatalog> *catalog_uptr_ptr = this->catalogs_.lookup_ptr(catalog_id);
|
||||
@@ -754,4 +781,14 @@ std::string AssetCatalog::sensible_simple_name_for_path(const AssetCatalogPath &
|
||||
return "..." + name.substr(name.length() - 60);
|
||||
}
|
||||
|
||||
AssetCatalogFilter::AssetCatalogFilter(Set<CatalogID> &&matching_catalog_ids)
|
||||
: matching_catalog_ids(std::move(matching_catalog_ids))
|
||||
{
|
||||
}
|
||||
|
||||
bool AssetCatalogFilter::contains(const CatalogID asset_catalog_id) const
|
||||
{
|
||||
return matching_catalog_ids.contains(asset_catalog_id);
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
@@ -906,4 +906,60 @@ TEST_F(AssetCatalogTest, create_missing_catalogs_after_loading)
|
||||
EXPECT_EQ(1, loaded_service.count_catalogs_with_path("character/Ružena"));
|
||||
}
|
||||
|
||||
TEST_F(AssetCatalogTest, create_catalog_filter)
|
||||
{
|
||||
AssetCatalogService service(asset_library_root_);
|
||||
service.load_from_disk();
|
||||
|
||||
/* Alias for the same catalog as the main one. */
|
||||
AssetCatalog *alias_ruzena = service.create_catalog("character/Ružena/poselib");
|
||||
/* Alias for a sub-catalog. */
|
||||
AssetCatalog *alias_ruzena_hand = service.create_catalog("character/Ružena/poselib/hand");
|
||||
|
||||
AssetCatalogFilter filter = service.create_catalog_filter(UUID_POSES_RUZENA);
|
||||
|
||||
/* Positive test for loaded-from-disk catalogs. */
|
||||
EXPECT_TRUE(filter.contains(UUID_POSES_RUZENA))
|
||||
<< "Main catalog should be included in the filter.";
|
||||
EXPECT_TRUE(filter.contains(UUID_POSES_RUZENA_HAND))
|
||||
<< "Sub-catalog should be included in the filter.";
|
||||
EXPECT_TRUE(filter.contains(UUID_POSES_RUZENA_FACE))
|
||||
<< "Sub-catalog should be included in the filter.";
|
||||
|
||||
/* Positive test for newly-created catalogs. */
|
||||
EXPECT_TRUE(filter.contains(alias_ruzena->catalog_id))
|
||||
<< "Alias of main catalog should be included in the filter.";
|
||||
EXPECT_TRUE(filter.contains(alias_ruzena_hand->catalog_id))
|
||||
<< "Alias of sub-catalog should be included in the filter.";
|
||||
|
||||
/* Negative test for unrelated catalogs. */
|
||||
EXPECT_FALSE(filter.contains(BLI_uuid_nil())) << "Nil catalog should not be included.";
|
||||
EXPECT_FALSE(filter.contains(UUID_ID_WITHOUT_PATH));
|
||||
EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE));
|
||||
EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE_WHITESPACE));
|
||||
EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE_TRAILING_SLASH));
|
||||
EXPECT_FALSE(filter.contains(UUID_WITHOUT_SIMPLENAME));
|
||||
}
|
||||
|
||||
TEST_F(AssetCatalogTest, create_catalog_filter_for_unknown_uuid)
|
||||
{
|
||||
AssetCatalogService service;
|
||||
const bUUID unknown_uuid = BLI_uuid_generate_random();
|
||||
|
||||
AssetCatalogFilter filter = service.create_catalog_filter(unknown_uuid);
|
||||
EXPECT_TRUE(filter.contains(unknown_uuid));
|
||||
|
||||
EXPECT_FALSE(filter.contains(BLI_uuid_nil())) << "Nil catalog should not be included.";
|
||||
EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE));
|
||||
}
|
||||
|
||||
TEST_F(AssetCatalogTest, create_catalog_filter_for_unassigned_assets)
|
||||
{
|
||||
AssetCatalogService service;
|
||||
|
||||
AssetCatalogFilter filter = service.create_catalog_filter(BLI_uuid_nil());
|
||||
EXPECT_TRUE(filter.contains(BLI_uuid_nil()));
|
||||
EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE));
|
||||
}
|
||||
|
||||
} // namespace blender::bke::tests
|
||||
|
Reference in New Issue
Block a user