This follows three main targets:
- Make creation of new tree-view 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.
I expect plenty of things to change still. But I want to get the overall idea approved before doing more work in master.
How you use it:
Defining a new tree can be done as simple as this:
class MyTreeView : ui::AbstractTreeView { void build_tree() override { ui::BasicTreeViewItem& item = add_tree_item<ui::BasicTreeViewItem>(IFACE_("All"), ICON_HOME); /* Child item. */ item.add_tree_item<ui::BasicTreeViewItem>(IFACE_("Child"), ICON_CON_CHILDOF); add_tree_item<ui::BasicTreeViewItem>(IFACE_("Unassigned"), ICON_FILE_HIDDEN); } };
This uses the ui::BasicTreeViewItem class to create, well basic tree items :) If you need more control over what goes into a row, you can define your own item type in a similar fashion:
class MyTreeViewItem : ui::AbstractTreeViewItem { void build_row(uiLayout& row) override { /* ... UI code ... :) */ } };
Different item types can be mixed in a tree.
Lasty, to actually create an instance of the tree-view and add it to a UI block:
ui::AbstractTreeView *tree_view = UI_block_add_view( block, "My tree View", std::make_unique<MyTreeView>()); ui::TreeViewBuilder builder(*block); builder.build_tree_view(*tree_view);
For a more real-life example, check the WIP asset-catalog tree-view code: https://developer.blender.org/diffusion/B/browse/temp-asset-browser-catalogs-ui/source/blender/editors/space_file/asset_catalog_tree_view.cc
Design Notes
- Tree-views are recreated on every redraw, like other UIs. That makes it easy to always represent the latest state of the data.
- Tree-view base-classes (AbstractTreeView and AbstractTreeViewItem) contain the logic needed to keep state persistent over redraws (e.g. active item, open/collapsed items). They do this by comparing themselves to their previous version.
- Tree-view construction and layout definition are separate phases:
- Firstly, we create a tree as a representation of the data. This will be recreated on redraws but can be matched against previews redraws to keep persistent state. Further processing, like filtering or lazy caching could be done here.
- Secondly, the layouts for the visible items (skipping collapsed sub-trees) are created.
- Idea is to support other views than tree-views (e.g. list-views to replace/rewrite UIList, table-views, grid-views for File/Asset Browser like display with big thumbnails, etc). These would probably share plenty of design ideas.
- A new Tree-row button type is introduced, to be placed underneath the row layout of the list-row.
Open To-do: File IO
The tree-views can currently not be written to files. I don't think that's hard to do, it's just not prioritized right now. My idea is to have dedicated DNA structs for this, so not the same data-structures we use at runtime. In a region we could store a tree-view using an identifier, and then the items with their state. On file reading this data is read to reconstruct the state of the tree-view and its items as good as possible.
