From 44d8fafd7f0d4f17f1e4c79ec5f9b6154d9bd57c Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Sat, 7 Nov 2020 20:13:37 +0100 Subject: 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. --- .../blender/editors/space_outliner/CMakeLists.txt | 2 + .../blender/editors/space_outliner/outliner_tree.c | 160 +--------------- .../blender/editors/space_outliner/tree/common.cc | 41 +++++ .../editors/space_outliner/tree/tree_view.cc | 3 + .../editors/space_outliner/tree/tree_view.hh | 23 ++- .../space_outliner/tree/tree_view_libraries.cc | 204 +++++++++++++++++++++ .../space_outliner/tree/tree_view_view_layer.cc | 4 +- 7 files changed, 275 insertions(+), 162 deletions(-) create mode 100644 source/blender/editors/space_outliner/tree/common.cc create mode 100644 source/blender/editors/space_outliner/tree/tree_view_libraries.cc (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index a0577b1103d..bbcd5a32ad6 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -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 diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 991d831d6e0..67616db6b3a 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -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; diff --git a/source/blender/editors/space_outliner/tree/common.cc b/source/blender/editors/space_outliner/tree/common.cc new file mode 100644 index 00000000000..56b5d62195e --- /dev/null +++ b/source/blender/editors/space_outliner/tree/common.cc @@ -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"; +} + +/** \} */ diff --git a/source/blender/editors/space_outliner/tree/tree_view.cc b/source/blender/editors/space_outliner/tree/tree_view.cc index 8f8e6c606be..f6c3da012f4 100644 --- a/source/blender/editors/space_outliner/tree/tree_view.cc +++ b/source/blender/editors/space_outliner/tree/tree_view.cc @@ -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: diff --git a/source/blender/editors/space_outliner/tree/tree_view.hh b/source/blender/editors/space_outliner/tree/tree_view.hh index a4ce0ce6e78..cb3bb4681c8 100644 --- a/source/blender/editors/space_outliner/tree/tree_view.hh +++ b/source/blender/editors/space_outliner/tree/tree_view.hh @@ -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 diff --git a/source/blender/editors/space_outliner/tree/tree_view_libraries.cc b/source/blender/editors/space_outliner/tree/tree_view_libraries.cc new file mode 100644 index 00000000000..83f4b93df22 --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_view_libraries.cc @@ -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 using List = ListBaseWrapper; + +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(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(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(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(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(source_data.bmain->libraries)) { + Library *lib = reinterpret_cast(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(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(source_data.bmain->libraries)) { + library_id->newid = nullptr; + } + + return tree; +} + +} // namespace outliner +} // namespace ed +} // namespace blender diff --git a/source/blender/editors/space_outliner/tree/tree_view_view_layer.cc b/source/blender/editors/space_outliner/tree/tree_view_view_layer.cc index e13fe85f5b0..abfce72d0fb 100644 --- a/source/blender/editors/space_outliner/tree/tree_view_view_layer.cc +++ b/source/blender/editors/space_outliner/tree/tree_view_view_layer.cc @@ -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); -- cgit v1.2.3