Asset system: Move catalog tree code to own files

The catalog code is already quite complex, I rather keep the tree stuff
separate in a more focused unit.
This commit is contained in:
Julian Eisel
2022-11-18 11:46:12 +01:00
parent 6239e089cf
commit 7c0cecfd00
9 changed files with 270 additions and 243 deletions

View File

@@ -165,7 +165,7 @@ class AssetCatalogService {
protected:
std::unique_ptr<AssetCatalogCollection> catalog_collection_;
std::unique_ptr<AssetCatalogTree> catalog_tree_ = std::make_unique<AssetCatalogTree>();
std::unique_ptr<AssetCatalogTree> catalog_tree_;
CatalogFilePath asset_library_root_;
Vector<std::unique_ptr<AssetCatalogCollection>> undo_snapshots_;
@@ -274,88 +274,6 @@ class AssetCatalogCollection {
static OwningAssetCatalogMap copy_catalog_map(const OwningAssetCatalogMap &orig);
};
/**
* Representation of a catalog path in the #AssetCatalogTree.
*/
class AssetCatalogTreeItem {
friend class AssetCatalogTree;
public:
/** Container for child items. Uses a #std::map to keep items ordered by their name (i.e. their
* last catalog component). */
using ChildMap = std::map<std::string, AssetCatalogTreeItem>;
using ItemIterFn = FunctionRef<void(AssetCatalogTreeItem &)>;
AssetCatalogTreeItem(StringRef name,
CatalogID catalog_id,
StringRef simple_name,
const AssetCatalogTreeItem *parent = nullptr);
CatalogID get_catalog_id() const;
StringRefNull get_simple_name() const;
StringRefNull get_name() const;
bool has_unsaved_changes() const;
/** Return the full catalog path, defined as the name of this catalog prefixed by the full
* catalog path of its parent and a separator. */
AssetCatalogPath catalog_path() const;
int count_parents() const;
bool has_children() const;
/** Iterate over children calling \a callback for each of them, but do not recurse into their
* children. */
void foreach_child(ItemIterFn callback);
protected:
/** Child tree items, ordered by their names. */
ChildMap children_;
/** The user visible name of this component. */
CatalogPathComponent name_;
CatalogID catalog_id_;
/** Copy of #AssetCatalog::simple_name. */
std::string simple_name_;
/** Copy of #AssetCatalog::flags.has_unsaved_changes. */
bool has_unsaved_changes_ = false;
/** Pointer back to the parent item. Used to reconstruct the hierarchy from an item (e.g. to
* build a path). */
const AssetCatalogTreeItem *parent_ = nullptr;
private:
static void foreach_item_recursive(ChildMap &children_, ItemIterFn callback);
};
/**
* A representation of the catalog paths as tree structure. Each component of the catalog tree is
* represented by an #AssetCatalogTreeItem. The last path component of an item is used as its name,
* which may also be shown to the user.
* An item can not have multiple children with the same name. That means the name uniquely
* identifies an item within its parent.
*
* There is no single root tree element, the #AssetCatalogTree instance itself represents the root.
*/
class AssetCatalogTree {
using ChildMap = AssetCatalogTreeItem::ChildMap;
using ItemIterFn = AssetCatalogTreeItem::ItemIterFn;
public:
/** Ensure an item representing \a path is in the tree, adding it if necessary. */
void insert_item(const AssetCatalog &catalog);
void foreach_item(ItemIterFn callback);
/** Iterate over root items calling \a callback for each of them, but do not recurse into their
* children. */
void foreach_root_item(ItemIterFn callback);
bool is_empty() const;
AssetCatalogTreeItem *find_item(const AssetCatalogPath &path);
AssetCatalogTreeItem *find_root_item(const AssetCatalogPath &path);
protected:
/** Child tree items, ordered by their names. */
ChildMap root_items_;
};
/** Keeps track of which catalogs are defined in a certain file on disk.
* Only contains non-owning pointers to the #AssetCatalog instances, so ensure the lifetime of this
* class is shorter than that of the #`AssetCatalog`s themselves. */

View File

@@ -0,0 +1,94 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup asset_system
*
* A representation of the catalog paths as tree structure. Each component of the catalog tree is
* represented by an #AssetCatalogTreeItem. The last path component of an item is used as its name,
* which may also be shown to the user.
* An item can not have multiple children with the same name. That means the name uniquely
* identifies an item within its parent.
*
* There is no single root tree element, the #AssetCatalogTree instance itself represents the root.
*/
#pragma once
#include "AS_asset_catalog.hh"
namespace blender::asset_system {
/**
* Representation of a catalog path in the #AssetCatalogTree.
*/
class AssetCatalogTreeItem {
friend class AssetCatalogTree;
public:
/** Container for child items. Uses a #std::map to keep items ordered by their name (i.e. their
* last catalog component). */
using ChildMap = std::map<std::string, AssetCatalogTreeItem>;
using ItemIterFn = FunctionRef<void(AssetCatalogTreeItem &)>;
AssetCatalogTreeItem(StringRef name,
CatalogID catalog_id,
StringRef simple_name,
const AssetCatalogTreeItem *parent = nullptr);
CatalogID get_catalog_id() const;
StringRefNull get_simple_name() const;
StringRefNull get_name() const;
bool has_unsaved_changes() const;
/** Return the full catalog path, defined as the name of this catalog prefixed by the full
* catalog path of its parent and a separator. */
AssetCatalogPath catalog_path() const;
int count_parents() const;
bool has_children() const;
/** Iterate over children calling \a callback for each of them, but do not recurse into their
* children. */
void foreach_child(ItemIterFn callback);
protected:
/** Child tree items, ordered by their names. */
ChildMap children_;
/** The user visible name of this component. */
CatalogPathComponent name_;
CatalogID catalog_id_;
/** Copy of #AssetCatalog::simple_name. */
std::string simple_name_;
/** Copy of #AssetCatalog::flags.has_unsaved_changes. */
bool has_unsaved_changes_ = false;
/** Pointer back to the parent item. Used to reconstruct the hierarchy from an item (e.g. to
* build a path). */
const AssetCatalogTreeItem *parent_ = nullptr;
private:
static void foreach_item_recursive(ChildMap &children_, ItemIterFn callback);
};
class AssetCatalogTree {
using ChildMap = AssetCatalogTreeItem::ChildMap;
using ItemIterFn = AssetCatalogTreeItem::ItemIterFn;
public:
/** Ensure an item representing \a path is in the tree, adding it if necessary. */
void insert_item(const AssetCatalog &catalog);
void foreach_item(ItemIterFn callback);
/** Iterate over root items calling \a callback for each of them, but do not recurse into their
* children. */
void foreach_root_item(ItemIterFn callback);
bool is_empty() const;
AssetCatalogTreeItem *find_item(const AssetCatalogPath &path);
AssetCatalogTreeItem *find_root_item(const AssetCatalogPath &path);
protected:
/** Child tree items, ordered by their names. */
ChildMap root_items_;
};
} // namespace blender::asset_system

View File

@@ -16,6 +16,7 @@ set(INC_SYS
set(SRC
intern/asset_catalog.cc
intern/asset_catalog_path.cc
intern/asset_catalog_tree.cc
intern/asset_library.cc
intern/asset_library_service.cc
intern/asset_representation.cc
@@ -23,6 +24,7 @@ set(SRC
AS_asset_catalog.hh
AS_asset_catalog_path.hh
AS_asset_catalog_tree.hh
AS_asset_library.hh
AS_asset_representation.hh
intern/asset_library_service.hh

View File

@@ -8,6 +8,7 @@
#include <set>
#include "AS_asset_catalog.hh"
#include "AS_asset_catalog_tree.hh"
#include "AS_asset_library.h"
#include "AS_asset_library.hh"
@@ -38,14 +39,15 @@ const std::string AssetCatalogDefinitionFile::HEADER =
"# Other lines are of the format \"UUID:catalog/path/for/assets:simple catalog name\"\n";
AssetCatalogService::AssetCatalogService()
: catalog_collection_(std::make_unique<AssetCatalogCollection>())
: catalog_collection_(std::make_unique<AssetCatalogCollection>()),
catalog_tree_(std::make_unique<AssetCatalogTree>())
{
}
AssetCatalogService::AssetCatalogService(const CatalogFilePath &asset_library_root)
: catalog_collection_(std::make_unique<AssetCatalogCollection>()),
asset_library_root_(asset_library_root)
: AssetCatalogService()
{
asset_library_root_ = asset_library_root;
}
void AssetCatalogService::tag_has_unsaved_changes(AssetCatalog *edited_catalog)
@@ -670,162 +672,6 @@ OwningAssetCatalogMap AssetCatalogCollection::copy_catalog_map(const OwningAsset
/* ---------------------------------------------------------------------- */
AssetCatalogTreeItem::AssetCatalogTreeItem(StringRef name,
CatalogID catalog_id,
StringRef simple_name,
const AssetCatalogTreeItem *parent)
: name_(name), catalog_id_(catalog_id), simple_name_(simple_name), parent_(parent)
{
}
CatalogID AssetCatalogTreeItem::get_catalog_id() const
{
return catalog_id_;
}
StringRefNull AssetCatalogTreeItem::get_name() const
{
return name_;
}
StringRefNull AssetCatalogTreeItem::get_simple_name() const
{
return simple_name_;
}
bool AssetCatalogTreeItem::has_unsaved_changes() const
{
return has_unsaved_changes_;
}
AssetCatalogPath AssetCatalogTreeItem::catalog_path() const
{
AssetCatalogPath current_path = name_;
for (const AssetCatalogTreeItem *parent = parent_; parent; parent = parent->parent_) {
current_path = AssetCatalogPath(parent->name_) / current_path;
}
return current_path;
}
int AssetCatalogTreeItem::count_parents() const
{
int i = 0;
for (const AssetCatalogTreeItem *parent = parent_; parent; parent = parent->parent_) {
i++;
}
return i;
}
bool AssetCatalogTreeItem::has_children() const
{
return !children_.empty();
}
void AssetCatalogTreeItem::foreach_item_recursive(AssetCatalogTreeItem::ChildMap &children,
const ItemIterFn callback)
{
for (auto &[key, item] : children) {
callback(item);
foreach_item_recursive(item.children_, callback);
}
}
void AssetCatalogTreeItem::foreach_child(const ItemIterFn callback)
{
for (auto &[key, item] : children_) {
callback(item);
}
}
/* ---------------------------------------------------------------------- */
void AssetCatalogTree::insert_item(const AssetCatalog &catalog)
{
const AssetCatalogTreeItem *parent = nullptr;
/* The children for the currently iterated component, where the following component should be
* added to (if not there yet). */
AssetCatalogTreeItem::ChildMap *current_item_children = &root_items_;
BLI_assert_msg(!ELEM(catalog.path.str()[0], '/', '\\'),
"Malformed catalog path; should not start with a separator");
const CatalogID nil_id{};
catalog.path.iterate_components([&](StringRef component_name, const bool is_last_component) {
/* Insert new tree element - if no matching one is there yet! */
auto [key_and_item, was_inserted] = current_item_children->emplace(
component_name,
AssetCatalogTreeItem(component_name,
is_last_component ? catalog.catalog_id : nil_id,
is_last_component ? catalog.simple_name : "",
parent));
AssetCatalogTreeItem &item = key_and_item->second;
/* If full path of this catalog already exists as parent path of a previously read catalog,
* we can ensure this tree item's UUID is set here. */
if (is_last_component) {
if (BLI_uuid_is_nil(item.catalog_id_) || catalog.flags.is_first_loaded) {
item.catalog_id_ = catalog.catalog_id;
}
item.has_unsaved_changes_ = catalog.flags.has_unsaved_changes;
}
/* Walk further into the path (no matter if a new item was created or not). */
parent = &item;
current_item_children = &item.children_;
});
}
void AssetCatalogTree::foreach_item(AssetCatalogTreeItem::ItemIterFn callback)
{
AssetCatalogTreeItem::foreach_item_recursive(root_items_, callback);
}
void AssetCatalogTree::foreach_root_item(const ItemIterFn callback)
{
for (auto &[key, item] : root_items_) {
callback(item);
}
}
bool AssetCatalogTree::is_empty() const
{
return root_items_.empty();
}
AssetCatalogTreeItem *AssetCatalogTree::find_item(const AssetCatalogPath &path)
{
AssetCatalogTreeItem *result = nullptr;
this->foreach_item([&](AssetCatalogTreeItem &item) {
if (result) {
/* There is no way to stop iteration. */
return;
}
if (item.catalog_path() == path) {
result = &item;
}
});
return result;
}
AssetCatalogTreeItem *AssetCatalogTree::find_root_item(const AssetCatalogPath &path)
{
AssetCatalogTreeItem *result = nullptr;
this->foreach_root_item([&](AssetCatalogTreeItem &item) {
if (result) {
/* There is no way to stop iteration. */
return;
}
if (item.catalog_path() == path) {
result = &item;
}
});
return result;
}
/* ---------------------------------------------------------------------- */
/* ---------------------------------------------------------------------- */
bool AssetCatalogDefinitionFile::contains(const CatalogID catalog_id) const
{
return catalogs_.contains(catalog_id);

View File

@@ -0,0 +1,163 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup asset_system
*/
#include "AS_asset_catalog_tree.hh"
namespace blender::asset_system {
AssetCatalogTreeItem::AssetCatalogTreeItem(StringRef name,
CatalogID catalog_id,
StringRef simple_name,
const AssetCatalogTreeItem *parent)
: name_(name), catalog_id_(catalog_id), simple_name_(simple_name), parent_(parent)
{
}
CatalogID AssetCatalogTreeItem::get_catalog_id() const
{
return catalog_id_;
}
StringRefNull AssetCatalogTreeItem::get_name() const
{
return name_;
}
StringRefNull AssetCatalogTreeItem::get_simple_name() const
{
return simple_name_;
}
bool AssetCatalogTreeItem::has_unsaved_changes() const
{
return has_unsaved_changes_;
}
AssetCatalogPath AssetCatalogTreeItem::catalog_path() const
{
AssetCatalogPath current_path = name_;
for (const AssetCatalogTreeItem *parent = parent_; parent; parent = parent->parent_) {
current_path = AssetCatalogPath(parent->name_) / current_path;
}
return current_path;
}
int AssetCatalogTreeItem::count_parents() const
{
int i = 0;
for (const AssetCatalogTreeItem *parent = parent_; parent; parent = parent->parent_) {
i++;
}
return i;
}
bool AssetCatalogTreeItem::has_children() const
{
return !children_.empty();
}
void AssetCatalogTreeItem::foreach_item_recursive(AssetCatalogTreeItem::ChildMap &children,
const ItemIterFn callback)
{
for (auto &[key, item] : children) {
callback(item);
foreach_item_recursive(item.children_, callback);
}
}
void AssetCatalogTreeItem::foreach_child(const ItemIterFn callback)
{
for (auto &[key, item] : children_) {
callback(item);
}
}
/* ---------------------------------------------------------------------- */
void AssetCatalogTree::insert_item(const AssetCatalog &catalog)
{
const AssetCatalogTreeItem *parent = nullptr;
/* The children for the currently iterated component, where the following component should be
* added to (if not there yet). */
AssetCatalogTreeItem::ChildMap *current_item_children = &root_items_;
BLI_assert_msg(!ELEM(catalog.path.str()[0], '/', '\\'),
"Malformed catalog path; should not start with a separator");
const CatalogID nil_id{};
catalog.path.iterate_components([&](StringRef component_name, const bool is_last_component) {
/* Insert new tree element - if no matching one is there yet! */
auto [key_and_item, was_inserted] = current_item_children->emplace(
component_name,
AssetCatalogTreeItem(component_name,
is_last_component ? catalog.catalog_id : nil_id,
is_last_component ? catalog.simple_name : "",
parent));
AssetCatalogTreeItem &item = key_and_item->second;
/* If full path of this catalog already exists as parent path of a previously read catalog,
* we can ensure this tree item's UUID is set here. */
if (is_last_component) {
if (BLI_uuid_is_nil(item.catalog_id_) || catalog.flags.is_first_loaded) {
item.catalog_id_ = catalog.catalog_id;
}
item.has_unsaved_changes_ = catalog.flags.has_unsaved_changes;
}
/* Walk further into the path (no matter if a new item was created or not). */
parent = &item;
current_item_children = &item.children_;
});
}
void AssetCatalogTree::foreach_item(AssetCatalogTreeItem::ItemIterFn callback)
{
AssetCatalogTreeItem::foreach_item_recursive(root_items_, callback);
}
void AssetCatalogTree::foreach_root_item(const ItemIterFn callback)
{
for (auto &[key, item] : root_items_) {
callback(item);
}
}
bool AssetCatalogTree::is_empty() const
{
return root_items_.empty();
}
AssetCatalogTreeItem *AssetCatalogTree::find_item(const AssetCatalogPath &path)
{
AssetCatalogTreeItem *result = nullptr;
this->foreach_item([&](AssetCatalogTreeItem &item) {
if (result) {
/* There is no way to stop iteration. */
return;
}
if (item.catalog_path() == path) {
result = &item;
}
});
return result;
}
AssetCatalogTreeItem *AssetCatalogTree::find_root_item(const AssetCatalogPath &path)
{
AssetCatalogTreeItem *result = nullptr;
this->foreach_root_item([&](AssetCatalogTreeItem &item) {
if (result) {
/* There is no way to stop iteration. */
return;
}
if (item.catalog_path() == path) {
result = &item;
}
});
return result;
}
} // namespace blender::asset_system

View File

@@ -6,6 +6,7 @@
#include <memory>
#include "AS_asset_catalog_tree.hh"
#include "AS_asset_library.h"
#include "AS_asset_library.hh"
#include "AS_asset_representation.hh"

View File

@@ -2,6 +2,7 @@
* Copyright 2020 Blender Foundation. All rights reserved. */
#include "AS_asset_catalog.hh"
#include "AS_asset_catalog_tree.hh"
#include "BKE_appdir.h"
#include "BKE_preferences.h"

View File

@@ -7,9 +7,10 @@
#include "DNA_space_types.h"
#include "AS_asset_catalog.hh"
#include "AS_asset_catalog_tree.hh"
#include "AS_asset_library.hh"
#include "AS_asset_catalog.hh"
#include "BKE_asset.h"
#include "BLI_string_ref.hh"

View File

@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "AS_asset_catalog.hh"
#include "AS_asset_catalog_tree.hh"
#include "AS_asset_library.hh"
#include "BLI_multi_value_map.hh"