[Bf-blender-cvs] [50f4e176960] temp-asset-browser-catalogs-ui: Support opening and collapsing tree-view items

Julian Eisel noreply at git.blender.org
Fri Sep 10 21:41:39 CEST 2021


Commit: 50f4e176960d2bae5b402667d8b912dfaebb211d
Author: Julian Eisel
Date:   Fri Sep 10 21:36:18 2021 +0200
Branches: temp-asset-browser-catalogs-ui
https://developer.blender.org/rB50f4e176960d2bae5b402667d8b912dfaebb211d

Support opening and collapsing tree-view items

For this to work, we need to support tree-view items with state that is
persistent over redraws. This is achieved by matching tree items to tree
items of a previous redraw, and copying the state if needed. This is
done in the tree-view API, so except for more advanced cases (with
custom item state or custom ways to match items), API users shouldn't
have to deal with this.

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

M	source/blender/editors/include/UI_interface.h
M	source/blender/editors/include/UI_tree_view.hh
M	source/blender/editors/interface/interface.c
M	source/blender/editors/interface/interface_handlers.c
M	source/blender/editors/interface/interface_intern.h
M	source/blender/editors/interface/interface_view.cc
M	source/blender/editors/interface/tree_view.cc
M	source/blender/editors/space_file/asset_catalog_tree_view.cc

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

diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 683c39987de..2b8667aaca1 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -85,7 +85,9 @@ typedef struct uiBut uiBut;
 typedef struct uiLayout uiLayout;
 typedef struct uiPopupBlockHandle uiPopupBlockHandle;
 /* C handle for C++ #uiAbstractTreeView type. */
-typedef struct uiAbstractTreeViewHandle uiAbstractTreeViewHandle;
+typedef struct uiTreeViewHandle uiTreeViewHandle;
+/* C handle for C++ #uiAbstractTreeViewItem type. */
+typedef struct uiTreeViewItemHandle uiTreeViewItemHandle;
 
 /* Defines */
 
diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh
index d715f58a52b..725975b2b8d 100644
--- a/source/blender/editors/include/UI_tree_view.hh
+++ b/source/blender/editors/include/UI_tree_view.hh
@@ -36,6 +36,7 @@ struct uiLayout;
 
 namespace blender::ui {
 
+class uiAbstractTreeView;
 class uiAbstractTreeViewItem;
 
 /* ---------------------------------------------------------------------- */
@@ -83,29 +84,56 @@ class uiTreeViewItemContainer {
 /** \} */
 
 /* ---------------------------------------------------------------------- */
-/** \name Tree-View Base Class
+/** \name Tree-View Builders
  * \{ */
 
-class uiTreeViewLayoutBuilder {
+class uiTreeViewBuilder {
   uiBlock &block_;
 
  public:
-  uiTreeViewLayoutBuilder(uiBlock &);
+  uiTreeViewBuilder(uiBlock &block);
+
+  void build_tree_view(uiAbstractTreeView &tree_view);
+};
+
+class uiTreeViewLayoutBuilder {
+  uiBlock &block_;
 
+  friend uiTreeViewBuilder;
+
+ public:
   void build_row(uiAbstractTreeViewItem &item) const;
   uiBlock &block() const;
   uiLayout *current_layout() const;
+
+ private:
+  /* Created through #uiTreeViewBuilder. */
+  uiTreeViewLayoutBuilder(uiBlock &block);
 };
 
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Tree-View Base Class
+ * \{ */
+
 class uiAbstractTreeView : public uiTreeViewItemContainer {
+  friend uiTreeViewBuilder;
+  friend uiTreeViewLayoutBuilder;
+
  public:
   virtual ~uiAbstractTreeView() = default;
 
+ protected:
   virtual void build_tree() = 0;
 
-  void build_layout_from_tree(const uiTreeViewLayoutBuilder &builder);
-
  private:
+  void update_from_old(uiBlock &new_block);
+  static void update_children_from_old_recursive(const uiTreeViewItemContainer &new_items,
+                                                 const uiTreeViewItemContainer &old_items);
+  static uiAbstractTreeViewItem *find_matching_child(const uiAbstractTreeViewItem &lookup_item,
+                                                     const uiTreeViewItemContainer &items);
+  void build_layout_from_tree(const uiTreeViewLayoutBuilder &builder);
   void build_layout_from_tree_recursive(const uiTreeViewLayoutBuilder &builder,
                                         const uiTreeViewItemContainer &items);
 };
@@ -126,15 +154,26 @@ class uiAbstractTreeView : public uiTreeViewItemContainer {
 class uiAbstractTreeViewItem : public uiTreeViewItemContainer {
   friend class uiAbstractTreeView;
 
-  bool is_open_;
+  bool is_open_ = false;
+
+ protected:
+  /** This label is used for identifying an item (together with its parents labels). */
+  std::string label_{};
 
  public:
   virtual ~uiAbstractTreeViewItem() = default;
 
   virtual void build_row(uiLayout &row) = 0;
 
+  /** Copy persistent state (e.g. is-collapsed flag, selection, etc.) from a matching item of the
+   * last redraw to this item. If sub-classes introduce more advanced state they should override
+   * this and make update their state accordingly. */
+  virtual void update_from_old(uiAbstractTreeViewItem &old);
+
   int count_parents() const;
   void toggle_collapsed();
+  bool is_collapsed() const;
+  void set_collapsed(bool collapsed);
   bool is_collapsible() const;
 };
 
@@ -151,7 +190,6 @@ class uiAbstractTreeViewItem : public uiTreeViewItemContainer {
  */
 class uiBasicTreeViewItem : public uiAbstractTreeViewItem {
  public:
-  std::string label;
   BIFIconID icon;
 
   uiBasicTreeViewItem(StringRef label, BIFIconID icon = ICON_NONE);
@@ -161,7 +199,9 @@ class uiBasicTreeViewItem : public uiAbstractTreeViewItem {
  protected:
   /** Created in the #build() function. */
   uiButTreeRow *tree_row_but_ = nullptr;
+
   uiBut *button();
+  BIFIconID get_draw_icon() const;
 };
 
 /** \} */
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 4901e594174..e3c7b606ee4 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -856,10 +856,21 @@ static void ui_but_update_old_active_from_new(uiBut *oldbut, uiBut *but)
     oldbut->hardmax = but->hardmax;
   }
 
-  if (oldbut->type == UI_BTYPE_PROGRESS_BAR) {
-    uiButProgressbar *progress_oldbut = (uiButProgressbar *)oldbut;
-    uiButProgressbar *progress_but = (uiButProgressbar *)but;
-    progress_oldbut->progress = progress_but->progress;
+  switch (oldbut->type) {
+    case UI_BTYPE_PROGRESS_BAR: {
+      uiButProgressbar *progress_oldbut = (uiButProgressbar *)oldbut;
+      uiButProgressbar *progress_but = (uiButProgressbar *)but;
+      progress_oldbut->progress = progress_but->progress;
+      break;
+    }
+    case UI_BTYPE_TREEROW: {
+      uiButTreeRow *treerow_oldbut = (uiButTreeRow *)oldbut;
+      uiButTreeRow *treerow_newbut = (uiButTreeRow *)but;
+      SWAP(uiTreeViewItemHandle *, treerow_newbut->tree_item, treerow_oldbut->tree_item);
+      break;
+    }
+    default:
+      break;
   }
 
   /* move/copy string from the new button to the old */
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 5bc496c0a4e..e77a16b747e 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -384,6 +384,8 @@ typedef struct uiHandleButtonData {
   /* booleans (could be made into flags) */
   bool cancel, escapecancel;
   bool applied, applied_interactive;
+  /* Button is being applied through an extra icon. */
+  bool apply_through_extra_icon;
   bool changed_cursor;
   wmTimer *flashtimer;
 
@@ -1156,6 +1158,16 @@ static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleBu
   data->applied = true;
 }
 
+static void ui_apply_but_TREEROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
+{
+  if (data->apply_through_extra_icon) {
+    /* Don't apply this, it would cause unintended tree-row toggling when clicking on extra icons.
+     */
+    return;
+  }
+  ui_apply_but_ROW(C, block, but, data);
+}
+
 /**
  * \note Ownership of \a properties is moved here. The #uiAfterFunc owns it now.
  *
@@ -2297,9 +2309,11 @@ static void ui_apply_but(
       ui_apply_but_TOG(C, but, data);
       break;
     case UI_BTYPE_ROW:
-    case UI_BTYPE_TREEROW:
       ui_apply_but_ROW(C, block, but, data);
       break;
+    case UI_BTYPE_TREEROW:
+      ui_apply_but_TREEROW(C, block, but, data);
+      break;
     case UI_BTYPE_LISTROW:
       ui_apply_but_LISTROW(C, block, but, data);
       break;
@@ -4187,6 +4201,8 @@ static void ui_numedit_apply(bContext *C, uiBlock *block, uiBut *but, uiHandleBu
 
 static void ui_but_extra_operator_icon_apply(bContext *C, uiBut *but, uiButExtraOpIcon *op_icon)
 {
+  but->active->apply_through_extra_icon = true;
+
   if (but->active->interactive) {
     ui_apply_but(C, but->block, but, but->active, true);
   }
@@ -4729,7 +4745,7 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons
         /* Behave like other menu items. */
         do_activate = (event->val == KM_RELEASE);
       }
-      else {
+      else if (!ui_do_but_extra_operator_icon(C, but, data, event)) {
         /* Also use double-clicks to prevent fast clicks to leak to other handlers (T76481). */
         do_activate = ELEM(event->val, KM_PRESS, KM_DBL_CLICK);
       }
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 9d5df53681e..0bcad3f6b1d 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -364,6 +364,7 @@ typedef struct uiButDatasetRow {
 typedef struct uiButTreeRow {
   uiBut but;
 
+  uiTreeViewItemHandle *tree_item;
   int indentation;
 } uiButTreeRow;
 
@@ -1288,6 +1289,8 @@ void ui_interface_tag_script_reload_queries(void);
 
 /* interface_view.cc */
 void ui_block_free_views(struct uiBlock *block);
+uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block,
+                                                           const uiTreeViewHandle *new_view);
 
 #ifdef __cplusplus
 }
diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/interface_view.cc
index c7bf34d2098..b7b14b5ad64 100644
--- a/source/blender/editors/interface/interface_view.cc
+++ b/source/blender/editors/interface/interface_view.cc
@@ -16,6 +16,11 @@
 
 /** \file
  * \ingroup edinterface
+ *
+ * This part of the UI-View API is mostly needed to support persistent state of items within the
+ * view. Views are stored in #uiBlock's, and kept alive with it until after the next redraw. So we
+ * can compare the old view items with the new view items and keep state persistent for matching
+ * ones.
  */
 
 #include <memory>
@@ -32,7 +37,8 @@ using namespace blender;
 using namespace blender::ui;
 
 /**
- * Wrapper to store views in a #ListBase.
+ * Wrapper to store views in a #ListBase. There's no `uiView` base class, we just store views as a
+ * #std::variant.
  */
 struct uiViewLink : public Link {
   using TreeViewPtr = std::unique_ptr<uiAbstractTreeView>;
@@ -42,6 +48,15 @@ struct uiViewLink : public Link {
   std::variant<TreeViewPtr> view;
 };
 
+template<class T> T *get_view_from_link(uiViewLink &link)
+{
+  auto *t_uptr = std::get_if<std::unique_ptr<T>>(&link.view);
+  return t_uptr ? t_uptr->get() : nullptr;
+}
+
+/**
+ * Override this for all available tree types available.

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list