UI Code Quality: Convert Outliner Blender File mode to new tree buiding design
See https://developer.blender.org/D9499. Also: * Add `space_outliner/tree/common.cc` for functions shared between display modes. * I had to add a cast to `ListBaseWrapper` to make it work with ID lists. * Cleanup: Remove internal `Tree` alias for `ListBase`. That was more confusing than helpful.
This commit is contained in:
parent
ad0c387fdf
commit
44d8fafd7f
|
@ -56,7 +56,8 @@ template<typename T> class ListBaseWrapper {
|
|||
|
||||
Iterator &operator++()
|
||||
{
|
||||
current_ = current_->next;
|
||||
/* Some types store next/prev using `void *`, so cast is necessary. */
|
||||
current_ = static_cast<T *>(current_->next);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,9 @@ set(SRC
|
|||
outliner_tree.c
|
||||
outliner_utils.c
|
||||
space_outliner.c
|
||||
tree/common.cc
|
||||
tree/tree_view.cc
|
||||
tree/tree_view_libraries.cc
|
||||
tree/tree_view_view_layer.cc
|
||||
|
||||
outliner_intern.h
|
||||
|
|
|
@ -1382,110 +1382,6 @@ static void outliner_add_seq_dup(SpaceOutliner *space_outliner,
|
|||
|
||||
/* ----------------------------------------------- */
|
||||
|
||||
static const char *outliner_idcode_to_plural(short idcode)
|
||||
{
|
||||
const char *propname = BKE_idtype_idcode_to_name_plural(idcode);
|
||||
PropertyRNA *prop = RNA_struct_type_find_property(&RNA_BlendData, propname);
|
||||
return (prop) ? RNA_property_ui_name(prop) : "UNKNOWN";
|
||||
}
|
||||
|
||||
static bool outliner_library_id_show(Library *lib, ID *id, short filter_id_type)
|
||||
{
|
||||
if (id->lib != lib) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filter_id_type == ID_GR) {
|
||||
/* Don't show child collections of non-scene master collection,
|
||||
* they are already shown as children. */
|
||||
Collection *collection = (Collection *)id;
|
||||
bool has_non_scene_parent = false;
|
||||
|
||||
LISTBASE_FOREACH (CollectionParent *, cparent, &collection->parents) {
|
||||
if (!(cparent->collection->flag & COLLECTION_IS_MASTER)) {
|
||||
has_non_scene_parent = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_non_scene_parent) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static TreeElement *outliner_add_library_contents(Main *mainvar,
|
||||
SpaceOutliner *space_outliner,
|
||||
ListBase *lb,
|
||||
Library *lib)
|
||||
{
|
||||
TreeElement *ten, *tenlib = NULL;
|
||||
ListBase *lbarray[MAX_LIBARRAY];
|
||||
int a, tot;
|
||||
short filter_id_type = (space_outliner->filter & SO_FILTER_ID_TYPE) ?
|
||||
space_outliner->filter_id_type :
|
||||
0;
|
||||
|
||||
if (filter_id_type) {
|
||||
lbarray[0] = which_libbase(mainvar, space_outliner->filter_id_type);
|
||||
tot = 1;
|
||||
}
|
||||
else {
|
||||
tot = set_listbasepointers(mainvar, lbarray);
|
||||
}
|
||||
|
||||
for (a = 0; a < tot; a++) {
|
||||
if (lbarray[a] && lbarray[a]->first) {
|
||||
ID *id = lbarray[a]->first;
|
||||
const bool is_library = (GS(id->name) == ID_LI) && (lib != NULL);
|
||||
|
||||
/* check if there's data in current lib */
|
||||
for (; id; id = id->next) {
|
||||
if (id->lib == lib) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* We always want to create an entry for libraries, even if/when we have no more IDs from
|
||||
* them. This invalid state is important to show to user as well.*/
|
||||
if (id != NULL || is_library) {
|
||||
if (!tenlib) {
|
||||
/* Create library tree element on demand, depending if there are any data-blocks. */
|
||||
if (lib) {
|
||||
tenlib = outliner_add_element(space_outliner, lb, lib, NULL, 0, 0);
|
||||
}
|
||||
else {
|
||||
tenlib = outliner_add_element(space_outliner, lb, mainvar, NULL, TSE_ID_BASE, 0);
|
||||
tenlib->name = IFACE_("Current File");
|
||||
}
|
||||
}
|
||||
|
||||
/* Create data-block list parent element on demand. */
|
||||
if (id != NULL) {
|
||||
if (filter_id_type) {
|
||||
ten = tenlib;
|
||||
}
|
||||
else {
|
||||
ten = outliner_add_element(
|
||||
space_outliner, &tenlib->subtree, lbarray[a], NULL, TSE_ID_BASE, 0);
|
||||
ten->directdata = lbarray[a];
|
||||
ten->name = outliner_idcode_to_plural(GS(id->name));
|
||||
}
|
||||
|
||||
for (id = lbarray[a]->first; id; id = id->next) {
|
||||
if (outliner_library_id_show(lib, id, filter_id_type)) {
|
||||
outliner_add_element(space_outliner, &ten->subtree, id, ten, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tenlib;
|
||||
}
|
||||
|
||||
static void outliner_add_orphaned_datablocks(Main *mainvar, SpaceOutliner *space_outliner)
|
||||
{
|
||||
TreeElement *ten;
|
||||
|
@ -2328,61 +2224,13 @@ void outliner_build_tree(Main *mainvar,
|
|||
&source_data);
|
||||
}
|
||||
|
||||
if (!BLI_listbase_is_empty(&space_outliner->tree)) {
|
||||
/* Skip. */
|
||||
if (space_outliner->runtime->tree_view) {
|
||||
/* Skip if there's a tree view that's responsible for adding all elements. */
|
||||
}
|
||||
/* options */
|
||||
else if (space_outliner->outlinevis == SO_LIBRARIES) {
|
||||
Library *lib;
|
||||
|
||||
/* current file first - mainvar provides tselem with unique pointer - not used */
|
||||
ten = outliner_add_library_contents(mainvar, space_outliner, &space_outliner->tree, NULL);
|
||||
if (ten) {
|
||||
tselem = TREESTORE(ten);
|
||||
if (!tselem->used) {
|
||||
tselem->flag &= ~TSE_CLOSED;
|
||||
}
|
||||
}
|
||||
|
||||
for (lib = mainvar->libraries.first; lib; lib = lib->id.next) {
|
||||
ten = outliner_add_library_contents(mainvar, space_outliner, &space_outliner->tree, lib);
|
||||
/* NULL-check matters, due to filtering there may not be a new element. */
|
||||
if (ten) {
|
||||
lib->id.newid = (ID *)ten;
|
||||
}
|
||||
}
|
||||
/* make hierarchy */
|
||||
ten = space_outliner->tree.first;
|
||||
if (ten != NULL) {
|
||||
ten = ten->next; /* first one is main */
|
||||
while (ten) {
|
||||
TreeElement *nten = ten->next, *par;
|
||||
tselem = TREESTORE(ten);
|
||||
lib = (Library *)tselem->id;
|
||||
if (lib && lib->parent) {
|
||||
par = (TreeElement *)lib->parent->id.newid;
|
||||
if (tselem->id->tag & LIB_TAG_INDIRECT) {
|
||||
/* Only remove from 'first level' if lib is not also directly used. */
|
||||
BLI_remlink(&space_outliner->tree, ten);
|
||||
BLI_addtail(&par->subtree, ten);
|
||||
ten->parent = par;
|
||||
}
|
||||
else {
|
||||
/* Else, make a new copy of the libtree for our parent. */
|
||||
TreeElement *dupten = outliner_add_library_contents(
|
||||
mainvar, space_outliner, &par->subtree, lib);
|
||||
if (dupten) {
|
||||
dupten->parent = par;
|
||||
}
|
||||
}
|
||||
}
|
||||
ten = nten;
|
||||
}
|
||||
}
|
||||
/* restore newid pointers */
|
||||
for (lib = mainvar->libraries.first; lib; lib = lib->id.next) {
|
||||
lib->id.newid = NULL;
|
||||
}
|
||||
/* Ported to new tree-view, should be built there already. */
|
||||
BLI_assert(false);
|
||||
}
|
||||
else if (space_outliner->outlinevis == SO_SCENES) {
|
||||
Scene *sce;
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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 spoutliner
|
||||
*
|
||||
* Functions and helpers shared between tree-view types or other tree related code.
|
||||
*/
|
||||
|
||||
#include "BKE_idtype.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
|
||||
#include "tree_view.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name ID Helpers.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
const char *outliner_idcode_to_plural(short idcode)
|
||||
{
|
||||
const char *propname = BKE_idtype_idcode_to_name_plural(idcode);
|
||||
PropertyRNA *prop = RNA_struct_type_find_property(&RNA_BlendData, propname);
|
||||
return (prop) ? RNA_property_ui_name(prop) : "UNKNOWN";
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -34,7 +34,10 @@ TreeView *outliner_tree_view_create(eSpaceOutliner_Mode mode, SpaceOutliner *spa
|
|||
|
||||
switch (mode) {
|
||||
case SO_SCENES:
|
||||
break;
|
||||
case SO_LIBRARIES:
|
||||
tree_view = new outliner::TreeViewLibraries(*space_outliner);
|
||||
break;
|
||||
case SO_SEQUENCE:
|
||||
case SO_DATA_API:
|
||||
case SO_ID_ORPHANS:
|
||||
|
|
|
@ -36,8 +36,6 @@ namespace blender {
|
|||
namespace ed {
|
||||
namespace outliner {
|
||||
|
||||
using Tree = ListBase;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Tree-View Interface */
|
||||
|
||||
|
@ -60,7 +58,7 @@ class AbstractTreeView {
|
|||
* Build a tree for this view with the Blender context data given in \a source_data and the view
|
||||
* settings in \a space_outliner.
|
||||
*/
|
||||
virtual Tree buildTree(const TreeSourceData &source_data) = 0;
|
||||
virtual ListBase buildTree(const TreeSourceData &source_data) = 0;
|
||||
|
||||
protected:
|
||||
/** All derived classes will need a handle to this, so storing it in the base for convenience. */
|
||||
|
@ -80,7 +78,7 @@ class TreeViewViewLayer final : public AbstractTreeView {
|
|||
public:
|
||||
TreeViewViewLayer(SpaceOutliner &space_outliner);
|
||||
|
||||
Tree buildTree(const TreeSourceData &source_data) override;
|
||||
ListBase buildTree(const TreeSourceData &source_data) override;
|
||||
|
||||
private:
|
||||
void add_view_layer(ListBase &, TreeElement &);
|
||||
|
@ -89,6 +87,21 @@ class TreeViewViewLayer final : public AbstractTreeView {
|
|||
void add_layer_collection_objects_children(TreeElement &);
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Library Tree-View */
|
||||
|
||||
/**
|
||||
* \brief Tree-View for the Libraries display mode.
|
||||
*/
|
||||
class TreeViewLibraries final : public AbstractTreeView {
|
||||
public:
|
||||
TreeViewLibraries(SpaceOutliner &space_outliner);
|
||||
|
||||
ListBase buildTree(const TreeSourceData &source_data) override;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace outliner
|
||||
} // namespace ed
|
||||
} // namespace blender
|
||||
|
@ -126,6 +139,8 @@ struct TreeElement *outliner_add_element(struct SpaceOutliner *space_outliner,
|
|||
short index);
|
||||
void outliner_make_object_parent_hierarchy(ListBase *lb);
|
||||
|
||||
const char *outliner_idcode_to_plural(short idcode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* 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 spoutliner
|
||||
*/
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_listbase_wrapper.hh"
|
||||
|
||||
#include "BKE_collection.h"
|
||||
#include "BKE_main.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "../outliner_intern.h"
|
||||
#include "tree_view.hh"
|
||||
|
||||
namespace blender {
|
||||
namespace ed {
|
||||
namespace outliner {
|
||||
|
||||
/* Convenience/readability. */
|
||||
template<typename T> using List = ListBaseWrapper<T>;
|
||||
|
||||
TreeViewLibraries::TreeViewLibraries(SpaceOutliner &space_outliner)
|
||||
: AbstractTreeView(space_outliner)
|
||||
{
|
||||
}
|
||||
|
||||
static bool outliner_library_id_show(Library *lib, ID *id, short filter_id_type)
|
||||
{
|
||||
if (id->lib != lib) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filter_id_type == ID_GR) {
|
||||
/* Don't show child collections of non-scene master collection,
|
||||
* they are already shown as children. */
|
||||
Collection *collection = (Collection *)id;
|
||||
bool has_non_scene_parent = false;
|
||||
|
||||
for (CollectionParent *cparent : List<CollectionParent>(collection->parents)) {
|
||||
if (!(cparent->collection->flag & COLLECTION_IS_MASTER)) {
|
||||
has_non_scene_parent = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_non_scene_parent) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static TreeElement *outliner_add_library_contents(Main *mainvar,
|
||||
SpaceOutliner *space_outliner,
|
||||
ListBase *lb,
|
||||
Library *lib)
|
||||
{
|
||||
TreeElement *ten, *tenlib = nullptr;
|
||||
ListBase *lbarray[MAX_LIBARRAY];
|
||||
int a, tot;
|
||||
short filter_id_type = (space_outliner->filter & SO_FILTER_ID_TYPE) ?
|
||||
space_outliner->filter_id_type :
|
||||
0;
|
||||
|
||||
if (filter_id_type) {
|
||||
lbarray[0] = which_libbase(mainvar, space_outliner->filter_id_type);
|
||||
tot = 1;
|
||||
}
|
||||
else {
|
||||
tot = set_listbasepointers(mainvar, lbarray);
|
||||
}
|
||||
|
||||
for (a = 0; a < tot; a++) {
|
||||
if (lbarray[a] && lbarray[a]->first) {
|
||||
ID *id = static_cast<ID *>(lbarray[a]->first);
|
||||
const bool is_library = (GS(id->name) == ID_LI) && (lib != nullptr);
|
||||
|
||||
/* check if there's data in current lib */
|
||||
for (ID *id_iter : List<ID>(lbarray[a])) {
|
||||
if (id_iter->lib == lib) {
|
||||
id = id_iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* We always want to create an entry for libraries, even if/when we have no more IDs from
|
||||
* them. This invalid state is important to show to user as well.*/
|
||||
if (id != nullptr || is_library) {
|
||||
if (!tenlib) {
|
||||
/* Create library tree element on demand, depending if there are any data-blocks. */
|
||||
if (lib) {
|
||||
tenlib = outliner_add_element(space_outliner, lb, lib, nullptr, 0, 0);
|
||||
}
|
||||
else {
|
||||
tenlib = outliner_add_element(space_outliner, lb, mainvar, nullptr, TSE_ID_BASE, 0);
|
||||
tenlib->name = IFACE_("Current File");
|
||||
}
|
||||
}
|
||||
|
||||
/* Create data-block list parent element on demand. */
|
||||
if (id != nullptr) {
|
||||
if (filter_id_type) {
|
||||
ten = tenlib;
|
||||
}
|
||||
else {
|
||||
ten = outliner_add_element(
|
||||
space_outliner, &tenlib->subtree, lbarray[a], nullptr, TSE_ID_BASE, 0);
|
||||
ten->directdata = lbarray[a];
|
||||
ten->name = outliner_idcode_to_plural(GS(id->name));
|
||||
}
|
||||
|
||||
for (ID *id : List<ID>(lbarray[a])) {
|
||||
if (outliner_library_id_show(lib, id, filter_id_type)) {
|
||||
outliner_add_element(space_outliner, &ten->subtree, id, ten, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tenlib;
|
||||
}
|
||||
|
||||
ListBase TreeViewLibraries::buildTree(const TreeSourceData &source_data)
|
||||
{
|
||||
ListBase tree = {nullptr};
|
||||
|
||||
/* current file first - mainvar provides tselem with unique pointer - not used */
|
||||
TreeElement *ten = outliner_add_library_contents(
|
||||
source_data.bmain, &_space_outliner, &tree, nullptr);
|
||||
TreeStoreElem *tselem;
|
||||
|
||||
if (ten) {
|
||||
tselem = TREESTORE(ten);
|
||||
if (!tselem->used) {
|
||||
tselem->flag &= ~TSE_CLOSED;
|
||||
}
|
||||
}
|
||||
|
||||
for (ID *id : List<ID>(source_data.bmain->libraries)) {
|
||||
Library *lib = reinterpret_cast<Library *>(id);
|
||||
ten = outliner_add_library_contents(source_data.bmain, &_space_outliner, &tree, lib);
|
||||
/* NULL-check matters, due to filtering there may not be a new element. */
|
||||
if (ten) {
|
||||
lib->id.newid = (ID *)ten;
|
||||
}
|
||||
}
|
||||
/* make hierarchy */
|
||||
ten = static_cast<TreeElement *>(tree.first);
|
||||
if (ten != nullptr) {
|
||||
ten = ten->next; /* first one is main */
|
||||
while (ten) {
|
||||
TreeElement *nten = ten->next, *par;
|
||||
tselem = TREESTORE(ten);
|
||||
Library *lib = (Library *)tselem->id;
|
||||
if (lib && lib->parent) {
|
||||
par = (TreeElement *)lib->parent->id.newid;
|
||||
if (tselem->id->tag & LIB_TAG_INDIRECT) {
|
||||
/* Only remove from 'first level' if lib is not also directly used. */
|
||||
BLI_remlink(&tree, ten);
|
||||
BLI_addtail(&par->subtree, ten);
|
||||
ten->parent = par;
|
||||
}
|
||||
else {
|
||||
/* Else, make a new copy of the libtree for our parent. */
|
||||
TreeElement *dupten = outliner_add_library_contents(
|
||||
source_data.bmain, &_space_outliner, &par->subtree, lib);
|
||||
if (dupten) {
|
||||
dupten->parent = par;
|
||||
}
|
||||
}
|
||||
}
|
||||
ten = nten;
|
||||
}
|
||||
}
|
||||
/* restore newid pointers */
|
||||
for (ID *library_id : List<ID>(source_data.bmain->libraries)) {
|
||||
library_id->newid = nullptr;
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
} // namespace outliner
|
||||
} // namespace ed
|
||||
} // namespace blender
|
|
@ -69,9 +69,9 @@ TreeViewViewLayer::TreeViewViewLayer(SpaceOutliner &space_outliner)
|
|||
{
|
||||
}
|
||||
|
||||
Tree TreeViewViewLayer::buildTree(const TreeSourceData &source_data)
|
||||
ListBase TreeViewViewLayer::buildTree(const TreeSourceData &source_data)
|
||||
{
|
||||
Tree tree = {nullptr};
|
||||
ListBase tree = {nullptr};
|
||||
|
||||
_view_layer = source_data.view_layer;
|
||||
_show_objects = !(_space_outliner.filter & SO_FILTER_NO_OBJECT);
|
||||
|
|
Loading…
Reference in New Issue