Page MenuHome

UI: Tree-View API for easy creation of tree UIs
ClosedPublic

Authored by Julian Eisel (Severin) on Sep 20 2021, 8:57 PM.

Details

Summary

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.

Diff Detail

Repository
rB Blender
Branch
ui-tree-view-api (branched from master)
Build Status
Buildable 17171
Build 17171: arc lint + arc unit

Event Timeline

Julian Eisel (Severin) requested review of this revision.Sep 20 2021, 8:57 PM
Julian Eisel (Severin) created this revision.
Julian Eisel (Severin) edited the summary of this revision. (Show Details)Sep 20 2021, 8:58 PM
Julian Eisel (Severin) edited the summary of this revision. (Show Details)
Julian Eisel (Severin) edited the summary of this revision. (Show Details)Sep 20 2021, 9:01 PM
Julian Eisel (Severin) edited the summary of this revision. (Show Details)
Julian Eisel (Severin) edited the summary of this revision. (Show Details)
Julian Eisel (Severin) edited the summary of this revision. (Show Details)Sep 20 2021, 9:05 PM

This looks great! One thing I wonder is whether we really need the ui prefix on types that are in the UI namespace. It works in C since we don't have namespaces, but it doesn't seem necessary in C++ and just ends up as clutter.

Julian Eisel (Severin) edited the summary of this revision. (Show Details)Sep 20 2021, 9:20 PM

This looks great! One thing I wonder is whether we really need the ui prefix on types that are in the UI namespace. It works in C since we don't have namespaces, but it doesn't seem necessary in C++ and just ends up as clutter.

Yeah, we could remove it. For some reason I just find it aesthetically pleasing to have it there though :) But it's also nice to know when exactly you deal with UI types, not with data types. But we could also do using namespace blender; and then use ui::AbstractTreeView instead.

I do think ui::AbstractTreeView makes more sense if the "ui" at the beginning is helpful. I can see why the context is helpful, I just think the namespace and type names shouldn't be redundant.

source/blender/editors/include/UI_interface.hh
34

If this is only C++ code, maybe it makes sense to take the block by reference?

source/blender/editors/include/UI_tree_view.hh
148

Maybe add a note to see the comment in uiAbstractTreeViewItem?

222

Any particular reason to use a different case besides snake case here? Strikes me as a bit out of place.

228

What's the idea behind storing this as a function rather than using a virtual function? Maybe I'm missing something? Or the comment could mention why.

source/blender/editors/interface/tree_view.cc
265

despite -> besides

Julian Eisel (Severin) marked 5 inline comments as done.
Julian Eisel (Severin) edited the summary of this revision. (Show Details)
  • Address review points
  • Cleanup: Remove ui prefix from types (already has blender::ui namespace)
source/blender/editors/include/UI_tree_view.hh
222

No :)

228

The uiBasicTreeViewItem constructor takes this optionally, so you can pass an activate handler without having to sub-class uiBasicTreeViewItem just for this. That works quite well for the asset catalogs: https://developer.blender.org/diffusion/B/browse/temp-asset-browser-catalogs-ui/source/blender/editors/space_file/asset_catalog_tree_view.cc$110-113

Note uiAbstractTreeViewItem has a virtual void onActivate(); for this, which is how uiBasicTreeViewItem calls the function.

Julian Eisel (Severin) edited the summary of this revision. (Show Details)Sep 20 2021, 10:33 PM

Well done Julian, this looks good from my point of view.

source/blender/editors/include/UI_tree_view.hh
228

Thanks for the explanation, that makes sense, works nicely with lambdas doesn't it :)

229

just do implement -> just to implement

This revision is now accepted and ready to land.Sep 20 2021, 10:40 PM
Julian Eisel (Severin) edited the summary of this revision. (Show Details)Sep 20 2021, 11:15 PM
  • Cleanup: Corrections to comments

Seems fine, only noted very minor issues inline.

source/blender/editors/include/UI_tree_view.hh
164

Malformed doxy comment.

source/blender/editors/interface/interface_intern.h
499

contains? / stores?