Source/Interface/Views

= Views =

Views make it easy to build user interfaces for (possibly large) sets of data. Each unit of data can be displayed as an item in the view, and the items provide features like selection, activation, renaming, custom context menus or drag & drop controllers. Views are designed to be efficient and customizable.

There are multiple kinds of views available:
 * Tree-Views
 * Grid-Views

Sections below cover general aspects of views, the links above only contain the bits for that are specific to a view type.

Overview
Say we have a list of To-Do items, that we want to display as a field of "bubbles". For this, we use an imaginary bubble-view:



The graphic displays some basic aspects of the view system:
 * Data set -> View -> Layout A data set "drives" a view, which in return will output a layout of UI elements.
 * Inherit from a view base class A new view can be implemented by creating a class that inherits from a view base class of the wanted type (here: `MyTodoBubbleView` inherits from `ui::AbstractBubbleView`).
 * `ui::AbstractView` The root base class for all views to use.
 * `ui::AbstractViewItem` The root base class for all view items to use.

The white boxes of the view represent the the public parts of the view system. They provide a relatively simple interface behind which a lot of heavy lifting is done. A single view may actually hold items of different types, they just need to share the same view type specific base class. For example, say you want to display To-Do tasks that are done as well, but they should display a button to reopen the To-Do, show a different context menu, disable renaming, ... Rather than adding a bunch of `if`s in the item's code, there could be a separate `MyDoneTodoBubbleViewItem`, dedicated to just that.

The following sections will cover three main topics:
 * Creating views
 * View Reconstruction: Views are reconstructed on every redraw. State of items (selection, renaming, ect.) is preserved by comparing the reconstructed view to its earlier version from the last redraw, and copying the state of items from the previous to the new version.
 * Additional Features: Renaming, context menu building, selection/active binding, etc.

Creating a View
Views are built by first defining a number of items (for tree views a hierarchy even), then each visible item extends the layout.

How exactly the view is created depends a bit on the specific type, but it looks something like this:

Item types can be defined in a similar fashion:

There are some ready-to-use implementations for common/basic view item types. For example, for tree-views there is `ui::BasicTreeViewItem`, which just displays a label and an icon for each item in the tree, at the expected level of indentation. Similarly, for grid-views there is `ui::PreviewGridItem`, which to display a nice large preview image with a label below, like in the thumbnail mode of the File Browser.

Lastly, the actual instance of the bubble-view can be created for a `uiBlock`:

For a more complex real-life example, check the asset catalog tree-view code: https://developer.blender.org/diffusion/B/browse/master/source/blender/editors/space_file/asset_catalog_tree_view.cc

View Reconstruction
Like most UI components in Blender, views are reconstructed on every redraw. This makes it easy to always represent the latest state of data. So the UI doesn't have to be updated carefully as data changes, the UI is just recreated with the latest input data. An important task of the view API is reliable reconstruction of the views including their state (like which items are collapsed or selected) over redraws.

Most complexity is handled by the view system. But it's important to have an understanding of what's going on there.

The reconstruction is a two part process:
 * 1) Build the view Calls the view items build function (e.g. `ui::AbstractTreeView::build_tree` or `ui::AbstractGridView::build_items`) to create the individual view items for the current state of the data to represent.
 * 2) Reconstruct state First the view system attempts to recognize the view and all of its items from a previous redraw. This is done by looking up the view by name in `uiBlock.oldblock`, and then comparing each new item with the previous items. Items are compared using the `ui::AbstractViewItem::matches` function, which can be overridden if the default of the view type isn't enough to identify items reliably. If two items were identified as matching (meaning the view system thinks an item represents the same data as the matched item from the previous frame, i.e. it recognizes it), the state of the old item is copied to the new one using the `ui::AbstractViewItem::update_from_old`. If you want to implement a view with some custom state (say a `show_details` boolean to display more information in the item), this function has to be overridden so that your custom state is also copied to the new item. The base function should always be called.

Once both steps are completed `ui::AbstractView::is_reconstructed` will return `true`. Only then the final state of the view and the items is known. So only then can state be queried reliably and state changes be detected.

Note: Actually building the layout (e.g. placing the widgets for each item) is not considered part of the reconstruction.

Further Features
The following features are supported typically. Not all view types may support all of them (yet).

Custom Activation Behavior
An item type's `::on_activate` can be overriden and is executed whenever the item gets activated (note: activated, not selected). E.g. this could be used to load the details of a To-Do for display in a sidebar, when activating a bubble item.

To not have to create a sub-class of `ui::BasicTreeViewItem` just to customize its activation behavior, it offers a different way to set the custom behavior:

Context Menus
An item can build a context menu similar to how it builds its item's layout:

It's recommended to use `WM_menutype_find` and `UI_menutype_draw` to draw a context menu defined in Python. This makes it easy to edit the menu and allows add-ons to extend it.

Advanced Persistent State
When the UI is redrawn, the `::update_from_old` of each recognized item is called to copy state from the tem of the last redraw, to the matching one in this redraw. This allows persistent state, e.g. a tree-view item can stay collapsed or expanded over redraws that way. If a custom tree item contains own state it wants to keep persistent, it should override the function, call `ui::AbstractTreeViewItem::update_from_old` and then copy (or move) its custom state variables from `old` to itself. The same goes for other view types.

Drag Support
Some view items may support being dragged.

TODO: Document `ui::AbstractTreeViewItemDragController` design.

Drop Actions
View items may support responding to drop events. The important functions to override for this are `bool can_drop(const wmDrag &drag)` and `bool on_drop`. The latter can assume the former returns true when executed. In addition, `std::string drop_tooltip(...)` provides a way to construct a string that will be shown to the user, whenever something is dragged over this specific view item. It can also be implemented assuming `can_drop` returned true already.