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_function_ref.hh"
|
||||||
#include "BLI_map.hh"
|
#include "BLI_map.hh"
|
||||||
|
#include "BLI_set.hh"
|
||||||
#include "BLI_string_ref.hh"
|
#include "BLI_string_ref.hh"
|
||||||
#include "BLI_uuid.h"
|
#include "BLI_uuid.h"
|
||||||
#include "BLI_vector.hh"
|
#include "BLI_vector.hh"
|
||||||
@@ -48,6 +49,7 @@ using CatalogFilePath = std::string;
|
|||||||
class AssetCatalog;
|
class AssetCatalog;
|
||||||
class AssetCatalogDefinitionFile;
|
class AssetCatalogDefinitionFile;
|
||||||
class AssetCatalogTree;
|
class AssetCatalogTree;
|
||||||
|
class AssetCatalogFilter;
|
||||||
|
|
||||||
/* Manages the asset catalogs of a single asset library (i.e. of catalogs defined in a single
|
/* Manages the asset catalogs of a single asset library (i.e. of catalogs defined in a single
|
||||||
* directory hierarchy). */
|
* directory hierarchy). */
|
||||||
@@ -100,6 +102,14 @@ class AssetCatalogService {
|
|||||||
* efficient call as it's just a linear search over the catalogs. */
|
* efficient call as it's just a linear search over the catalogs. */
|
||||||
AssetCatalog *find_catalog_by_path(const AssetCatalogPath &path) const;
|
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.
|
/** Create a catalog with some sensible auto-generated catalog ID.
|
||||||
* The catalog will be saved to the default catalog file.*/
|
* The catalog will be saved to the default catalog file.*/
|
||||||
AssetCatalog *create_catalog(const AssetCatalogPath &catalog_path);
|
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. */
|
* Being a set, duplicates are removed. The catalog's simple name is ignored in this. */
|
||||||
using AssetCatalogOrderedSet = std::set<const AssetCatalog *, AssetCatalogPathCmp>;
|
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
|
} // namespace blender::bke
|
||||||
|
|||||||
@@ -85,6 +85,33 @@ AssetCatalog *AssetCatalogService::find_catalog_by_path(const AssetCatalogPath &
|
|||||||
return nullptr;
|
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)
|
void AssetCatalogService::delete_catalog(CatalogID catalog_id)
|
||||||
{
|
{
|
||||||
std::unique_ptr<AssetCatalog> *catalog_uptr_ptr = this->catalogs_.lookup_ptr(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);
|
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
|
} // 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"));
|
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
|
} // namespace blender::bke::tests
|
||||||
|
|||||||
Reference in New Issue
Block a user