1
1

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:
2021-10-01 15:21:44 +02:00
parent 1c7ce7e0b4
commit 56ce51d1f7
3 changed files with 119 additions and 0 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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