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:
authorJulian Eisel <julian@blender.org>2021-09-23 19:56:29 +0300
committerJulian Eisel <julian@blender.org>2021-09-23 20:33:54 +0300
commit323fd80aada46105e23985f5646f9252b5e6f193 (patch)
treee422378a2e2932b091653dcd0eb5d94d0ed9a51d /source/blender/editors/include/UI_tree_view.hh
parenteb0eb54d9644c5139ef139fee1e14da35c4fab7e (diff)
UI: Tree-View API for easy creation of tree UIs
This follows three main targets: * Make creation of new tree UIs easy. * Groundwork to generalize tree UIs (so e.g. Outliner, animation channels, asset catalogs and spreadsheet data-sets don't have to re-implement basic tree UI code) or even other data-view UIs. * Better separate data and UI state. E.g. with this, tree-item selection or the open/collapsed state can be stored on the UI level, rather than in data. (Asset Catalogs need this, storing UI state info in them is not an option.) In addition, the design should be well testable and could even be exposed to Python. Note that things will likely change in master still. E.g. the actually resulting UI isn't very nice visually yet. The design is documented here: https://wiki.blender.org/wiki/Source/Interface/Views Differential Revision: https://developer.blender.org/D12573
Diffstat (limited to 'source/blender/editors/include/UI_tree_view.hh')
-rw-r--r--source/blender/editors/include/UI_tree_view.hh238
1 files changed, 238 insertions, 0 deletions
diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh
new file mode 100644
index 00000000000..51b8e65521a
--- /dev/null
+++ b/source/blender/editors/include/UI_tree_view.hh
@@ -0,0 +1,238 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup editorui
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "BLI_function_ref.hh"
+#include "BLI_vector.hh"
+
+#include "UI_resources.h"
+
+struct PointerRNA;
+struct uiBlock;
+struct uiBut;
+struct uiButTreeRow;
+struct uiLayout;
+
+namespace blender::ui {
+
+class AbstractTreeView;
+class AbstractTreeViewItem;
+
+/* ---------------------------------------------------------------------- */
+/** \name Tree-View Item Container
+ * \{ */
+
+/**
+ * Helper base class to expose common child-item data and functionality to both #AbstractTreeView
+ * and #AbstractTreeViewItem.
+ *
+ * That means this type can be used whenever either a #AbstractTreeView or a
+ * #AbstractTreeViewItem is needed.
+ */
+class TreeViewItemContainer {
+ friend class AbstractTreeView;
+ friend class AbstractTreeViewItem;
+
+ /* Private constructor, so only the friends above can create this! */
+ TreeViewItemContainer() = default;
+
+ protected:
+ Vector<std::unique_ptr<AbstractTreeViewItem>> children_;
+ /** Adding the first item to the root will set this, then it's passed on to all children. */
+ TreeViewItemContainer *root_ = nullptr;
+ /** Pointer back to the owning item. */
+ AbstractTreeViewItem *parent_ = nullptr;
+
+ public:
+ enum class IterOptions {
+ None = 0,
+ SkipCollapsed = 1 << 0,
+
+ /* Keep ENUM_OPERATORS() below updated! */
+ };
+ using ItemIterFn = FunctionRef<void(AbstractTreeViewItem &)>;
+
+ /**
+ * Convenience wrapper taking the arguments needed to construct an item of type \a ItemT. Calls
+ * the version just below.
+ */
+ template<class ItemT, typename... Args> ItemT &add_tree_item(Args &&...args)
+ {
+ static_assert(std::is_base_of<AbstractTreeViewItem, ItemT>::value,
+ "Type must derive from and implement the AbstractTreeViewItem interface");
+
+ return dynamic_cast<ItemT &>(
+ add_tree_item(std::make_unique<ItemT>(std::forward<Args>(args)...)));
+ }
+
+ AbstractTreeViewItem &add_tree_item(std::unique_ptr<AbstractTreeViewItem> item);
+
+ protected:
+ void foreach_item_recursive(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const;
+};
+
+ENUM_OPERATORS(TreeViewItemContainer::IterOptions,
+ TreeViewItemContainer::IterOptions::SkipCollapsed);
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Tree-View Builders
+ * \{ */
+
+class TreeViewBuilder {
+ uiBlock &block_;
+
+ public:
+ TreeViewBuilder(uiBlock &block);
+
+ void build_tree_view(AbstractTreeView &tree_view);
+};
+
+class TreeViewLayoutBuilder {
+ uiBlock &block_;
+
+ friend TreeViewBuilder;
+
+ public:
+ void build_row(AbstractTreeViewItem &item) const;
+ uiBlock &block() const;
+ uiLayout *current_layout() const;
+
+ private:
+ /* Created through #TreeViewBuilder. */
+ TreeViewLayoutBuilder(uiBlock &block);
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Tree-View Base Class
+ * \{ */
+
+class AbstractTreeView : public TreeViewItemContainer {
+ friend TreeViewBuilder;
+ friend TreeViewLayoutBuilder;
+
+ public:
+ virtual ~AbstractTreeView() = default;
+
+ void foreach_item(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const;
+
+ protected:
+ virtual void build_tree() = 0;
+
+ private:
+ /** Match the tree-view against an earlier version of itself (if any) and copy the old UI state
+ * (e.g. collapsed, active, selected) to the new one. See
+ * #AbstractTreeViewItem.update_from_old(). */
+ void update_from_old(uiBlock &new_block);
+ static void update_children_from_old_recursive(const TreeViewItemContainer &new_items,
+ const TreeViewItemContainer &old_items);
+ static AbstractTreeViewItem *find_matching_child(const AbstractTreeViewItem &lookup_item,
+ const TreeViewItemContainer &items);
+ void build_layout_from_tree(const TreeViewLayoutBuilder &builder);
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Tree-View Item Type
+ * \{ */
+
+/** \brief Abstract base class for defining a customizable tree-view item.
+ *
+ * The tree-view item defines how to build its data into a tree-row. There are implementations for
+ * common layouts, e.g. #BasicTreeViewItem.
+ * It also stores state information that needs to be persistent over redraws, like the collapsed
+ * state.
+ */
+class AbstractTreeViewItem : public TreeViewItemContainer {
+ friend class AbstractTreeView;
+
+ bool is_open_ = false;
+ bool is_active_ = false;
+
+ protected:
+ /** This label is used for identifying an item (together with its parent's labels). */
+ std::string label_{};
+
+ public:
+ virtual ~AbstractTreeViewItem() = default;
+
+ virtual void build_row(uiLayout &row) = 0;
+
+ virtual void on_activate();
+
+ /** 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 it update their state accordingly. */
+ virtual void update_from_old(AbstractTreeViewItem &old);
+
+ const AbstractTreeView &get_tree_view() const;
+ int count_parents() const;
+ void set_active(bool value = true);
+ bool is_active() const;
+ void toggle_collapsed();
+ bool is_collapsed() const;
+ void set_collapsed(bool collapsed);
+ bool is_collapsible() const;
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Predefined Tree-View Item Types
+ *
+ * Common, Basic Tree-View Item Types.
+ * \{ */
+
+/**
+ * The most basic type, just a label with an icon.
+ */
+class BasicTreeViewItem : public AbstractTreeViewItem {
+ public:
+ using ActivateFn = std::function<void(BasicTreeViewItem &new_active)>;
+ BIFIconID icon;
+
+ BasicTreeViewItem(StringRef label, BIFIconID icon = ICON_NONE, ActivateFn activate_fn = nullptr);
+
+ void build_row(uiLayout &row) override;
+ void on_activate() override;
+
+ protected:
+ /** Created in the #build() function. */
+ uiButTreeRow *tree_row_but_ = nullptr;
+ /** Optionally passed to the #BasicTreeViewItem constructor. Called when activating this tree
+ * view item. This way users don't have to sub-class #BasicTreeViewItem, just to implement
+ * custom activation behavior (a common thing to do). */
+ ActivateFn activate_fn_;
+
+ uiBut *button();
+ BIFIconID get_draw_icon() const;
+};
+
+/** \} */
+
+} // namespace blender::ui