[Bf-blender-cvs] [824733ea47d] master: Assets: Additions/fixes to the catalog system in preparation for the UI

Julian Eisel noreply at git.blender.org
Mon Sep 27 17:49:36 CEST 2021


Commit: 824733ea47d13030c18faa618c1dc31a08dfa43d
Author: Julian Eisel
Date:   Mon Sep 27 17:45:02 2021 +0200
Branches: master
https://developer.blender.org/rB824733ea47d13030c18faa618c1dc31a08dfa43d

Assets: Additions/fixes to the catalog system in preparation for the UI

* Fixes missing update of the catalog tree when adding catalogs.
* Adds iterators for the catalogs, needed for UI code.
* Store catalog ID in the catalog tree items, needed for UI code.
* Other smaller API additions for the UI.
* Improve comments and smaller cleanups.

New functions are covered with unit tests.

Differential Revision: https://developer.blender.org/D12618

Reviewed by: Sybren Stüvel

===================================================================

M	source/blender/blenkernel/BKE_asset_catalog.hh
M	source/blender/blenkernel/intern/asset_catalog.cc
M	source/blender/blenkernel/intern/asset_catalog_test.cc

===================================================================

diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh
index cf9d581b148..07373caf701 100644
--- a/source/blender/blenkernel/BKE_asset_catalog.hh
+++ b/source/blender/blenkernel/BKE_asset_catalog.hh
@@ -86,6 +86,10 @@ class AssetCatalogService {
   /** Return catalog with the given ID. Return nullptr if not found. */
   AssetCatalog *find_catalog(CatalogID catalog_id);
 
+  /** Return first catalog with the given path. Return nullptr if not found. This is not an
+   * efficient call as it's just a linear search over the catalogs. */
+  AssetCatalog *find_catalog_by_path(const CatalogPath &path) 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 CatalogPath &catalog_path);
@@ -124,48 +128,74 @@ class AssetCatalogService {
   void rebuild_tree();
 };
 
+/**
+ * Representation of a catalog path in the #AssetCatalogTree.
+ */
 class AssetCatalogTreeItem {
-  friend class AssetCatalogService;
+  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(const AssetCatalogTreeItem &)>;
+  using ItemIterFn = FunctionRef<void(AssetCatalogTreeItem &)>;
 
-  AssetCatalogTreeItem(StringRef name, const AssetCatalogTreeItem *parent = nullptr);
+  AssetCatalogTreeItem(StringRef name,
+                       CatalogID catalog_id,
+                       const AssetCatalogTreeItem *parent = nullptr);
 
+  CatalogID get_catalog_id() const;
   StringRef get_name() 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. */
   CatalogPath catalog_path() const;
   int count_parents() const;
+  bool has_children() const;
 
-  static void foreach_item_recursive(const ChildMap &children_, const ItemIterFn callback);
+  /** Iterate over children calling \a callback for each of them, but do not recurse into their
+   * children. */
+  void foreach_child(const ItemIterFn callback);
 
  protected:
   /** Child tree items, ordered by their names. */
   ChildMap children_;
   /** The user visible name of this component. */
   CatalogPathComponent name_;
+  CatalogID catalog_id_;
 
   /** 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 a #AssetCatalogTreeItem.
+ * 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 {
-  friend class AssetCatalogService;
+  using ChildMap = AssetCatalogTreeItem::ChildMap;
+  using ItemIterFn = AssetCatalogTreeItem::ItemIterFn;
 
  public:
-  void foreach_item(const AssetCatalogTreeItem::ItemIterFn callback) const;
+  /** Ensure an item representing \a path is in the tree, adding it if necessary. */
+  void insert_item(const AssetCatalog &catalog);
+
+  void foreach_item(const AssetCatalogTreeItem::ItemIterFn callback);
+  /** Iterate over root items calling \a callback for each of them, but do not recurse into their
+   * children. */
+  void foreach_root_item(const ItemIterFn callback);
 
  protected:
   /** Child tree items, ordered by their names. */
-  AssetCatalogTreeItem::ChildMap children_;
+  ChildMap root_items_;
 };
 
 /** Keeps track of which catalogs are defined in a certain file on disk.
diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc
index 4f1de09e148..b65ae12e5a7 100644
--- a/source/blender/blenkernel/intern/asset_catalog.cc
+++ b/source/blender/blenkernel/intern/asset_catalog.cc
@@ -64,6 +64,17 @@ AssetCatalog *AssetCatalogService::find_catalog(CatalogID catalog_id)
   return catalog_uptr_ptr->get();
 }
 
+AssetCatalog *AssetCatalogService::find_catalog_by_path(const CatalogPath &path) const
+{
+  for (auto &catalog : catalogs_.values()) {
+    if (catalog->path == path) {
+      return catalog.get();
+    }
+  }
+
+  return nullptr;
+}
+
 void AssetCatalogService::delete_catalog(CatalogID catalog_id)
 {
   std::unique_ptr<AssetCatalog> *catalog_uptr_ptr = this->catalogs_.lookup_ptr(catalog_id);
@@ -104,6 +115,12 @@ AssetCatalog *AssetCatalogService::create_catalog(const CatalogPath &catalog_pat
     catalog_definition_file_->add_new(catalog_ptr);
   }
 
+  /* The tree may not exist; this happens when no catalog definition file has been loaded yet. When
+   * the tree is created any in-memory catalogs will be added, so it doesn't need to happen now. */
+  if (catalog_tree_) {
+    catalog_tree_->insert_item(*catalog_ptr);
+  }
+
   return catalog_ptr;
 }
 
