Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSybren A. Stüvel <sybren@blender.org>2021-10-12 13:39:24 +0300
committerSybren A. Stüvel <sybren@blender.org>2021-10-12 13:42:44 +0300
commita06435e43a6516207c617c2b4ad9961f865b66d8 (patch)
treed21e378f4c33f1d203cefeeca03dbefa81691158 /source/blender/blenkernel/intern/asset_catalog.cc
parentb67a9373945d07700b378aaa8be06aa2fd6d2cb9 (diff)
Asset Catalogs: undo stack for catalog edits
Add an undo stack for catalog edits. This only implements the backend, no operators or UI yet. A bunch of `this->xxx` has been replaced by `catalog_collection_->xxx`. Things are getting a bit long, and the class is turning into a god object; refactoring the class is tracked in T92114. Reviewed By: Severin Maniphest Tasks: T92047 Differential Revision: https://developer.blender.org/D12825
Diffstat (limited to 'source/blender/blenkernel/intern/asset_catalog.cc')
-rw-r--r--source/blender/blenkernel/intern/asset_catalog.cc181
1 files changed, 146 insertions, 35 deletions
diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc
index 4531dabf0cf..1b8ab6f7196 100644
--- a/source/blender/blenkernel/intern/asset_catalog.cc
+++ b/source/blender/blenkernel/intern/asset_catalog.cc
@@ -55,19 +55,36 @@ const std::string AssetCatalogDefinitionFile::HEADER =
"# The first non-ignored line should be the version indicator.\n"
"# Other lines are of the format \"UUID:catalog/path/for/assets:simple catalog name\"\n";
+AssetCatalogService::AssetCatalogService()
+ : catalog_collection_(std::make_unique<AssetCatalogCollection>())
+{
+}
+
AssetCatalogService::AssetCatalogService(const CatalogFilePath &asset_library_root)
- : asset_library_root_(asset_library_root)
+ : catalog_collection_(std::make_unique<AssetCatalogCollection>()),
+ asset_library_root_(asset_library_root)
{
}
bool AssetCatalogService::is_empty() const
{
- return catalogs_.is_empty();
+ return catalog_collection_->catalogs_.is_empty();
+}
+
+OwningAssetCatalogMap &AssetCatalogService::get_catalogs()
+{
+ return catalog_collection_->catalogs_;
+}
+
+AssetCatalogDefinitionFile *AssetCatalogService::get_catalog_definition_file()
+{
+ return catalog_collection_->catalog_definition_file_.get();
}
AssetCatalog *AssetCatalogService::find_catalog(CatalogID catalog_id) const
{
- const std::unique_ptr<AssetCatalog> *catalog_uptr_ptr = this->catalogs_.lookup_ptr(catalog_id);
+ const std::unique_ptr<AssetCatalog> *catalog_uptr_ptr =
+ catalog_collection_->catalogs_.lookup_ptr(catalog_id);
if (catalog_uptr_ptr == nullptr) {
return nullptr;
}
@@ -76,7 +93,7 @@ AssetCatalog *AssetCatalogService::find_catalog(CatalogID catalog_id) const
AssetCatalog *AssetCatalogService::find_catalog_by_path(const AssetCatalogPath &path) const
{
- for (const auto &catalog : catalogs_.values()) {
+ for (const auto &catalog : catalog_collection_->catalogs_.values()) {
if (catalog->path == path) {
return catalog.get();
}
@@ -103,7 +120,7 @@ AssetCatalogFilter AssetCatalogService::create_catalog_filter(
* 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()) {
+ for (const auto &catalog_uptr : catalog_collection_->catalogs_.values()) {
if (catalog_uptr->path.is_contained_in(active_catalog->path)) {
matching_catalog_ids.add(catalog_uptr->catalog_id);
}
@@ -114,7 +131,8 @@ AssetCatalogFilter AssetCatalogService::create_catalog_filter(
void AssetCatalogService::delete_catalog_by_id(const CatalogID catalog_id)
{
- std::unique_ptr<AssetCatalog> *catalog_uptr_ptr = this->catalogs_.lookup_ptr(catalog_id);
+ std::unique_ptr<AssetCatalog> *catalog_uptr_ptr = catalog_collection_->catalogs_.lookup_ptr(
+ catalog_id);
if (catalog_uptr_ptr == nullptr) {
/* Catalog cannot be found, which is fine. */
return;
@@ -124,18 +142,19 @@ void AssetCatalogService::delete_catalog_by_id(const CatalogID catalog_id)
AssetCatalog *catalog = catalog_uptr_ptr->get();
catalog->flags.is_deleted = true;
- /* Move ownership from this->catalogs_ to this->deleted_catalogs_. */
- this->deleted_catalogs_.add(catalog_id, std::move(*catalog_uptr_ptr));
+ /* Move ownership from catalog_collection_->catalogs_ to catalog_collection_->deleted_catalogs_.
+ */
+ catalog_collection_->deleted_catalogs_.add(catalog_id, std::move(*catalog_uptr_ptr));
/* The catalog can now be removed from the map without freeing the actual AssetCatalog. */
- this->catalogs_.remove(catalog_id);
+ catalog_collection_->catalogs_.remove(catalog_id);
}
void AssetCatalogService::prune_catalogs_by_path(const AssetCatalogPath &path)
{
/* Build a collection of catalog IDs to delete. */
Set<CatalogID> catalogs_to_delete;
- for (const auto &catalog_uptr : this->catalogs_.values()) {
+ for (const auto &catalog_uptr : catalog_collection_->catalogs_.values()) {
const AssetCatalog *cat = catalog_uptr.get();
if (cat->path.is_contained_in(path)) {
catalogs_to_delete.add(cat->catalog_id);
@@ -166,7 +185,7 @@ void AssetCatalogService::update_catalog_path(const CatalogID catalog_id,
AssetCatalog *renamed_cat = this->find_catalog(catalog_id);
const AssetCatalogPath old_cat_path = renamed_cat->path;
- for (auto &catalog_uptr : catalogs_.values()) {
+ for (auto &catalog_uptr : catalog_collection_->catalogs_.values()) {
AssetCatalog *cat = catalog_uptr.get();
const AssetCatalogPath new_path = cat->path.rebase(old_cat_path, new_catalog_path);
@@ -189,13 +208,14 @@ AssetCatalog *AssetCatalogService::create_catalog(const AssetCatalogPath &catalo
/* TODO(@sybren): move the `AssetCatalog::from_path()` function to another place, that can reuse
* catalogs when a catalog with the given path is already known, and avoid duplicate catalog IDs.
*/
- BLI_assert_msg(!catalogs_.contains(catalog->catalog_id), "duplicate catalog ID not supported");
- catalogs_.add_new(catalog->catalog_id, std::move(catalog));
+ BLI_assert_msg(!catalog_collection_->catalogs_.contains(catalog->catalog_id),
+ "duplicate catalog ID not supported");
+ catalog_collection_->catalogs_.add_new(catalog->catalog_id, std::move(catalog));
- if (catalog_definition_file_) {
+ if (catalog_collection_->catalog_definition_file_) {
/* Ensure the new catalog gets written to disk at some point. If there is no CDF in memory yet,
* it's enough to have the catalog known to the service as it'll be saved to a new file. */
- catalog_definition_file_->add_new(catalog_ptr);
+ catalog_collection_->catalog_definition_file_->add_new(catalog_ptr);
}
BLI_assert_msg(catalog_tree_, "An Asset Catalog tree should always exist.");
@@ -263,9 +283,9 @@ void AssetCatalogService::load_single_file(const CatalogFilePath &catalog_defini
std::unique_ptr<AssetCatalogDefinitionFile> cdf = parse_catalog_file(
catalog_definition_file_path);
- BLI_assert_msg(!this->catalog_definition_file_,
+ BLI_assert_msg(!catalog_collection_->catalog_definition_file_,
"Only loading of a single catalog definition file is supported.");
- this->catalog_definition_file_ = std::move(cdf);
+ catalog_collection_->catalog_definition_file_ = std::move(cdf);
}
std::unique_ptr<AssetCatalogDefinitionFile> AssetCatalogService::parse_catalog_file(
@@ -276,7 +296,7 @@ std::unique_ptr<AssetCatalogDefinitionFile> AssetCatalogService::parse_catalog_f
auto catalog_parsed_callback = [this, catalog_definition_file_path](
std::unique_ptr<AssetCatalog> catalog) {
- if (this->catalogs_.contains(catalog->catalog_id)) {
+ if (catalog_collection_->catalogs_.contains(catalog->catalog_id)) {
// TODO(@sybren): apparently another CDF was already loaded. This is not supported yet.
std::cerr << catalog_definition_file_path << ": multiple definitions of catalog "
<< catalog->catalog_id << " in multiple files, ignoring this one." << std::endl;
@@ -285,7 +305,7 @@ std::unique_ptr<AssetCatalogDefinitionFile> AssetCatalogService::parse_catalog_f
}
/* The AssetCatalog pointer is now owned by the AssetCatalogService. */
- this->catalogs_.add_new(catalog->catalog_id, std::move(catalog));
+ catalog_collection_->catalogs_.add_new(catalog->catalog_id, std::move(catalog));
return true;
};
@@ -297,9 +317,8 @@ std::unique_ptr<AssetCatalogDefinitionFile> AssetCatalogService::parse_catalog_f
void AssetCatalogService::merge_from_disk_before_writing()
{
/* TODO(Sybren): expand to support multiple CDFs. */
-
- if (!catalog_definition_file_ || catalog_definition_file_->file_path.empty() ||
- !BLI_is_file(catalog_definition_file_->file_path.c_str())) {
+ AssetCatalogDefinitionFile *const cdf = catalog_collection_->catalog_definition_file_.get();
+ if (!cdf || cdf->file_path.empty() || !BLI_is_file(cdf->file_path.c_str())) {
return;
}
@@ -308,22 +327,21 @@ void AssetCatalogService::merge_from_disk_before_writing()
/* The following two conditions could be or'ed together. Keeping them separated helps when
* adding debug prints, breakpoints, etc. */
- if (this->catalogs_.contains(catalog_id)) {
+ if (catalog_collection_->catalogs_.contains(catalog_id)) {
/* This catalog was already seen, so just ignore it. */
return false;
}
- if (this->deleted_catalogs_.contains(catalog_id)) {
+ if (catalog_collection_->deleted_catalogs_.contains(catalog_id)) {
/* This catalog was already seen and subsequently deleted, so just ignore it. */
return false;
}
/* This is a new catalog, so let's keep it around. */
- this->catalogs_.add_new(catalog_id, std::move(catalog));
+ catalog_collection_->catalogs_.add_new(catalog_id, std::move(catalog));
return true;
};
- catalog_definition_file_->parse_catalog_file(catalog_definition_file_->file_path,
- catalog_parsed_callback);
+ cdf->parse_catalog_file(cdf->file_path, catalog_parsed_callback);
}
bool AssetCatalogService::write_to_disk_on_blendfile_save(const CatalogFilePath &blend_file_path)
@@ -331,20 +349,21 @@ bool AssetCatalogService::write_to_disk_on_blendfile_save(const CatalogFilePath
/* TODO(Sybren): expand to support multiple CDFs. */
/* - Already loaded a CDF from disk? -> Always write to that file. */
- if (this->catalog_definition_file_) {
+ if (catalog_collection_->catalog_definition_file_) {
merge_from_disk_before_writing();
- return catalog_definition_file_->write_to_disk();
+ return catalog_collection_->catalog_definition_file_->write_to_disk();
}
- if (catalogs_.is_empty() && deleted_catalogs_.is_empty()) {
+ if (catalog_collection_->catalogs_.is_empty() &&
+ catalog_collection_->deleted_catalogs_.is_empty()) {
/* Avoid saving anything, when there is nothing to save. */
return true; /* Writing nothing when there is nothing to write is still a success. */
}
const CatalogFilePath cdf_path_to_write = find_suitable_cdf_path_for_writing(blend_file_path);
- this->catalog_definition_file_ = construct_cdf_in_memory(cdf_path_to_write);
+ catalog_collection_->catalog_definition_file_ = construct_cdf_in_memory(cdf_path_to_write);
merge_from_disk_before_writing();
- return catalog_definition_file_->write_to_disk();
+ return catalog_collection_->catalog_definition_file_->write_to_disk();
}
CatalogFilePath AssetCatalogService::find_suitable_cdf_path_for_writing(
@@ -382,7 +401,7 @@ std::unique_ptr<AssetCatalogDefinitionFile> AssetCatalogService::construct_cdf_i
auto cdf = std::make_unique<AssetCatalogDefinitionFile>();
cdf->file_path = file_path;
- for (auto &catalog : catalogs_.values()) {
+ for (auto &catalog : catalog_collection_->catalogs_.values()) {
cdf->add_new(catalog.get());
}
@@ -399,7 +418,7 @@ std::unique_ptr<AssetCatalogTree> AssetCatalogService::read_into_tree()
auto tree = std::make_unique<AssetCatalogTree>();
/* Go through the catalogs, insert each path component into the tree where needed. */
- for (auto &catalog : catalogs_.values()) {
+ for (auto &catalog : catalog_collection_->catalogs_.values()) {
tree->insert_item(*catalog);
}
@@ -416,7 +435,7 @@ void AssetCatalogService::create_missing_catalogs()
{
/* Construct an ordered set of paths to check, so that parents are ordered before children. */
std::set<AssetCatalogPath> paths_to_check;
- for (auto &catalog : catalogs_.values()) {
+ for (auto &catalog : catalog_collection_->catalogs_.values()) {
paths_to_check.insert(catalog->path);
}
@@ -450,6 +469,68 @@ void AssetCatalogService::create_missing_catalogs()
/* TODO(Sybren): bind the newly created catalogs to a CDF, if we know about it. */
}
+bool AssetCatalogService::is_undo_possbile() const
+{
+ return !undo_snapshots_.is_empty();
+}
+
+bool AssetCatalogService::is_redo_possbile() const
+{
+ return !redo_snapshots_.is_empty();
+}
+
+void AssetCatalogService::undo()
+{
+ BLI_assert_msg(is_undo_possbile(), "Undo stack is empty");
+
+ redo_snapshots_.append(std::move(catalog_collection_));
+ catalog_collection_ = std::move(undo_snapshots_.pop_last());
+}
+
+void AssetCatalogService::redo()
+{
+ BLI_assert_msg(is_redo_possbile(), "Redo stack is empty");
+
+ undo_snapshots_.append(std::move(catalog_collection_));
+ catalog_collection_ = std::move(redo_snapshots_.pop_last());
+}
+
+void AssetCatalogService::store_undo_snapshot()
+{
+ std::unique_ptr<AssetCatalogCollection> snapshot = catalog_collection_->deep_copy();
+ undo_snapshots_.append(std::move(snapshot));
+ redo_snapshots_.clear();
+}
+
+/* ---------------------------------------------------------------------- */
+
+std::unique_ptr<AssetCatalogCollection> AssetCatalogCollection::deep_copy() const
+{
+ auto copy = std::make_unique<AssetCatalogCollection>();
+
+ copy->catalogs_ = std::move(copy_catalog_map(this->catalogs_));
+ copy->deleted_catalogs_ = std::move(copy_catalog_map(this->deleted_catalogs_));
+
+ if (catalog_definition_file_) {
+ copy->catalog_definition_file_ = std::move(
+ catalog_definition_file_->copy_and_remap(copy->catalogs_, copy->deleted_catalogs_));
+ }
+
+ return copy;
+}
+
+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<AssetCatalog>(*orig_catalog_uptr);
+ copy.add_new(copy_catalog_uptr->catalog_id, std::move(copy_catalog_uptr));
+ }
+
+ return copy;
+}
+
/* ---------------------------------------------------------------------- */
AssetCatalogTreeItem::AssetCatalogTreeItem(StringRef name,
@@ -564,6 +645,8 @@ void AssetCatalogTree::foreach_root_item(const ItemIterFn callback)
/* ---------------------------------------------------------------------- */
+/* ---------------------------------------------------------------------- */
+
bool AssetCatalogDefinitionFile::contains(const CatalogID catalog_id) const
{
return catalogs_.contains(catalog_id);
@@ -782,6 +865,34 @@ bool AssetCatalogDefinitionFile::ensure_directory_exists(
return true;
}
+std::unique_ptr<AssetCatalogDefinitionFile> AssetCatalogDefinitionFile::copy_and_remap(
+ const OwningAssetCatalogMap &catalogs, const OwningAssetCatalogMap &deleted_catalogs) const
+{
+ auto copy = std::make_unique<AssetCatalogDefinitionFile>(*this);
+ copy->catalogs_.clear();
+
+ /* Remap pointers of the copy from the original AssetCatalogCollection to the given one. */
+ for (CatalogID catalog_id : catalogs_.keys()) {
+ /* The catalog can be in the regular or the deleted map. */
+ const std::unique_ptr<AssetCatalog> *remapped_catalog_uptr_ptr = catalogs.lookup_ptr(
+ catalog_id);
+ if (remapped_catalog_uptr_ptr) {
+ copy->catalogs_.add_new(catalog_id, remapped_catalog_uptr_ptr->get());
+ continue;
+ }
+
+ remapped_catalog_uptr_ptr = deleted_catalogs.lookup_ptr(catalog_id);
+ if (remapped_catalog_uptr_ptr) {
+ copy->catalogs_.add_new(catalog_id, remapped_catalog_uptr_ptr->get());
+ continue;
+ }
+
+ BLI_assert(!"A CDF should only reference known catalogs.");
+ }
+
+ return copy;
+}
+
AssetCatalog::AssetCatalog(const CatalogID catalog_id,
const AssetCatalogPath &path,
const std::string &simple_name)