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
117 lines
3.5 KiB
C++
117 lines
3.5 KiB
C++
/*
|
|
* 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 edinterface
|
|
*
|
|
* This part of the UI-View API is mostly needed to support persistent state of items within the
|
|
* view. Views are stored in #uiBlock's, and kept alive with it until after the next redraw. So we
|
|
* can compare the old view items with the new view items and keep state persistent for matching
|
|
* ones.
|
|
*/
|
|
|
|
#include <memory>
|
|
#include <variant>
|
|
|
|
#include "BLI_listbase.h"
|
|
|
|
#include "interface_intern.h"
|
|
|
|
#include "UI_interface.hh"
|
|
#include "UI_tree_view.hh"
|
|
|
|
using namespace blender;
|
|
using namespace blender::ui;
|
|
|
|
/**
|
|
* Wrapper to store views in a #ListBase. There's no `uiView` base class, we just store views as a
|
|
* #std::variant.
|
|
*/
|
|
struct ViewLink : public Link {
|
|
using TreeViewPtr = std::unique_ptr<AbstractTreeView>;
|
|
|
|
std::string idname;
|
|
/* Note: Can't use std::get() on this until minimum macOS deployment target is 10.14. */
|
|
std::variant<TreeViewPtr> view;
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Override this for all available tree types.
|
|
*/
|
|
AbstractTreeView *UI_block_add_view(uiBlock &block,
|
|
StringRef idname,
|
|
std::unique_ptr<AbstractTreeView> tree_view)
|
|
{
|
|
ViewLink *view_link = OBJECT_GUARDED_NEW(ViewLink);
|
|
BLI_addtail(&block.views, view_link);
|
|
|
|
view_link->view = std::move(tree_view);
|
|
view_link->idname = idname;
|
|
|
|
return get_view_from_link<AbstractTreeView>(*view_link);
|
|
}
|
|
|
|
void ui_block_free_views(uiBlock *block)
|
|
{
|
|
LISTBASE_FOREACH_MUTABLE (ViewLink *, link, &block->views) {
|
|
OBJECT_GUARDED_DELETE(link, ViewLink);
|
|
}
|
|
}
|
|
|
|
static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractTreeView &view)
|
|
{
|
|
/* First get the idname the of the view we're looking for. */
|
|
LISTBASE_FOREACH (ViewLink *, view_link, &block.views) {
|
|
if (get_view_from_link<AbstractTreeView>(*view_link) == &view) {
|
|
return view_link->idname;
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block,
|
|
const uiTreeViewHandle *new_view_handle)
|
|
{
|
|
const AbstractTreeView &needle_view = reinterpret_cast<const AbstractTreeView &>(
|
|
*new_view_handle);
|
|
|
|
uiBlock *old_block = new_block->oldblock;
|
|
if (!old_block) {
|
|
return nullptr;
|
|
}
|
|
|
|
StringRef idname = ui_block_view_find_idname(*new_block, needle_view);
|
|
if (idname.is_empty()) {
|
|
return nullptr;
|
|
}
|
|
|
|
LISTBASE_FOREACH (ViewLink *, old_view_link, &old_block->views) {
|
|
if (old_view_link->idname == idname) {
|
|
return reinterpret_cast<uiTreeViewHandle *>(
|
|
get_view_from_link<AbstractTreeView>(*old_view_link));
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|