UI: Add AbstractView base class for views, unify reconstruction in there

No user visible changes expected.

There's plenty of duplicated code in the grid and the tree view, and I expect
this to become more. This starts the process of unifying these parts, which
should also make it easier to add new views. Complexity in the view classes is
reduced, and some type shenanigans for C compatibility and general view
management can be removed, since there is now a common base type.

For the start this ports some of the view reconstruction, where the view and
its items are compared to the version of itself in the previous redraw, so that
state (highlighted, active, renaming, collapsed, ...) can be preserved.
Notifier listening is also ported.
This commit is contained in:
2022-07-02 21:49:21 +02:00
parent 4ffee9a48d
commit c355be6fae
11 changed files with 146 additions and 184 deletions

View File

@@ -25,6 +25,7 @@ set(INC
)
set(SRC
abstract_view.cc
grid_view.cc
interface.cc
interface_align.c

View File

@@ -0,0 +1,52 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edinterface
*/
#include "interface_intern.h"
#include "UI_abstract_view.hh"
namespace blender::ui {
/* ---------------------------------------------------------------------- */
/** \name View Reconstruction
* \{ */
bool AbstractView::is_reconstructed() const
{
return is_reconstructed_;
}
void AbstractView::update_from_old(uiBlock &new_block)
{
uiBlock *old_block = new_block.oldblock;
if (!old_block) {
is_reconstructed_ = true;
return;
}
const uiViewHandle *old_view_handle = ui_block_view_find_matching_in_old_block(
&new_block, reinterpret_cast<uiViewHandle *>(this));
if (old_view_handle == nullptr) {
/* Initial construction, nothing to update. */
is_reconstructed_ = true;
return;
}
update_children_from_old(reinterpret_cast<const AbstractView &>(*old_view_handle));
/* Finished (re-)constructing the tree. */
is_reconstructed_ = true;
}
/** \} */
bool AbstractView::listen(const wmNotifier & /*notifier*/) const
{
/* Nothing by default. */
return false;
}
} // namespace blender::ui

View File

@@ -43,12 +43,6 @@ void AbstractGridView::foreach_item(ItemIterFn iter_fn) const
}
}
bool AbstractGridView::listen(const wmNotifier & /*notifier*/) const
{
/* Nothing by default. */
return false;
}
AbstractGridViewItem *AbstractGridView::find_matching_item(
const AbstractGridViewItem &item_to_match, const AbstractGridView &view_to_search_in) const
{
@@ -67,34 +61,18 @@ void AbstractGridView::change_state_delayed()
foreach_item([](AbstractGridViewItem &item) { item.change_state_delayed(); });
}
void AbstractGridView::update_from_old(uiBlock &new_block)
void AbstractGridView::update_children_from_old(const AbstractView &old_view)
{
uiGridViewHandle *old_view_handle = ui_block_grid_view_find_matching_in_old_block(
&new_block, reinterpret_cast<uiGridViewHandle *>(this));
if (!old_view_handle) {
/* Initial construction, nothing to update. */
is_reconstructed_ = true;
return;
}
const AbstractGridView &old_grid_view = dynamic_cast<const AbstractGridView &>(old_view);
AbstractGridView &old_view = reinterpret_cast<AbstractGridView &>(*old_view_handle);
foreach_item([this, &old_view](AbstractGridViewItem &new_item) {
const AbstractGridViewItem *matching_old_item = find_matching_item(new_item, old_view);
foreach_item([this, &old_grid_view](AbstractGridViewItem &new_item) {
const AbstractGridViewItem *matching_old_item = find_matching_item(new_item, old_grid_view);
if (!matching_old_item) {
return;
}
new_item.update_from_old(*matching_old_item);
});
/* Finished (re-)constructing the tree. */
is_reconstructed_ = true;
}
bool AbstractGridView::is_reconstructed() const
{
return is_reconstructed_;
}
const GridViewStyle &AbstractGridView::get_style() const
@@ -509,13 +487,6 @@ bool UI_grid_view_item_is_active(const uiGridViewItemHandle *item_handle)
return item.is_active();
}
bool UI_grid_view_listen_should_redraw(const uiGridViewHandle *view_handle,
const wmNotifier *notifier)
{
const AbstractGridView &view = *reinterpret_cast<const AbstractGridView *>(view_handle);
return view.listen(*notifier);
}
bool UI_grid_view_item_matches(const uiGridViewItemHandle *a_handle,
const uiGridViewItemHandle *b_handle)
{

View File

@@ -1543,10 +1543,9 @@ void ui_interface_tag_script_reload_queries(void);
/* interface_view.cc */
void ui_block_free_views(struct uiBlock *block);
uiTreeViewHandle *ui_block_tree_view_find_matching_in_old_block(const uiBlock *new_block,
const uiTreeViewHandle *new_view);
uiGridViewHandle *ui_block_grid_view_find_matching_in_old_block(
const uiBlock *new_block, const uiGridViewHandle *new_view_handle);
uiViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block,
const uiViewHandle *new_view);
uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block,
const uiTreeViewItemHandle *new_item_handle);

