From e1ae5bd45fd0a8c0073039fa3b46835fe20f530e Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 7 Apr 2021 15:58:34 +0200 Subject: LibOverride: Add a dedicated view in the Outliner. This is a minimal, information-only view currently, listing by default all the override data-blocks, with their user-edited override properties. System-generated overrides (like the overrides of pointers to other override data-blocks) can be shown through a filter option. Finally, potential info or warning messages from (auto-)resync propcess are also shown, as an icon + tooltip next to the affected items. Part of D10855. --- .../blender/editors/space_outliner/CMakeLists.txt | 1 + .../blender/editors/space_outliner/outliner_draw.c | 100 +++++++++- .../editors/space_outliner/outliner_intern.h | 2 +- .../blender/editors/space_outliner/outliner_sync.c | 7 +- .../blender/editors/space_outliner/outliner_tree.c | 5 + .../editors/space_outliner/outliner_utils.c | 1 + .../editors/space_outliner/space_outliner.c | 2 +- .../editors/space_outliner/tree/tree_display.cc | 3 + .../editors/space_outliner/tree/tree_display.hh | 18 ++ .../tree/tree_display_override_library.cc | 204 +++++++++++++++++++++ .../space_outliner/tree/tree_element_overrides.cc | 41 ++++- .../space_outliner/tree/tree_element_overrides.hh | 1 + 12 files changed, 371 insertions(+), 14 deletions(-) create mode 100644 source/blender/editors/space_outliner/tree/tree_display_override_library.cc (limited to 'source/blender/editors/space_outliner') diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index 4a1e5c1a12c..c31239f0e9c 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -51,6 +51,7 @@ set(SRC tree/tree_display_data.cc tree/tree_display_libraries.cc tree/tree_display_orphaned.cc + tree/tree_display_override_library.cc tree/tree_display_scenes.cc tree/tree_display_sequencer.cc tree/tree_display_view_layer.cc diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 0916e106abf..99bc23ad246 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -1775,6 +1775,88 @@ static void outliner_draw_userbuts(uiBlock *block, } } +static bool outliner_draw_overrides_buts(uiBlock *block, + ARegion *region, + SpaceOutliner *space_outliner, + ListBase *lb, + const bool is_open) +{ + bool any_item_has_warnings = false; + + LISTBASE_FOREACH (TreeElement *, te, lb) { + bool item_has_warnings = false; + const bool do_draw = (te->ys + 2 * UI_UNIT_Y >= region->v2d.cur.ymin && + te->ys <= region->v2d.cur.ymax); + int but_flag = UI_BUT_DRAG_LOCK; + const char *tip = NULL; + + TreeStoreElem *tselem = TREESTORE(te); + switch (tselem->type) { + case TSE_LIBRARY_OVERRIDE_BASE: { + ID *id = tselem->id; + + if (ID_IS_OVERRIDE_LIBRARY_REAL(id) && ID_REAL_USERS(id) == 0) { + item_has_warnings = true; + if (do_draw) { + tip = TIP_("This override data-block is unused"); + } + } + + else if (id->flag & LIB_LIB_OVERRIDE_RESYNC_LEFTOVER) { + item_has_warnings = true; + if (do_draw) { + tip = TIP_( + "This override data-block is not needed anymore, but was detected as user-edited"); + } + } + break; + } + case TSE_LIBRARY_OVERRIDE: { + const bool is_rna_path_valid = (bool)(POINTER_AS_UINT(te->directdata)); + if (!is_rna_path_valid) { + item_has_warnings = true; + if (do_draw) { + tip = TIP_( + "This override property does not exist in current data, it will be removed on " + "next .blend file save"); + } + } + break; + } + default: + break; + } + + const bool any_child_has_warnings = outliner_draw_overrides_buts( + block, + region, + space_outliner, + &te->subtree, + is_open && TSELEM_OPEN(tselem, space_outliner)); + + if (do_draw && + (item_has_warnings || (any_child_has_warnings && !TSELEM_OPEN(tselem, space_outliner)))) { + if (tip == NULL) { + tip = TIP_("Some sub-items require attention"); + } + uiBut *bt = uiDefIconBlockBut(block, + NULL, + NULL, + 1, + ICON_ERROR, + (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS), + te->ys, + UI_UNIT_X, + UI_UNIT_Y, + tip); + UI_but_flag_enable(bt, but_flag); + } + any_item_has_warnings = any_item_has_warnings || item_has_warnings || any_child_has_warnings; + } + + return any_item_has_warnings; +} + static void outliner_draw_rnacols(ARegion *region, int sizex) { View2D *v2d = ®ion->v2d; @@ -2896,7 +2978,13 @@ static void outliner_draw_iconrow(bContext *C, active = tree_element_type_active_state_get(C, tvc, te, tselem); } - if (!ELEM(tselem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION, TSE_R_LAYER, TSE_GP_LAYER)) { + if (!ELEM(tselem->type, + TSE_SOME_ID, + TSE_LAYER_COLLECTION, + TSE_R_LAYER, + TSE_GP_LAYER, + TSE_LIBRARY_OVERRIDE_BASE, + TSE_LIBRARY_OVERRIDE)) { outliner_draw_iconrow_doit(block, te, fstyle, xmax, offsx, ys, alpha_fac, active, 1); } else { @@ -3656,7 +3744,11 @@ void draw_outliner(const bContext *C) } /* Sync selection state from view layer. */ - if (!ELEM(space_outliner->outlinevis, SO_LIBRARIES, SO_DATA_API, SO_ID_ORPHANS) && + if (!ELEM(space_outliner->outlinevis, + SO_LIBRARIES, + SO_OVERRIDES_LIBRARY, + SO_DATA_API, + SO_ID_ORPHANS) && space_outliner->flag & SO_SYNC_SELECT) { outliner_sync_selection(C, space_outliner); } @@ -3703,6 +3795,10 @@ void draw_outliner(const bContext *C) /* draw user toggle columns */ outliner_draw_userbuts(block, region, space_outliner, &space_outliner->tree); } + else if (space_outliner->outlinevis == SO_OVERRIDES_LIBRARY) { + /* Draw overrides status columns. */ + outliner_draw_overrides_buts(block, region, space_outliner, &space_outliner->tree, true); + } else if (restrict_column_width > 0.0f) { /* draw restriction columns */ RestrictPropertiesActive props_active; diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index f65e273c1b5..fea5ddae16b 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -193,7 +193,7 @@ typedef enum { /* The outliner display modes that support the filter system. * Note: keep it synced with space_outliner.py */ #define SUPPORT_FILTER_OUTLINER(space_outliner_) \ - (ELEM((space_outliner_)->outlinevis, SO_VIEW_LAYER)) + (ELEM((space_outliner_)->outlinevis, SO_VIEW_LAYER, SO_OVERRIDES_LIBRARY)) /* Outliner Searching -- * diff --git a/source/blender/editors/space_outliner/outliner_sync.c b/source/blender/editors/space_outliner/outliner_sync.c index 6543a909a41..d78767019b5 100644 --- a/source/blender/editors/space_outliner/outliner_sync.c +++ b/source/blender/editors/space_outliner/outliner_sync.c @@ -356,8 +356,11 @@ static void outliner_sync_selection_from_outliner(Scene *scene, void ED_outliner_select_sync_from_outliner(bContext *C, SpaceOutliner *space_outliner) { /* Don't sync if not checked or in certain outliner display modes */ - if (!(space_outliner->flag & SO_SYNC_SELECT) || - ELEM(space_outliner->outlinevis, SO_LIBRARIES, SO_DATA_API, SO_ID_ORPHANS)) { + if (!(space_outliner->flag & SO_SYNC_SELECT) || ELEM(space_outliner->outlinevis, + SO_LIBRARIES, + SO_OVERRIDES_LIBRARY, + SO_DATA_API, + SO_ID_ORPHANS)) { return; } diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 573fb492613..14ab7fd9974 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -908,6 +908,11 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, BLI_assert(!"Expected this ID type to be ported to new Outliner tree-element design"); } } + else if (ELEM(type, TSE_LIBRARY_OVERRIDE_BASE, TSE_LIBRARY_OVERRIDE)) { + if (!te->type) { + BLI_assert(!"Expected override types to be ported to new Outliner tree-element design"); + } + } else { /* Other cases must be caught above. */ BLI_assert(TSE_IS_REAL_ID(tselem)); diff --git a/source/blender/editors/space_outliner/outliner_utils.c b/source/blender/editors/space_outliner/outliner_utils.c index 562457c62e9..5feb157bfc8 100644 --- a/source/blender/editors/space_outliner/outliner_utils.c +++ b/source/blender/editors/space_outliner/outliner_utils.c @@ -361,6 +361,7 @@ float outliner_restrict_columns_width(const SpaceOutliner *space_outliner) case SO_DATA_API: case SO_SEQUENCE: case SO_LIBRARIES: + case SO_OVERRIDES_LIBRARY: return 0.0f; case SO_ID_ORPHANS: num_columns = 3; diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index 419713035b6..5d774049e3e 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -273,7 +273,7 @@ static void outliner_main_region_message_subscribe(const wmRegionMessageSubscrib .notify = ED_region_do_msg_notify_tag_redraw, }; - if (ELEM(space_outliner->outlinevis, SO_VIEW_LAYER, SO_SCENES)) { + if (ELEM(space_outliner->outlinevis, SO_VIEW_LAYER, SO_SCENES, SO_OVERRIDES_LIBRARY)) { WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_region_tag_redraw); } } diff --git a/source/blender/editors/space_outliner/tree/tree_display.cc b/source/blender/editors/space_outliner/tree/tree_display.cc index 4395383e838..003afd5bdec 100644 --- a/source/blender/editors/space_outliner/tree/tree_display.cc +++ b/source/blender/editors/space_outliner/tree/tree_display.cc @@ -44,6 +44,9 @@ TreeDisplay *outliner_tree_display_create(eSpaceOutliner_Mode mode, SpaceOutline case SO_ID_ORPHANS: tree_display = new TreeDisplayIDOrphans(*space_outliner); break; + case SO_OVERRIDES_LIBRARY: + tree_display = new TreeDisplayOverrideLibrary(*space_outliner); + break; case SO_VIEW_LAYER: default: tree_display = new TreeDisplayViewLayer(*space_outliner); diff --git a/source/blender/editors/space_outliner/tree/tree_display.hh b/source/blender/editors/space_outliner/tree/tree_display.hh index b6183050e82..f089a149805 100644 --- a/source/blender/editors/space_outliner/tree/tree_display.hh +++ b/source/blender/editors/space_outliner/tree/tree_display.hh @@ -110,6 +110,24 @@ class TreeDisplayLibraries final : public AbstractTreeDisplay { short id_filter_get() const; }; +/* -------------------------------------------------------------------- */ +/* Library Overrides Tree-Display. */ + +/** + * \brief Tree-Display for the Library Overrides display mode. + */ +class TreeDisplayOverrideLibrary final : public AbstractTreeDisplay { + public: + TreeDisplayOverrideLibrary(SpaceOutliner &space_outliner); + + ListBase buildTree(const TreeSourceData &source_data) override; + + private: + TreeElement *add_library_contents(Main &, ListBase &, Library *) const; + bool override_library_id_filter_poll(Library *lib, ID *id) const; + short id_filter_get() const; +}; + /* -------------------------------------------------------------------- */ /* Video Sequencer Tree-Display */ diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc new file mode 100644 index 00000000000..3059f8bfe0c --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_display_override_library.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 "DNA_collection_types.h" + +#include "BLT_translation.h" + +#include "../outliner_intern.h" +#include "tree_display.hh" + +namespace blender::ed::outliner { + +/* Convenience/readability. */ +template using List = ListBaseWrapper; + +TreeDisplayOverrideLibrary::TreeDisplayOverrideLibrary(SpaceOutliner &space_outliner) + : AbstractTreeDisplay(space_outliner) +{ +} + +ListBase TreeDisplayOverrideLibrary::buildTree(const TreeSourceData &source_data) +{ + ListBase tree = {nullptr}; + + { + /* current file first - mainvar provides tselem with unique pointer - not used */ + TreeElement *ten = add_library_contents(*source_data.bmain, 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); + TreeElement *ten = add_library_contents(*source_data.bmain, tree, lib); + /* NULL-check matters, due to filtering there may not be a new element. */ + if (ten) { + lib->id.newid = (ID *)ten; + } + } + + /* make hierarchy */ + for (TreeElement *ten : List(tree)) { + if (ten == tree.first) { + /* First item is main, skip. */ + continue; + } + + TreeStoreElem *tselem = TREESTORE(ten); + Library *lib = (Library *)tselem->id; + BLI_assert(!lib || (GS(lib->id.name) == ID_LI)); + if (!lib || !lib->parent) { + continue; + } + + TreeElement *parent = (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(&parent->subtree, ten); + ten->parent = parent; + } + else { + /* Else, make a new copy of the libtree for our parent. */ + TreeElement *dupten = add_library_contents(*source_data.bmain, parent->subtree, lib); + if (dupten) { + dupten->parent = parent; + } + } + } + /* restore newid pointers */ + for (ID *library_id : List(source_data.bmain->libraries)) { + library_id->newid = nullptr; + } + + return tree; +} + +TreeElement *TreeDisplayOverrideLibrary::add_library_contents(Main &mainvar, + ListBase &lb, + Library *lib) const +{ + const short filter_id_type = id_filter_get(); + + ListBase *lbarray[INDEX_ID_MAX]; + int tot; + if (filter_id_type) { + lbarray[0] = which_libbase(&mainvar, space_outliner_.filter_id_type); + tot = 1; + } + else { + tot = set_listbasepointers(&mainvar, lbarray); + } + + TreeElement *tenlib = nullptr; + for (int a = 0; a < tot; a++) { + if (!lbarray[a] || !lbarray[a]->first) { + continue; + } + + ID *id = nullptr; + + /* check if there's data in current lib */ + for (ID *id_iter : List(lbarray[a])) { + if (id_iter->lib == lib && ID_IS_OVERRIDE_LIBRARY_REAL(id_iter)) { + id = id_iter; + break; + } + } + + if (id != nullptr) { + 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, TSE_SOME_ID, 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) { + TreeElement *ten; + + 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 (override_library_id_filter_poll(lib, id)) { + TreeElement *override_tree_element = outliner_add_element( + &space_outliner_, &ten->subtree, id, ten, TSE_LIBRARY_OVERRIDE_BASE, 0); + + if (BLI_listbase_is_empty(&override_tree_element->subtree)) { + outliner_free_tree_element(override_tree_element, &ten->subtree); + } + } + } + } + } + } + + return tenlib; +} + +short TreeDisplayOverrideLibrary::id_filter_get() const +{ + if (space_outliner_.filter & SO_FILTER_ID_TYPE) { + return space_outliner_.filter_id_type; + } + return 0; +} + +bool TreeDisplayOverrideLibrary::override_library_id_filter_poll(Library *lib, ID *id) const +{ + if (id->lib != lib) { + return false; + } + + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + return false; + } + + return true; +} + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc index 6b222e877b1..3c561702a43 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc @@ -40,13 +40,24 @@ TreeElementOverridesBase::TreeElementOverridesBase(TreeElement &legacy_te, ID &i : AbstractTreeElement(legacy_te), id_(id) { BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE_BASE); - legacy_te.name = IFACE_("Library Overrides"); + if (legacy_te.parent != nullptr && + ELEM(legacy_te.parent->store_elem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION)) + + { + legacy_te.name = IFACE_("Library Overrides"); + } + else { + legacy_te.name = id.name + 2; + } } void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const { BLI_assert(id_.override_library != nullptr); + const bool show_system_overrides = (SUPPORT_FILTER_OUTLINER(&space_outliner) && + (space_outliner.filter & SO_FILTER_SHOW_SYSTEM_OVERRIDES) != + 0); PointerRNA idpoin; RNA_id_pointer_create(&id_, &idpoin); @@ -56,15 +67,26 @@ void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const for (auto *override_prop : ListBaseWrapper(id_.override_library->properties)) { - if (!BKE_lib_override_rna_property_find( - &idpoin, override_prop, &override_rna_ptr, &override_rna_prop)) { - /* This is fine, override properties list is not always fully up-to-date with current - * RNA/IDProps etc., this gets cleaned up when re-generating the overrides rules, - * no error here. */ - continue; + const bool is_rna_path_valid = BKE_lib_override_rna_property_find( + &idpoin, override_prop, &override_rna_ptr, &override_rna_prop); + if (is_rna_path_valid && !show_system_overrides && + ELEM(override_prop->rna_prop_type, PROP_POINTER, PROP_COLLECTION) && + RNA_struct_is_ID(RNA_property_pointer_type(&override_rna_ptr, override_rna_prop))) { + bool do_continue = true; + for (auto *override_prop_op : + ListBaseWrapper(override_prop->operations)) { + if ((override_prop_op->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) { + do_continue = false; + break; + } + } + + if (do_continue) { + continue; + } } - TreeElementOverridesData data = {id_, *override_prop}; + TreeElementOverridesData data = {id_, *override_prop, is_rna_path_valid}; outliner_add_element( &space_outliner, &legacy_te_.subtree, &data, &legacy_te_, TSE_LIBRARY_OVERRIDE, index++); } @@ -79,6 +101,9 @@ TreeElementOverridesProperty::TreeElementOverridesProperty(TreeElement &legacy_t BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE); legacy_te.name = override_prop_.rna_path; + /* Abusing this for now, better way to do it is also pending current refacor of the whole tree + * code to use CPP... */ + legacy_te.directdata = POINTER_FROM_UINT(override_data.is_rna_path_valid); } } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh index b5c772f5b33..c3caab8e268 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh @@ -27,6 +27,7 @@ namespace blender::ed::outliner { struct TreeElementOverridesData { ID &id; IDOverrideLibraryProperty &override_property; + bool is_rna_path_valid; }; class TreeElementOverridesBase final : public AbstractTreeElement { -- cgit v1.2.3