@@ -268,34 +285,7 @@ std::unique_ptr<AssetCatalogTree> AssetCatalogService::read_into_tree()
 
   /* Go through the catalogs, insert each path component into the tree where needed. */
   for (auto &catalog : catalogs_.values()) {
-    const AssetCatalogTreeItem *parent = nullptr;
-    AssetCatalogTreeItem::ChildMap *insert_to_map = &tree->children_;
-
-    BLI_assert_msg(!ELEM(catalog->path[0], '/', '\\'),
-                   "Malformed catalog path; should not start with a separator");
-
-    const char *next_slash_ptr;
-    /* Looks more complicated than it is, this just iterates over path components. E.g.
-     * "just/some/path" iterates over "just", then "some" then "path". */
-    for (const char *name_begin = catalog->path.data(); name_begin && name_begin[0];
-         /* Jump to one after the next slash if there is any. */
-         name_begin = next_slash_ptr ? next_slash_ptr + 1 : nullptr) {
-      next_slash_ptr = BLI_path_slash_find(name_begin);
-
-      /* Note that this won't be null terminated. */
-      StringRef component_name = next_slash_ptr ?
-                                     StringRef(name_begin, next_slash_ptr - name_begin) :
-                                     /* Last component in the path. */
-                                     name_begin;
-
-      /* Insert new tree element - if no matching one is there yet! */
-      auto [item, was_inserted] = insert_to_map->emplace(
-          component_name, AssetCatalogTreeItem(component_name, parent));
-
-      /* Walk further into the path (no matter if a new item was created or not). */
-      parent = &item->second;
-      insert_to_map = &item->second.children_;
-    }
+    tree->insert_item(*catalog);
   }
 
   return tree;
@@ -306,9 +296,18 @@ void AssetCatalogService::rebuild_tree()
   this->catalog_tree_ = read_into_tree();
 }
 
-AssetCatalogTreeItem::AssetCatalogTreeItem(StringRef name, const AssetCatalogTreeItem *parent)
-    : name_(name), parent_(parent)
+/* ---------------------------------------------------------------------- */
+
+AssetCatalogTreeItem::AssetCatalogTreeItem(StringRef name,
+                                           CatalogID catalog_id,
+                                           const AssetCatalogTreeItem *parent)
+    : name_(name), catalog_id_(catalog_id), parent_(parent)
+{
+}
+
+CatalogID AssetCatalogTreeItem::get_catalog_id() const
 {
+  return catalog_id_;
 }
 
 StringRef AssetCatalogTreeItem::get_name() const
@@ -334,20 +333,100 @@ int AssetCatalogTreeItem::count_parents() const
   return i;
 }
 
-void AssetCatalogTree::foreach_item(const AssetCatalogTreeItem::ItemIterFn callback) const
+bool AssetCatalogTreeItem::has_children() const
+{
+  return !children_.empty();
+}
+
+/* ---------------------------------------------------------------------- */
+
+/**
+ * Iterate over path components, calling \a callback for each component. E.g. "just/some/path"
+ * iterates over "just", then "some" then "path".
+ */
+static void iterate_over_catalog_path_components(
+    const CatalogPath &path,
+    FunctionRef<void(StringRef component_name, bool is_last_component)> callback)
+{
+  const char *next_slash_ptr;
+
+  for (const char *path_component = path.data(); path_component && path_component[0];
+       /* Jump to one after the next slash if there is any. */
+       path_component = next_slash_ptr ? next_slash_ptr + 1 : nullptr) {
+    next_slash_ptr = BLI_path_slash_find(path_component);
+
+    const bool is_last_component = next_slash_ptr == nullptr;
+    /* Note that this won't be null terminated. */
+    const StringRef component_name = is_last_component ?
+                                         path_component :
+                                         StringRef(path_component,
+                                                   next_slash_ptr - path_component);
+
+    callback(component_name, is_last_component);
+  }
+}
+
+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[0], '/', '\\'),
+                 "Malformed catalog path; should not start with a separator");
+
+  const CatalogID nil_id{};
+
+  iterate_over_catalog_path_components(
+      catalog.path, [&](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, parent));
+   

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list