View File

@@ -25,6 +25,7 @@
#include "UI_interface.hh"
#include "UI_abstract_view.hh"
#include "UI_grid_view.hh"
#include "UI_tree_view.hh"
@@ -36,43 +37,27 @@ using namespace blender::ui;
* #std::variant.
*/
struct ViewLink : public Link {
using TreeViewPtr = std::unique_ptr<AbstractTreeView>;
using GridViewPtr = std::unique_ptr<AbstractGridView>;
std::string idname;
/* NOTE: Can't use std::get() on this until minimum macOS deployment target is 10.14. */
std::variant<TreeViewPtr, GridViewPtr> view;
std::unique_ptr<AbstractView> view;
};
template<class T> constexpr void check_if_valid_view_type()
{
static_assert(std::is_same_v<T, AbstractTreeView> || std::is_same_v<T, AbstractGridView>,
"Unsupported view type");
}
template<class T> T *get_view_from_link(ViewLink &link)
{
auto *t_uptr = std::get_if<std::unique_ptr<T>>(&link.view);
return t_uptr ? t_uptr->get() : nullptr;
}
template<class T>
static T *ui_block_add_view_impl(uiBlock &block, StringRef idname, std::unique_ptr<T> view)
static T *ui_block_add_view_impl(uiBlock &block,
StringRef idname,
std::unique_ptr<AbstractView> view)
{
check_if_valid_view_type<T>();
ViewLink *view_link = MEM_new<ViewLink>(__func__);
BLI_addtail(&block.views, view_link);
view_link->view = std::move(view);
view_link->idname = idname;
return get_view_from_link<T>(*view_link);
return dynamic_cast<T *>(view_link->view.get());
}
AbstractGridView *UI_block_add_view(uiBlock &block,
StringRef idname,
std::unique_ptr<AbstractGridView> tree_view)
std::unique_ptr<AbstractView> tree_view)
{
return ui_block_add_view_impl<AbstractGridView>(block, idname, std::move(tree_view));
}
@@ -96,17 +81,8 @@ void UI_block_views_listen(const uiBlock *block, const wmRegionListenerParams *l
ARegion *region = listener_params->region;
LISTBASE_FOREACH (ViewLink *, view_link, &block->views) {
if (AbstractGridView *grid_view = get_view_from_link<AbstractGridView>(*view_link)) {
if (UI_grid_view_listen_should_redraw(reinterpret_cast<uiGridViewHandle *>(grid_view),
listener_params->notifier)) {
ED_region_tag_redraw(region);
}
}
else if (AbstractTreeView *tree_view = get_view_from_link<AbstractTreeView>(*view_link)) {
if (UI_tree_view_listen_should_redraw(reinterpret_cast<uiTreeViewHandle *>(tree_view),
listener_params->notifier)) {
ED_region_tag_redraw(region);
}
if (view_link->view->listen(*listener_params->notifier)) {
ED_region_tag_redraw(region);
}
}
}
@@ -131,13 +107,11 @@ uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const ARegion *region)
return tree_row_but->tree_item;
}
template<class T> static StringRef ui_block_view_find_idname(const uiBlock &block, const T &view)
static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractView &view)
{
check_if_valid_view_type<T>();
/* First get the idname the of the view we're looking for. */
LISTBASE_FOREACH (ViewLink *, view_link, &block.views) {
if (get_view_from_link<T>(*view_link) == &view) {
if (view_link->view.get() == &view) {
return view_link->idname;
}
}
@@ -146,10 +120,9 @@ template<class T> static StringRef ui_block_view_find_idname(const uiBlock &bloc
}
template<class T>
static T *ui_block_view_find_matching_in_old_block(const uiBlock &new_block, const T &new_view)
static T *ui_block_view_find_matching_in_old_block_impl(const uiBlock &new_block,
const T &new_view)
{
check_if_valid_view_type<T>();
uiBlock *old_block = new_block.oldblock;
if (!old_block) {
return nullptr;
@@ -162,31 +135,21 @@ static T *ui_block_view_find_matching_in_old_block(const uiBlock &new_block, con
LISTBASE_FOREACH (ViewLink *, old_view_link, &old_block->views) {
if (old_view_link->idname == idname) {
return get_view_from_link<T>(*old_view_link);
return dynamic_cast<T *>(old_view_link->view.get());
}
}
return nullptr;
}
uiTreeViewHandle *ui_block_tree_view_find_matching_in_old_block(
const uiBlock *new_block, const uiTreeViewHandle *new_view_handle)
uiViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block,
const uiViewHandle *new_view_handle)
{
BLI_assert(new_block && new_view_handle);
const AbstractTreeView &new_view = reinterpret_cast<const AbstractTreeView &>(*new_view_handle);
const AbstractView &new_view = reinterpret_cast<const AbstractView &>(*new_view_handle);
AbstractTreeView *old_view = ui_block_view_find_matching_in_old_block(*new_block, new_view);
return reinterpret_cast<uiTreeViewHandle *>(old_view);
}
uiGridViewHandle *ui_block_grid_view_find_matching_in_old_block(
const uiBlock *new_block, const uiGridViewHandle *new_view_handle)
{
BLI_assert(new_block && new_view_handle);
const AbstractGridView &new_view = reinterpret_cast<const AbstractGridView &>(*new_view_handle);
AbstractGridView *old_view = ui_block_view_find_matching_in_old_block(*new_block, new_view);
return reinterpret_cast<uiGridViewHandle *>(old_view);
AbstractView *old_view = ui_block_view_find_matching_in_old_block_impl(*new_block, new_view);
return reinterpret_cast<uiViewHandle *>(old_view);
}
uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block,
@@ -199,9 +162,9 @@ uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block,
const AbstractTreeViewItem &new_item = *reinterpret_cast<const AbstractTreeViewItem *>(
new_item_handle);
const AbstractTreeView *old_tree_view = ui_block_view_find_matching_in_old_block(
const AbstractView *old_view = ui_block_view_find_matching_in_old_block_impl(
*new_block, new_item.get_tree_view());
if (!old_tree_view) {
if (!old_view) {
return nullptr;
}
@@ -216,7 +179,7 @@ uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block,
AbstractTreeViewItem &old_item = *reinterpret_cast<AbstractTreeViewItem *>(
old_treerow_but->tree_item);
/* Check if the row is from the expected tree-view. */
if (&old_item.get_tree_view() != old_tree_view) {
if (&old_item.get_tree_view() != old_view) {
continue;
}

View File

@@ -68,45 +68,25 @@ void AbstractTreeView::foreach_item(ItemIterFn iter_fn, IterOptions options) con
foreach_item_recursive(iter_fn, options);
}
bool AbstractTreeView::listen(const wmNotifier & /*notifier*/) const
{
/* Nothing by default. */
return false;
}
bool AbstractTreeView::is_renaming() const
{
return rename_buffer_ != nullptr;
}
void AbstractTreeView::update_from_old(uiBlock &new_block)
void AbstractTreeView::update_children_from_old(const AbstractView &old_view)
{
uiBlock *old_block = new_block.oldblock;
if (!old_block) {
/* Initial construction, nothing to update. */
is_reconstructed_ = true;
return;
}
uiTreeViewHandle *old_view_handle = ui_block_tree_view_find_matching_in_old_block(
&new_block, reinterpret_cast<uiTreeViewHandle *>(this));
if (old_view_handle == nullptr) {
is_reconstructed_ = true;
return;
}
AbstractTreeView &old_view = reinterpret_cast<AbstractTreeView &>(*old_view_handle);
/* TODO: Get rid of const cast. */
AbstractTreeView &old_tree_view = const_cast<AbstractTreeView &>(
dynamic_cast<const AbstractTreeView &>(old_view));
/* TODO: Move to AbstractView. */
/* Update own persistent data. */
/* Keep the rename buffer persistent while renaming! The rename button uses the buffer's
* pointer to identify itself over redraws. */
rename_buffer_ = std::move(old_view.rename_buffer_);
old_view.rename_buffer_ = nullptr;
rename_buffer_ = std::move(old_tree_view.rename_buffer_);
old_tree_view.rename_buffer_ = nullptr;
update_children_from_old_recursive(*this, old_view);
/* Finished (re-)constructing the tree. */
is_reconstructed_ = true;
update_children_from_old_recursive(*this, old_tree_view);
}
void AbstractTreeView::update_children_from_old_recursive(const TreeViewOrItem &new_items,
@@ -138,11 +118,6 @@ AbstractTreeViewItem *AbstractTreeView::find_matching_child(
return nullptr;
}
bool AbstractTreeView::is_reconstructed() const
{
return is_reconstructed_;
}
void AbstractTreeView::change_state_delayed()
{
BLI_assert_msg(
@@ -811,13 +786,6 @@ class TreeViewItemAPIWrapper {
using namespace blender::ui;
bool UI_tree_view_listen_should_redraw(const uiTreeViewHandle *view_handle,
const wmNotifier *notifier)
{
const AbstractTreeView &view = *reinterpret_cast<const AbstractTreeView *>(view_handle);
return view.listen(*notifier);
}
bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item_handle)
{
const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle);