diff options
author | jon denning <gfxcoder@gmail.com> | 2022-07-07 21:51:22 +0300 |
---|---|---|
committer | jon denning <gfxcoder@gmail.com> | 2022-07-07 21:51:22 +0300 |
commit | 7502bc583c6dfb39dd9e548f47c8e427cfaec6d0 (patch) | |
tree | 53914cd88ddaefc83ad7f4d0b25ba5f4e01bb097 | |
parent | 089870ab3a8fdf40139a6737c25dc0662fca24f5 (diff) | |
parent | 52fa0c4251e7c92b2df665e9e3c150a54066389f (diff) |
Merge branch 'master' into transform_api
30 files changed, 669 insertions, 223 deletions
diff --git a/release/scripts/startup/bl_ui/properties_workspace.py b/release/scripts/startup/bl_ui/properties_workspace.py index 542d770c062..1a245b33cd2 100644 --- a/release/scripts/startup/bl_ui/properties_workspace.py +++ b/release/scripts/startup/bl_ui/properties_workspace.py @@ -28,6 +28,8 @@ class WORKSPACE_PT_main(WorkSpaceButtonsPanel, Panel): layout = self.layout layout.use_property_split = True layout.use_property_decorate = False + + layout.prop(workspace, "use_pin_scene") layout.prop(workspace, "object_mode", text="Mode") diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 5070bd04142..e08b921b1a9 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2279,6 +2279,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel): ({"property": "use_full_frame_compositor"}, "T88150"), ({"property": "enable_eevee_next"}, "T93220"), ({"property": "use_draw_manager_acquire_lock"}, "T98016"), + ({"property": "use_override_new_fully_editable"}, None), ), ) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index a71fc8d959c..5f790b31afb 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -2354,8 +2354,6 @@ class VIEW3D_MT_object_relations(Menu): layout = self.layout layout.operator("object.make_override_library", text="Make Library Override...") - layout.operator("object.make_override_library", - text="Make Library Override - Fully Editable...").do_fully_editable = True layout.operator("object.make_dupli_face") diff --git a/source/blender/blenkernel/BKE_mball.h b/source/blender/blenkernel/BKE_mball.h index f40d0bb3004..5a4988c7a5f 100644 --- a/source/blender/blenkernel/BKE_mball.h +++ b/source/blender/blenkernel/BKE_mball.h @@ -24,14 +24,25 @@ struct MetaBall *BKE_mball_add(struct Main *bmain, const char *name); bool BKE_mball_is_any_selected(const struct MetaBall *mb); bool BKE_mball_is_any_selected_multi(struct Base **bases, int bases_len); bool BKE_mball_is_any_unselected(const struct MetaBall *mb); -bool BKE_mball_is_basis_for(struct Object *ob1, struct Object *ob2); + +/** + * Return `true` if `ob1` and `ob2` are part of the same metaBall group. + * + * \note Currently checks whether their two base names (without numerical suffix) is the same. + */ +bool BKE_mball_is_same_group(const struct Object *ob1, const struct Object *ob2); +/** + * Return `true` if `ob1` and `ob2` are part of the same metaBall group, and `ob1` is its + * basis. + */ +bool BKE_mball_is_basis_for(const struct Object *ob1, const struct Object *ob2); /** * Test, if \a ob is a basis meta-ball. * * It test last character of Object ID name. * If last character is digit it return 0, else it return 1. */ -bool BKE_mball_is_basis(struct Object *ob); +bool BKE_mball_is_basis(const struct Object *ob); /** * This function finds the basis meta-ball. * @@ -58,13 +69,14 @@ struct BoundBox *BKE_mball_boundbox_get(struct Object *ob); float *BKE_mball_make_orco(struct Object *ob, struct ListBase *dispbase); /** - * Copy some properties from object to other meta-ball object with same base name. + * Copy some properties from a meta-ball obdata to all other meta-ball obdata belonging to the same + * family (i.e. object sharing the same name basis). * * When some properties (wire-size, threshold, update flags) of meta-ball are changed, then this * properties are copied to all meta-balls in same "group" (meta-balls with same base name: * `MBall`, `MBall.001`, `MBall.002`, etc). The most important is to copy properties to the base - * meta-ball, because this meta-ball influence polygonization of meta-balls. */ -void BKE_mball_properties_copy(struct Scene *scene, struct Object *active_object); + * meta-ball, because this meta-ball influences polygonization of meta-balls. */ +void BKE_mball_properties_copy(struct Main *bmain, struct MetaBall *active_metaball); bool BKE_mball_minmax_ex( const struct MetaBall *mb, float min[3], float max[3], const float obmat[4][4], short flag); diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index 1340e53f06e..819bbde6556 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -347,7 +347,7 @@ float *BKE_mball_make_orco(Object *ob, ListBase *dispbase) return orcodata; } -bool BKE_mball_is_basis(Object *ob) +bool BKE_mball_is_basis(const Object *ob) { /* Meta-Ball Basis Notes from Blender-2.5x * ======================================= @@ -370,7 +370,7 @@ bool BKE_mball_is_basis(Object *ob) return (!isdigit(ob->id.name[len - 1])); } -bool BKE_mball_is_basis_for(Object *ob1, Object *ob2) +bool BKE_mball_is_same_group(const Object *ob1, const Object *ob2) { int basis1nr, basis2nr; char basis1name[MAX_ID_NAME], basis2name[MAX_ID_NAME]; @@ -383,11 +383,12 @@ bool BKE_mball_is_basis_for(Object *ob1, Object *ob2) BLI_split_name_num(basis1name, &basis1nr, ob1->id.name + 2, '.'); BLI_split_name_num(basis2name, &basis2nr, ob2->id.name + 2, '.'); - if (STREQ(basis1name, basis2name)) { - return BKE_mball_is_basis(ob1); - } + return STREQ(basis1name, basis2name); +} - return false; +bool BKE_mball_is_basis_for(const Object *ob1, const Object *ob2) +{ + return BKE_mball_is_same_group(ob1, ob2) && BKE_mball_is_basis(ob1); } bool BKE_mball_is_any_selected(const MetaBall *mb) @@ -422,41 +423,82 @@ bool BKE_mball_is_any_unselected(const MetaBall *mb) return false; } -void BKE_mball_properties_copy(Scene *scene, Object *active_object) +static void mball_data_properties_copy(MetaBall *mb_dst, MetaBall *mb_src) { - Scene *sce_iter = scene; - Base *base; - Object *ob; - MetaBall *active_mball = (MetaBall *)active_object->data; - int basisnr, obnr; - char basisname[MAX_ID_NAME], obname[MAX_ID_NAME]; - SceneBaseIter iter; - - BLI_split_name_num(basisname, &basisnr, active_object->id.name + 2, '.'); - - /* Pass depsgraph as NULL, which means we will not expand into - * duplis unlike when we generate the meta-ball. Expanding duplis - * would not be compatible when editing multiple view layers. */ - BKE_scene_base_iter_next(NULL, &iter, &sce_iter, 0, NULL, NULL); - while (BKE_scene_base_iter_next(NULL, &iter, &sce_iter, 1, &base, &ob)) { - if (ob->type == OB_MBALL) { - if (ob != active_object) { - BLI_split_name_num(obname, &obnr, ob->id.name + 2, '.'); - - /* Object ob has to be in same "group" ... it means, that it has to have - * same base of its name */ - if (STREQ(obname, basisname)) { - MetaBall *mb = ob->data; - - /* Copy properties from selected/edited metaball */ - mb->wiresize = active_mball->wiresize; - mb->rendersize = active_mball->rendersize; - mb->thresh = active_mball->thresh; - mb->flag = active_mball->flag; - DEG_id_tag_update(&mb->id, 0); - } + mb_dst->wiresize = mb_src->wiresize; + mb_dst->rendersize = mb_src->rendersize; + mb_dst->thresh = mb_src->thresh; + mb_dst->flag = mb_src->flag; + DEG_id_tag_update(&mb_dst->id, 0); +} + +void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src) +{ + /* WARNING: This code does not cover all potential corner-cases. E.g. if: + * | Object | ObData | + * | ---------- | ---------- | + * | Meta_A | Meta_A | + * | Meta_A.001 | Meta_A.001 | + * | Meta_B | Meta_A | + * | Meta_B.001 | Meta_B.001 | + * + * Calling this function with `metaball_src` being `Meta_A.001` will update `Meta_A`, but NOT + * `Meta_B.001`. So in the 'Meta_B' family, the two metaballs will have unmatching settings now. + * + * Solving this case would drastically increase the complexity of this code though, so don't + * think it would be worth it. + */ + for (Object *ob_src = bmain->objects.first; ob_src != NULL && !ID_IS_LINKED(ob_src);) { + if (ob_src->data != metaball_src) { + ob_src = ob_src->id.next; + continue; + } + + /* In this code we take advantage of two facts: + * - MetaBalls of the same family have the same basis name, + * - IDs are sorted by name in their Main listbase. + * So, all MetaBall objects of the same family are contiguous in bmain list (potentially mixed + * with non-meta-ball objects with same basis names). + * + * Using this, it is possible to process the whole set of meta-balls with a single loop on the + * whole list of Objects, though additionally going backward on part of the list in some cases. + */ + Object *ob_iter = NULL; + int obactive_nr, ob_nr; + char obactive_name[MAX_ID_NAME], ob_name[MAX_ID_NAME]; + BLI_split_name_num(obactive_name, &obactive_nr, ob_src->id.name + 2, '.'); + + for (ob_iter = ob_src->id.prev; ob_iter != NULL; ob_iter = ob_iter->id.prev) { + if (ob_iter->id.name[2] != obactive_name[0]) { + break; + } + if (ob_iter->type != OB_MBALL || ob_iter->data == metaball_src) { + continue; + } + BLI_split_name_num(ob_name, &ob_nr, ob_iter->id.name + 2, '.'); + if (!STREQ(obactive_name, ob_name)) { + break; + } + + mball_data_properties_copy(ob_iter->data, metaball_src); + } + + for (ob_iter = ob_src->id.next; ob_iter != NULL; ob_iter = ob_iter->id.next) { + if (ob_iter->id.name[2] != obactive_name[0] || ID_IS_LINKED(ob_iter)) { + break; + } + if (ob_iter->type != OB_MBALL || ob_iter->data == metaball_src) { + continue; } + BLI_split_name_num(ob_name, &ob_nr, ob_iter->id.name + 2, '.'); + if (!STREQ(obactive_name, ob_name)) { + break; + } + + mball_data_properties_copy(ob_iter->data, metaball_src); } + + ob_src = ob_iter; } } diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index ef0a3069815..0265dd037b1 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -67,6 +67,8 @@ static void workspace_foreach_id(ID *id, LibraryForeachIDData *data) { WorkSpace *workspace = (WorkSpace *)id; + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, workspace->pin_scene, IDWALK_CB_NOP); + LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, layout->screen, IDWALK_CB_USER); } @@ -120,6 +122,15 @@ static void workspace_blend_read_lib(BlendLibReader *reader, ID *id) WorkSpace *workspace = (WorkSpace *)id; Main *bmain = BLO_read_lib_get_main(reader); + /* Do not keep the scene reference when appending a workspace. Setting a scene for a workspace is + * a convenience feature, but the workspace should never truly depend on scene data. */ + if (ID_IS_LINKED(id)) { + workspace->pin_scene = NULL; + } + else { + BLO_read_id_address(reader, NULL, &workspace->pin_scene); + } + /* Restore proper 'parent' pointers to relevant data, and clean up unused/invalid entries. */ LISTBASE_FOREACH_MUTABLE (WorkSpaceDataRelation *, relation, &workspace->hook_layout_relations) { relation->parent = NULL; diff --git a/source/blender/blenlib/BLI_generic_span.hh b/source/blender/blenlib/BLI_generic_span.hh index 4c0bfc83ba8..143ab235d2e 100644 --- a/source/blender/blenlib/BLI_generic_span.hh +++ b/source/blender/blenlib/BLI_generic_span.hh @@ -16,20 +16,31 @@ namespace blender { */ class GSpan { protected: - const CPPType *type_; - const void *data_; - int64_t size_; + const CPPType *type_ = nullptr; + const void *data_ = nullptr; + int64_t size_ = 0; public: - GSpan(const CPPType &type, const void *buffer, int64_t size) - : type_(&type), data_(buffer), size_(size) + GSpan() = default; + + GSpan(const CPPType *type, const void *buffer, int64_t size) + : type_(type), data_(buffer), size_(size) { BLI_assert(size >= 0); BLI_assert(buffer != nullptr || size == 0); - BLI_assert(type.pointer_has_valid_alignment(buffer)); + BLI_assert(size == 0 || type != nullptr); + BLI_assert(type == nullptr || type->pointer_has_valid_alignment(buffer)); + } + + GSpan(const CPPType &type, const void *buffer, int64_t size) : GSpan(&type, buffer, size) + { + } + + GSpan(const CPPType &type) : type_(&type) + { } - GSpan(const CPPType &type) : GSpan(type, nullptr, 0) + GSpan(const CPPType *type) : type_(type) { } @@ -41,9 +52,15 @@ class GSpan { const CPPType &type() const { + BLI_assert(type_ != nullptr); return *type_; } + const CPPType *type_ptr() const + { + return type_; + } + bool is_empty() const { return size_ == 0; @@ -76,7 +93,7 @@ class GSpan { BLI_assert(start >= 0); BLI_assert(size >= 0); const int64_t new_size = std::max<int64_t>(0, std::min(size, size_ - start)); - return GSpan(*type_, POINTER_OFFSET(data_, type_->size() * start), new_size); + return GSpan(type_, POINTER_OFFSET(data_, type_->size() * start), new_size); } GSpan slice(const IndexRange range) const @@ -91,20 +108,31 @@ class GSpan { */ class GMutableSpan { protected: - const CPPType *type_; - void *data_; - int64_t size_; + const CPPType *type_ = nullptr; + void *data_ = nullptr; + int64_t size_ = 0; public: - GMutableSpan(const CPPType &type, void *buffer, int64_t size) - : type_(&type), data_(buffer), size_(size) + GMutableSpan() = default; + + GMutableSpan(const CPPType *type, void *buffer, int64_t size) + : type_(type), data_(buffer), size_(size) { BLI_assert(size >= 0); BLI_assert(buffer != nullptr || size == 0); - BLI_assert(type.pointer_has_valid_alignment(buffer)); + BLI_assert(size == 0 || type != nullptr); + BLI_assert(type == nullptr || type->pointer_has_valid_alignment(buffer)); + } + + GMutableSpan(const CPPType &type, void *buffer, int64_t size) : GMutableSpan(&type, buffer, size) + { + } + + GMutableSpan(const CPPType &type) : type_(&type) + { } - GMutableSpan(const CPPType &type) : GMutableSpan(type, nullptr, 0) + GMutableSpan(const CPPType *type) : type_(type) { } @@ -116,14 +144,20 @@ class GMutableSpan { operator GSpan() const { - return GSpan(*type_, data_, size_); + return GSpan(type_, data_, size_); } const CPPType &type() const { + BLI_assert(type_ != nullptr); return *type_; } + const CPPType *type_ptr() const + { + return type_; + } + bool is_empty() const { return size_ == 0; diff --git a/source/blender/blenlib/BLI_generic_virtual_array.hh b/source/blender/blenlib/BLI_generic_virtual_array.hh index 3526925c2e2..43ca16a894f 100644 --- a/source/blender/blenlib/BLI_generic_virtual_array.hh +++ b/source/blender/blenlib/BLI_generic_virtual_array.hh @@ -267,6 +267,7 @@ class GVArraySpan : public GSpan { void *owned_data_ = nullptr; public: + GVArraySpan(); GVArraySpan(GVArray varray); GVArraySpan(GVArraySpan &&other); ~GVArraySpan(); @@ -282,11 +283,14 @@ class GMutableVArraySpan : public GMutableSpan, NonCopyable, NonMovable { bool show_not_saved_warning_ = true; public: + GMutableVArraySpan(); GMutableVArraySpan(GVMutableArray varray, bool copy_values_to_span = true); GMutableVArraySpan(GMutableVArraySpan &&other); ~GMutableVArraySpan(); GMutableVArraySpan &operator=(GMutableVArraySpan &&other); + const GVMutableArray &varray() const; + void save(); void disable_not_applied_warning(); }; diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh index 84f6223580a..56b2f9f6c6f 100644 --- a/source/blender/blenlib/BLI_virtual_array.hh +++ b/source/blender/blenlib/BLI_virtual_array.hh @@ -1130,6 +1130,9 @@ template<typename T> class VArraySpan final : public Span<T> { VArraySpan(VArray<T> varray) : Span<T>(), varray_(std::move(varray)) { + if (!varray_) { + return; + } this->size_ = varray_.size(); const CommonVArrayInfo info = varray_.common_info(); if (info.type == CommonVArrayInfo::Type::Span) { @@ -1146,6 +1149,9 @@ template<typename T> class VArraySpan final : public Span<T> { VArraySpan(VArraySpan &&other) : varray_(std::move(other.varray_)), owned_data_(std::move(other.owned_data_)) { + if (!varray_) { + return; + } this->size_ = varray_.size(); const CommonVArrayInfo info = varray_.common_info(); if (info.type == CommonVArrayInfo::Type::Span) { @@ -1184,11 +1190,17 @@ template<typename T> class MutableVArraySpan final : public MutableSpan<T> { bool show_not_saved_warning_ = true; public: + MutableVArraySpan() = default; + /* Create a span for any virtual array. This is cheap when the virtual array is a span itself. If * not, a new array has to be allocated as a wrapper for the underlying virtual array. */ MutableVArraySpan(VMutableArray<T> varray, const bool copy_values_to_span = true) : MutableSpan<T>(), varray_(std::move(varray)) { + if (!varray_) { + return; + } + this->size_ = varray_.size(); const CommonVArrayInfo info = varray_.common_info(); if (info.type == CommonVArrayInfo::Type::Span) { @@ -1212,10 +1224,14 @@ template<typename T> class MutableVArraySpan final : public MutableSpan<T> { owned_data_(std::move(owned_data_)), show_not_saved_warning_(other.show_not_saved_warning_) { + if (!varray_) { + return; + } + this->size_ = varray_.size(); const CommonVArrayInfo info = varray_.common_info(); if (info.type == CommonVArrayInfo::Type::Span) { - this->data_ = reinterpret_cast<T *>(info.data); + this->data_ = static_cast<T *>(const_cast<void *>(info.data)); } else { this->data_ = owned_data_.data(); @@ -1245,6 +1261,11 @@ template<typename T> class MutableVArraySpan final : public MutableSpan<T> { return *this; } + const VMutableArray<T> &varray() const + { + return varray_; + } + /* Write back all values from a temporary allocated array to the underlying virtual array. */ void save() { diff --git a/source/blender/blenlib/intern/generic_virtual_array.cc b/source/blender/blenlib/intern/generic_virtual_array.cc index 532c7b9b626..28bc66e8524 100644 --- a/source/blender/blenlib/intern/generic_virtual_array.cc +++ b/source/blender/blenlib/intern/generic_virtual_array.cc @@ -287,8 +287,15 @@ template<int BufferSize> class GVArrayImpl_For_SmallTrivialSingleValue : public /** \name #GVArraySpan * \{ */ -GVArraySpan::GVArraySpan(GVArray varray) : GSpan(varray.type()), varray_(std::move(varray)) +GVArraySpan::GVArraySpan() = default; + +GVArraySpan::GVArraySpan(GVArray varray) + : GSpan(varray ? &varray.type() : nullptr), varray_(std::move(varray)) { + if (!varray_) { + return; + } + size_ = varray_.size(); const CommonVArrayInfo info = varray_.common_info(); if (info.type == CommonVArrayInfo::Type::Span) { @@ -302,8 +309,12 @@ GVArraySpan::GVArraySpan(GVArray varray) : GSpan(varray.type()), varray_(std::mo } GVArraySpan::GVArraySpan(GVArraySpan &&other) - : GSpan(other.type()), varray_(std::move(other.varray_)), owned_data_(other.owned_data_) + : GSpan(other.type_ptr()), varray_(std::move(other.varray_)), owned_data_(other.owned_data_) { + if (!varray_) { + return; + } + size_ = varray_.size(); const CommonVArrayInfo info = varray_.common_info(); if (info.type == CommonVArrayInfo::Type::Span) { @@ -340,9 +351,14 @@ GVArraySpan &GVArraySpan::operator=(GVArraySpan &&other) /** \name #GMutableVArraySpan * \{ */ +GMutableVArraySpan::GMutableVArraySpan() = default; + GMutableVArraySpan::GMutableVArraySpan(GVMutableArray varray, const bool copy_values_to_span) - : GMutableSpan(varray.type()), varray_(std::move(varray)) + : GMutableSpan(varray ? &varray.type() : nullptr), varray_(std::move(varray)) { + if (!varray_) { + return; + } size_ = varray_.size(); const CommonVArrayInfo info = varray_.common_info(); if (info.type == CommonVArrayInfo::Type::Span) { @@ -361,11 +377,14 @@ GMutableVArraySpan::GMutableVArraySpan(GVMutableArray varray, const bool copy_va } GMutableVArraySpan::GMutableVArraySpan(GMutableVArraySpan &&other) - : GMutableSpan(other.type()), + : GMutableSpan(other.type_ptr()), varray_(std::move(other.varray_)), owned_data_(other.owned_data_), show_not_saved_warning_(other.show_not_saved_warning_) { + if (!varray_) { + return; + } size_ = varray_.size(); const CommonVArrayInfo info = varray_.common_info(); if (info.type == CommonVArrayInfo::Type::Span) { @@ -417,6 +436,11 @@ void GMutableVArraySpan::disable_not_applied_warning() show_not_saved_warning_ = false; } +const GVMutableArray &GMutableVArraySpan::varray() const +{ + return varray_; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenlib/tests/BLI_virtual_array_test.cc b/source/blender/blenlib/tests/BLI_virtual_array_test.cc index c3c6d84ea42..14f5480f751 100644 --- a/source/blender/blenlib/tests/BLI_virtual_array_test.cc +++ b/source/blender/blenlib/tests/BLI_virtual_array_test.cc @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 */ #include "BLI_array.hh" +#include "BLI_generic_virtual_array.hh" #include "BLI_strict_flags.h" #include "BLI_vector.hh" #include "BLI_vector_set.hh" @@ -222,4 +223,36 @@ TEST(virtual_array, MaterializeCompressed) } } +TEST(virtual_array, EmptySpanWrapper) +{ + { + VArray<int> varray; + VArraySpan<int> span1 = varray; + EXPECT_TRUE(span1.is_empty()); + VArraySpan<int> span2 = std::move(span1); + EXPECT_TRUE(span2.is_empty()); + } + { + VMutableArray<int> varray; + MutableVArraySpan<int> span1 = varray; + EXPECT_TRUE(span1.is_empty()); + MutableVArraySpan<int> span2 = std::move(span1); + EXPECT_TRUE(span2.is_empty()); + } + { + GVArray varray; + GVArraySpan span1 = varray; + EXPECT_TRUE(span1.is_empty()); + GVArraySpan span2 = std::move(span1); + EXPECT_TRUE(span2.is_empty()); + } + { + GVMutableArray varray; + GMutableVArraySpan span1 = varray; + EXPECT_TRUE(span1.is_empty()); + GMutableVArraySpan span2 = std::move(span1); + EXPECT_TRUE(span2.is_empty()); + } +} + } // namespace blender::tests diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 973965ada50..f0d390677bb 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -2732,6 +2732,8 @@ void blo_lib_link_restore(Main *oldmain, LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) { lib_link_workspace_layout_restore(id_map, newmain, layout); } + workspace->pin_scene = restore_pointer_by_name( + id_map, (ID *)workspace->pin_scene, USER_IGNORE); } LISTBASE_FOREACH (wmWindow *, win, &curwm->windows) { @@ -2745,6 +2747,7 @@ void blo_lib_link_restore(Main *oldmain, if (win->scene == NULL) { win->scene = curscene; } + win->unpinned_scene = restore_pointer_by_name(id_map, (ID *)win->unpinned_scene, USER_IGNORE); if (BKE_view_layer_find(win->scene, win->view_layer_name) == NULL) { STRNCPY(win->view_layer_name, cur_view_layer->name); } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 05ae5299e58..14da5a7cd62 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -741,8 +741,15 @@ static void template_id_liboverride_hierarchy_create(bContext *C, if (object_active != NULL) { object_active->id.tag |= LIB_TAG_DOIT; } - BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override, false); + BKE_lib_override_library_create(bmain, + scene, + view_layer, + NULL, + id, + &collection_active->id, + NULL, + &id_override, + U.experimental.use_override_new_fully_editable); } else if (object_active != NULL && !ID_IS_LINKED(object_active) && &object_active->instance_collection->id == id) { @@ -755,7 +762,7 @@ static void template_id_liboverride_hierarchy_create(bContext *C, &object_active->id, &object_active->id, &id_override, - false); + U.experimental.use_override_new_fully_editable); } break; case ID_OB: @@ -765,8 +772,15 @@ static void template_id_liboverride_hierarchy_create(bContext *C, if (object_active != NULL) { object_active->id.tag |= LIB_TAG_DOIT; } - BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override, false); + BKE_lib_override_library_create(bmain, + scene, + view_layer, + NULL, + id, + &collection_active->id, + NULL, + &id_override, + U.experimental.use_override_new_fully_editable); } break; case ID_ME: @@ -796,12 +810,19 @@ static void template_id_liboverride_hierarchy_create(bContext *C, &collection_active->id, NULL, &id_override, - false); + U.experimental.use_override_new_fully_editable); } else { object_active->id.tag |= LIB_TAG_DOIT; - BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, &object_active->id, NULL, &id_override, false); + BKE_lib_override_library_create(bmain, + scene, + view_layer, + NULL, + id, + &object_active->id, + NULL, + &id_override, + U.experimental.use_override_new_fully_editable); } } break; @@ -1024,6 +1045,26 @@ static const char *template_id_browse_tip(const StructRNA *type) } /** + * Add a superimposed extra icon to \a but, for workspace pinning. + * Rather ugly special handling, but this is really a special case at this point, nothing worth + * generalizing. + */ +static void template_id_workspace_pin_extra_icon(const TemplateID *template_ui, uiBut *but) +{ + if ((template_ui->idcode != ID_SCE) || (template_ui->ptr.type != &RNA_Window)) { + return; + } + + const wmWindow *win = template_ui->ptr.data; + const WorkSpace *workspace = WM_window_get_active_workspace(win); + UI_but_extra_operator_icon_add(but, + "WORKSPACE_OT_scene_pin_toggle", + WM_OP_INVOKE_DEFAULT, + (workspace->flags & WORKSPACE_USE_PIN_SCENE) ? ICON_PINNED : + ICON_UNPINNED); +} + +/** * \return a type-based i18n context, needed e.g. by "New" button. * In most languages, this adjective takes different form based on gender of type name... */ @@ -1220,6 +1261,8 @@ static void template_ID(const bContext *C, UI_but_flag_enable(but, UI_BUT_REDALERT); } + template_id_workspace_pin_extra_icon(template_ui, but); + if (ID_IS_LINKED(id)) { const bool disabled = !BKE_idtype_idcode_is_localizable(GS(id->name)); if (id->tag & LIB_TAG_INDIRECT) { diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index f55ffdf0fcd..b58b0dac79b 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2267,7 +2267,7 @@ static int make_override_library_exec(bContext *C, wmOperator *op) ID *id_root = NULL; bool is_override_instancing_object = false; - const bool do_fully_editable = RNA_boolean_get(op->ptr, "do_fully_editable"); + const bool do_fully_editable = U.experimental.use_override_new_fully_editable; GSet *user_overrides_objects_uids = do_fully_editable ? NULL : BLI_gset_new(BLI_ghashutil_inthash_p, @@ -2495,13 +2495,6 @@ void OBJECT_OT_make_override_library(wmOperatorType *ot) INT_MAX); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); ot->prop = prop; - - prop = RNA_def_boolean(ot->srna, - "do_fully_editable", - false, - "Create Fully Editable", - "Make all created override data-blocks fully editable"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /** \} */ diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c index 01417650b3d..cb29f15420c 100644 --- a/source/blender/editors/screen/workspace_edit.c +++ b/source/blender/editors/screen/workspace_edit.c @@ -54,17 +54,87 @@ WorkSpace *ED_workspace_add(Main *bmain, const char *name) return BKE_workspace_add(bmain, name); } +static void workspace_exit(WorkSpace *workspace, wmWindow *win) +{ + /* Scene pinning: Store whatever scene was active when leaving the workspace. It's reactivated + * when the workspace gets reactivated as well. */ + if (workspace->flags & WORKSPACE_USE_PIN_SCENE) { + workspace->pin_scene = WM_window_get_active_scene(win); + } + else { + /* The active scene may have been changed. So also always update the unpinned scene to the + * latest when leaving a workspace that has no scene pinning. */ + win->unpinned_scene = WM_window_get_active_scene(win); + } +} + +/** + * State changes (old workspace to new workspace): + * 1) unpinned -> pinned + * * Store current scene as the unpinned one (done in #workspace_exit()). + * * Change the current scene to the pinned one. + * 2) pinned -> pinned + * * Change the current scene to the new pinned one. + * 3) pinned -> unpinned + * * Change current scene back to the unpinned one + * 4) unpinned -> unpinned + * * Make sure the unpinned scene is active. + * + * Note that the pin scene must also be updated when leaving a workspace with a pinned scene. + * That's done separately via workspace_exit() above. + */ +static void workspace_scene_pinning_update(WorkSpace *workspace_new, + const WorkSpace *workspace_old, + bContext *C) +{ + wmWindow *win = CTX_wm_window(C); + Main *bmain = CTX_data_main(C); + Scene *active_scene = WM_window_get_active_scene(win); + + const bool is_new_pinned = (workspace_new->flags & WORKSPACE_USE_PIN_SCENE); + const bool is_old_pinned = (workspace_old->flags & WORKSPACE_USE_PIN_SCENE); + + /* State changes 1 and 2. */ + if (is_new_pinned) { + if (workspace_new->pin_scene && (workspace_new->pin_scene != active_scene)) { + WM_window_set_active_scene(bmain, C, win, workspace_new->pin_scene); + workspace_new->pin_scene = NULL; + } + } + /* State change 3 - Changing from workspace with pinned scene to unpinned scene. */ + else if (is_old_pinned) { + if (win->unpinned_scene) { + WM_window_set_active_scene(bmain, C, win, win->unpinned_scene); + } + else { + /* When leaving a workspace where the pinning was just enabled, the unpinned scene wasn't set + * yet. */ + win->unpinned_scene = active_scene; + } + } + else { + /* When leaving a workspace where the pinning was just disabled, we still want to restore the + * unpinned scene. */ + if (win->unpinned_scene) { + WM_window_set_active_scene(bmain, C, win, win->unpinned_scene); + } + } + + BLI_assert(WM_window_get_active_scene(win)); +} + /** * Changes the object mode (if needed) to the one set in \a workspace_new. * Object mode is still stored on object level. In future it should all be workspace level instead. */ static void workspace_change_update(WorkSpace *workspace_new, - const WorkSpace *workspace_old, + WorkSpace *workspace_old, bContext *C, wmWindowManager *wm) { + workspace_scene_pinning_update(workspace_new, workspace_old, C); /* needs to be done before changing mode! (to ensure right context) */ - UNUSED_VARS(workspace_old, workspace_new, C, wm); + UNUSED_VARS(wm); #if 0 Object *ob_act = CTX_data_active_object(C) eObjectMode mode_old = workspace_old->object_mode; eObjectMode mode_new = workspace_new->object_mode; @@ -113,6 +183,8 @@ bool ED_workspace_change(WorkSpace *workspace_new, bContext *C, wmWindowManager return false; } + workspace_exit(workspace_old, win); + screen_change_prepare(screen_old, screen_new, bmain, C, win); if (screen_new == NULL) { @@ -143,6 +215,7 @@ WorkSpace *ED_workspace_duplicate(WorkSpace *workspace_old, Main *bmain, wmWindo WorkSpace *workspace_new = ED_workspace_add(bmain, workspace_old->id.name + 2); workspace_new->flags = workspace_old->flags; + workspace_new->pin_scene = workspace_old->pin_scene; workspace_new->object_mode = workspace_old->object_mode; workspace_new->order = workspace_old->order; BLI_duplicatelist(&workspace_new->owner_ids, &workspace_old->owner_ids); @@ -512,6 +585,35 @@ static void WORKSPACE_OT_reorder_to_front(wmOperatorType *ot) ot->exec = workspace_reorder_to_front_exec; } +static int workspace_scene_pin_toggle(bContext *C, wmOperator *UNUSED(op)) +{ + WorkSpace *workspace = workspace_context_get(C); + + /* Trivial. The operator is only needed to display a superimposed extra icon, which + * requires an operator. */ + workspace->flags ^= WORKSPACE_USE_PIN_SCENE; + + WM_event_add_notifier(C, NC_WORKSPACE, NULL); + + return OPERATOR_FINISHED; +} + +static void WORKSPACE_OT_scene_pin_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Pin Scene to Workspace"; + ot->description = + "Remember the last used scene for the current workspace and switch to it whenever this " + "workspace is activated again"; + ot->idname = "WORKSPACE_OT_scene_pin_toggle"; + + /* api callbacks */ + ot->poll = workspace_context_poll; + ot->exec = workspace_scene_pin_toggle; + + ot->flag = OPTYPE_INTERNAL; +} + void ED_operatortypes_workspace(void) { WM_operatortype_append(WORKSPACE_OT_duplicate); @@ -520,6 +622,7 @@ void ED_operatortypes_workspace(void) WM_operatortype_append(WORKSPACE_OT_append_activate); WM_operatortype_append(WORKSPACE_OT_reorder_to_back); WM_operatortype_append(WORKSPACE_OT_reorder_to_front); + WM_operatortype_append(WORKSPACE_OT_scene_pin_toggle); } /** \} Workspace Operators */ diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 052af39319c..e60946b8f66 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -778,6 +778,9 @@ static void buttons_area_listener(const wmSpaceTypeListenerParams *params) sbuts->preview = 1; } break; + case NC_WORKSPACE: + buttons_area_redraw(area, BCONTEXT_TOOL); + break; case NC_SPACE: if (wmn->data == ND_SPACE_PROPERTIES) { ED_area_tag_redraw(area); diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index 04e74f5438b..7b7182eb106 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -1995,7 +1995,6 @@ enum eOutlinerIdOpTypes { OUTLINER_IDOP_LOCAL, OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE, OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY, - OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE, OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY, @@ -2041,12 +2040,6 @@ static const EnumPropertyItem prop_id_op_types[] = { "Make Library Override Hierarchy", "Make a local override of this linked data-block, and its hierarchy of dependencies - only " "applies to active Outliner item"}, - {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE, - "OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE", - 0, - "Make Library Override Hierarchy Fully Editable", - "Make a local override of this linked data-block, and its hierarchy of dependencies, making " - "them all fully user-editable - only applies to active Outliner item"}, {OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE, "OVERRIDE_LIBRARY_MAKE_EDITABLE", 0, @@ -2126,7 +2119,6 @@ static bool outliner_id_operation_item_poll(bContext *C, } return false; case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE: if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id) || (ID_IS_LINKED(tselem->id))) { return true; } @@ -2270,6 +2262,7 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: { OutlinerLibOverrideData override_data{}; override_data.do_hierarchy = true; + override_data.do_fully_editable = U.experimental.use_override_new_fully_editable; outliner_do_libdata_operation(C, op->reports, scene, @@ -2283,23 +2276,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) ED_undo_push(C, "Overridden Data Hierarchy"); break; } - case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY_FULLY_EDITABLE: { - OutlinerLibOverrideData override_data{}; - override_data.do_hierarchy = true; - override_data.do_fully_editable = true; - outliner_do_libdata_operation(C, - op->reports, - scene, - space_outliner, - id_override_library_create_hierarchy_pre_process_fn, - &override_data); - outliner_do_libdata_operation( - C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data); - id_override_library_create_hierarchy_post_process(C, &override_data); - - ED_undo_push(C, "Overridden Data Hierarchy Fully Editable"); - break; - } case OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE: { outliner_do_libdata_operation(C, op->reports, diff --git a/source/blender/editors/space_outliner/tree/tree_display.hh b/source/blender/editors/space_outliner/tree/tree_display.hh index 190e35c81d6..f8e35655c26 100644 --- a/source/blender/editors/space_outliner/tree/tree_display.hh +++ b/source/blender/editors/space_outliner/tree/tree_display.hh @@ -161,7 +161,6 @@ class TreeDisplayOverrideLibraryHierarchies final : public AbstractTreeDisplay { ListBase build_hierarchy_for_lib_or_main(Main *bmain, TreeElement &parent_te, Library *lib = nullptr); - void build_hierarchy_for_ID(Main *bmain, ID &override_root_id, TreeElementID &te_id) const; }; /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc index 67798e978ab..f8705c3f0ad 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc @@ -4,25 +4,23 @@ * \ingroup spoutliner */ -#include "DNA_ID.h" -#include "DNA_collection_types.h" #include "DNA_key_types.h" #include "DNA_space_types.h" -#include "BLI_listbase.h" +#include "BLI_function_ref.hh" +#include "BLI_ghash.h" #include "BLI_map.hh" + #include "BLI_set.hh" #include "BLT_translation.h" -#include "BKE_collection.h" #include "BKE_lib_query.h" #include "BKE_main.h" #include "../outliner_intern.hh" #include "common.hh" #include "tree_display.hh" -#include "tree_element_id.hh" namespace blender::ed::outliner { @@ -42,8 +40,8 @@ ListBase TreeDisplayOverrideLibraryHierarchies::buildTree(const TreeSourceData & TreeElement *current_file_te = outliner_add_element( &space_outliner_, &tree, source_data.bmain, nullptr, TSE_ID_BASE, -1); current_file_te->name = IFACE_("Current File"); + AbstractTreeElement::uncollapse_by_default(current_file_te); { - AbstractTreeElement::uncollapse_by_default(current_file_te); build_hierarchy_for_lib_or_main(source_data.bmain, *current_file_te); /* Add dummy child if there's nothing to display. */ @@ -76,11 +74,49 @@ ListBase TreeDisplayOverrideLibraryHierarchies::buildTree(const TreeSourceData & return tree; } +/* -------------------------------------------------------------------- */ +/** \name Library override hierarchy building + * \{ */ + +class OverrideIDHierarchyBuilder { + SpaceOutliner &space_outliner_; + MainIDRelations &id_relations_; + + struct HierarchyBuildData { + const ID &override_root_id_; + /* The ancestor IDs leading to the current ID, to avoid IDs recursing into themselves. Changes + * with every level of recursion. */ + Set<const ID *> parent_ids{}; + /* The IDs that were already added to #parent_te, to avoid duplicates. Entirely new set with + * every level of recursion. */ + Set<const ID *> sibling_ids{}; + }; + + public: + OverrideIDHierarchyBuilder(SpaceOutliner &space_outliner, MainIDRelations &id_relations) + : space_outliner_(space_outliner), id_relations_(id_relations) + { + } + + void build_hierarchy_for_ID(ID &root_id, TreeElement &te_to_expand); + + private: + void build_hierarchy_for_ID_recursive(const ID &parent_id, + HierarchyBuildData &build_data, + TreeElement &te_to_expand); +}; + ListBase TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_lib_or_main( Main *bmain, TreeElement &parent_te, Library *lib) { ListBase tree = {nullptr}; + /* Ensure #Main.relations contains the latest mapping of relations. Must be freed before + * returning. */ + BKE_main_relations_create(bmain, 0); + + OverrideIDHierarchyBuilder builder(space_outliner_, *bmain->relations); + /* Keep track over which ID base elements were already added, and expand them once added. */ Map<ID_Type, TreeElement *> id_base_te_map; /* Index for the ID base elements ("Objects", "Materials", etc). */ @@ -109,108 +145,174 @@ ListBase TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_lib_or_main( TreeElement *new_id_te = outliner_add_element( &space_outliner_, &new_base_te->subtree, iter_id, new_base_te, TSE_SOME_ID, 0, false); - build_hierarchy_for_ID(bmain, *iter_id, *tree_element_cast<TreeElementID>(new_id_te)); + builder.build_hierarchy_for_ID(*iter_id, *new_id_te); } FOREACH_MAIN_ID_END; + BKE_main_relations_free(bmain); + return tree; } -struct BuildHierarchyForeachIDCbData { - /* Don't allow copies, the sets below would need deep copying. */ - BuildHierarchyForeachIDCbData(const BuildHierarchyForeachIDCbData &) = delete; - - Main &bmain; - SpaceOutliner &space_outliner; - ID &override_root_id; - - /* The tree element to expand. Changes with every level of recursion. */ - TreeElementID *parent_te; - /* The ancestor IDs leading to the current ID, to avoid IDs recursing into themselves. Changes - * with every level of recursion. */ - Set<ID *> parent_ids{}; - /* The IDs that were already added to #parent_te, to avoid duplicates. Entirely new set with - * every level of recursion. */ - Set<ID *> sibling_ids{}; -}; +void OverrideIDHierarchyBuilder::build_hierarchy_for_ID(ID &override_root_id, + TreeElement &te_to_expand) +{ + HierarchyBuildData build_data{override_root_id}; + build_hierarchy_for_ID_recursive(override_root_id, build_data, te_to_expand); +} + +/* Helpers (defined below). */ +static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations, + const ID &parent_id, + FunctionRef<void(ID &)> fn); +static bool id_is_in_override_hierarchy(const ID &id, + const ID &relationship_parent_id, + const ID &override_root_id); + +void OverrideIDHierarchyBuilder::build_hierarchy_for_ID_recursive(const ID &parent_id, + HierarchyBuildData &build_data, + TreeElement &te_to_expand) +{ + /* In case this isn't added to the parents yet (does nothing if already there). */ + build_data.parent_ids.add(&parent_id); + + foreach_natural_hierarchy_child(id_relations_, parent_id, [&](ID &id) { + if (!id_is_in_override_hierarchy(id, parent_id, build_data.override_root_id_)) { + return; + } + + /* Avoid endless recursion: If there is an ancestor for this ID already, it recurses into + * itself. */ + if (build_data.parent_ids.lookup_key_default(&id, nullptr)) { + return; + } + + /* Avoid duplicates: If there is a sibling for this ID already, the same ID is just used + * multiple times by the same parent. */ + if (build_data.sibling_ids.lookup_key_default(&id, nullptr)) { + return; + } + + TreeElement *new_te = outliner_add_element( + &space_outliner_, &te_to_expand.subtree, &id, &te_to_expand, TSE_SOME_ID, 0, false); + + build_data.sibling_ids.add(&id); + + /* Recurse into this ID. */ + HierarchyBuildData child_build_data{build_data.override_root_id_}; + child_build_data.parent_ids = build_data.parent_ids; + child_build_data.parent_ids.add(&id); + child_build_data.sibling_ids.reserve(10); + build_hierarchy_for_ID_recursive(id, child_build_data, *new_te); + }); +} -static int build_hierarchy_foreach_ID_cb(LibraryIDLinkCallbackData *cb_data) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Helpers for library override hierarchy building + * \{ */ + +/** + * Iterate over the IDs \a parent_id uses. E.g. the child collections and contained objects of a + * parent collection. Also does special handling for object parenting, so that: + * - When iterating over a child object, \a fn is executed for the parent instead. + * - When iterating over a parent object, \a fn is _additionally_ executed for all children. Given + * that the parent object isn't skipped, the caller has to ensure it's not added in the hierarchy + * twice. + * This allows us to build the hierarchy in the expected ("natural") order, where parent objects + * are actual parent elements in the hierarchy, even though in data, the relation goes the other + * way around (children point to or "use" the parent). + * + * Only handles regular object parenting, not cases like the "Child of" constraint. Other Outliner + * display modes don't show this as parent in the hierarchy either. + */ +static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations, + const ID &parent_id, + FunctionRef<void(ID &)> fn) { - if (!*cb_data->id_pointer) { - return IDWALK_RET_NOP; + const MainIDRelationsEntry *relations_of_id = static_cast<MainIDRelationsEntry *>( + BLI_ghash_lookup(id_relations.relations_from_pointers, &parent_id)); + + /* Iterate over all IDs used by the parent ID (e.g. the child-collections of a collection). */ + for (MainIDRelationsEntryItem *to_id_entry = relations_of_id->to_ids; to_id_entry; + to_id_entry = to_id_entry->next) { + /* An ID pointed to (used) by the ID to recurse into. */ + ID &target_id = **to_id_entry->id_pointer.to; + + /* Don't walk up the hierarchy, e.g. ignore pointers to parent collections. */ + if (to_id_entry->usage_flag & IDWALK_CB_LOOPBACK) { + continue; + } + + /* Special case for objects: Process the parent object instead of the child object. Below the + * parent will add the child objects then. */ + if (GS(target_id.name) == ID_OB) { + const Object &potential_child_ob = reinterpret_cast<const Object &>(target_id); + if (potential_child_ob.parent) { + fn(potential_child_ob.parent->id); + continue; + } + } + + fn(target_id); + } + + /* If the ID is an object, find and iterate over any child objects. */ + if (GS(parent_id.name) == ID_OB) { + for (MainIDRelationsEntryItem *from_id_entry = relations_of_id->from_ids; from_id_entry; + from_id_entry = from_id_entry->next) { + ID &potential_child_id = *from_id_entry->id_pointer.from; + + if (GS(potential_child_id.name) != ID_OB) { + continue; + } + + Object &potential_child_ob = reinterpret_cast<Object &>(potential_child_id); + if (potential_child_ob.parent && &potential_child_ob.parent->id == &parent_id) { + fn(potential_child_id); + } + } } +} - BuildHierarchyForeachIDCbData &build_data = *reinterpret_cast<BuildHierarchyForeachIDCbData *>( - cb_data->user_data); - /* Note that this may be an embedded ID (see #real_override_id). */ - ID &id = **cb_data->id_pointer; +static bool id_is_in_override_hierarchy(const ID &id, + const ID &relationship_parent_id, + const ID &override_root_id) +{ /* If #id is an embedded ID, this will be set to the owner, which is a real ID and contains the * override data. So queries of override data should be done via this, but the actual tree * element we add is the embedded ID. */ const ID *real_override_id = &id; if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(&id)) { + /* This assumes that the parent ID is always the owner of the 'embedded' one, I.e. that no + * other ID directly uses the embedded one. Should be true, but the debug code adds some checks + * to validate this assumption. */ + real_override_id = &relationship_parent_id; + +#ifndef NDEBUG if (GS(id.name) == ID_KE) { - Key *key = (Key *)&id; - real_override_id = key->from; + const Key *key = (Key *)&id; + BLI_assert(real_override_id == key->from); } - else if (id.flag & LIB_EMBEDDED_DATA) { - /* TODO Needs double-checking if this handles all embedded IDs correctly. */ - real_override_id = cb_data->id_owner; + else { + BLI_assert((id.flag & LIB_EMBEDDED_DATA) != 0); } +#endif } if (!ID_IS_OVERRIDE_LIBRARY(real_override_id)) { - return IDWALK_RET_NOP; + return false; } /* Is this ID part of the same override hierarchy? */ - if (real_override_id->override_library->hierarchy_root != &build_data.override_root_id) { - return IDWALK_RET_NOP; - } - - /* Avoid endless recursion: If there is an ancestor for this ID already, it recurses into itself. - */ - if (build_data.parent_ids.lookup_key_default(&id, nullptr)) { - return IDWALK_RET_NOP; - } - - /* Avoid duplicates: If there is a sibling for this ID already, the same ID is just used multiple - * times by the same parent. */ - if (build_data.sibling_ids.lookup_key_default(&id, nullptr)) { - return IDWALK_RET_NOP; + if (real_override_id->override_library->hierarchy_root != &override_root_id) { + return false; } - TreeElement *new_te = outliner_add_element(&build_data.space_outliner, - &build_data.parent_te->getLegacyElement().subtree, - &id, - &build_data.parent_te->getLegacyElement(), - TSE_SOME_ID, - 0, - false); - build_data.sibling_ids.add(&id); - - BuildHierarchyForeachIDCbData child_build_data{build_data.bmain, - build_data.space_outliner, - build_data.override_root_id, - tree_element_cast<TreeElementID>(new_te)}; - child_build_data.parent_ids = build_data.parent_ids; - child_build_data.parent_ids.add(&id); - child_build_data.sibling_ids.reserve(10); - BKE_library_foreach_ID_link( - &build_data.bmain, &id, build_hierarchy_foreach_ID_cb, &child_build_data, IDWALK_READONLY); - - return IDWALK_RET_NOP; + return true; } -void TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_ID(Main *bmain, - ID &override_root_id, - TreeElementID &te_id) const -{ - BuildHierarchyForeachIDCbData build_data{*bmain, space_outliner_, override_root_id, &te_id}; - build_data.parent_ids.add(&override_root_id); - - BKE_library_foreach_ID_link( - bmain, &te_id.get_ID(), build_hierarchy_foreach_ID_cb, &build_data, IDWALK_READONLY); -} +/** \} */ } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_topbar/space_topbar.c b/source/blender/editors/space_topbar/space_topbar.c index e9a9e690e60..bc68de1dfce 100644 --- a/source/blender/editors/space_topbar/space_topbar.c +++ b/source/blender/editors/space_topbar/space_topbar.c @@ -155,6 +155,9 @@ static void topbar_header_listener(const wmRegionListenerParams *params) ED_region_tag_redraw(region); } break; + case NC_WORKSPACE: + ED_region_tag_redraw(region); + break; case NC_SPACE: if (wmn->data == ND_SPACE_INFO) { ED_region_tag_redraw(region); diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 9ed2fec96db..a423a842019 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1300,6 +1300,10 @@ static void view3d_main_region_listener(const wmRegionListenerParams *params) ED_region_tag_redraw(region); } break; + case NC_WORKSPACE: + /* In case the region displays workspace settings. */ + ED_region_tag_redraw(region); + break; } } diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index d00826208be..753c41f7f1b 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -637,6 +637,7 @@ typedef struct UserDef_Experimental { /* Debug options, always available. */ char use_undo_legacy; char no_override_auto_resync; + char use_override_new_fully_editable; char use_cycles_debug; char show_asset_debug_info; char no_asset_indexing; @@ -654,6 +655,7 @@ typedef struct UserDef_Experimental { char enable_eevee_next; char use_sculpt_texture_paint; char use_draw_manager_acquire_lock; + char _pad[7]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index f7aaa1186db..5e741508603 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -239,6 +239,9 @@ typedef struct wmWindow { struct Scene *new_scene; /** Active view layer displayed in this window. */ char view_layer_name[64]; + /** The workspace may temporarily override the window's scene with scene pinning. This is the + * "overriden" or "default" scene to restore when entering a workspace with no scene pinned. */ + struct Scene *unpinned_scene; struct WorkSpaceInstanceHook *workspace_hook; diff --git a/source/blender/makesdna/DNA_workspace_types.h b/source/blender/makesdna/DNA_workspace_types.h index b8ed39bfd5d..a72689badb1 100644 --- a/source/blender/makesdna/DNA_workspace_types.h +++ b/source/blender/makesdna/DNA_workspace_types.h @@ -123,6 +123,10 @@ typedef struct WorkSpace { /** List of #bToolRef */ ListBase tools; + /** Optional, scene to switch to when enabling this workspace (NULL to disable). Cleared on + * link/append. */ + struct Scene *pin_scene; + char _pad[4]; int object_mode; @@ -195,6 +199,7 @@ typedef struct WorkSpaceInstanceHook { typedef enum eWorkSpaceFlags { WORKSPACE_USE_FILTER_BY_ORIGIN = (1 << 1), + WORKSPACE_USE_PIN_SCENE = (1 << 2), } eWorkSpaceFlags; #ifdef __cplusplus diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index a0b25cf60b2..cf4182243b9 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -2141,6 +2141,7 @@ void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop) void RNA_property_update_main(Main *bmain, Scene *scene, PointerRNA *ptr, PropertyRNA *prop) { + BLI_assert(bmain != NULL); rna_property_update(NULL, bmain, scene, ptr, prop); } diff --git a/source/blender/makesrna/intern/rna_meta.c b/source/blender/makesrna/intern/rna_meta.c index e6cf743e167..75188f29fac 100644 --- a/source/blender/makesrna/intern/rna_meta.c +++ b/source/blender/makesrna/intern/rna_meta.c @@ -81,18 +81,16 @@ static void rna_MetaBall_redraw_data(Main *UNUSED(bmain), Scene *UNUSED(scene), WM_main_add_notifier(NC_GEOM | ND_DATA, id); } -static void rna_MetaBall_update_data(Main *bmain, Scene *scene, PointerRNA *ptr) +static void rna_MetaBall_update_data(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { MetaBall *mb = (MetaBall *)ptr->owner_id; - Object *ob; - /* cheating way for importers to avoid slow updates */ + /* NOTE: The check on the number of users allows to avoid many repetitive (slow) updates in some + * cases, like e.g. importers. Calling `BKE_mball_properties_copy` on an obdata with no users + * would be meaningless anyway, as by definition it would not be used by any object, so not part + * of any meta-ball group. */ if (mb->id.us > 0) { - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - if (ob->data == mb) { - BKE_mball_properties_copy(scene, ob); - } - } + BKE_mball_properties_copy(bmain, mb); DEG_id_tag_update(&mb->id, 0); WM_main_add_notifier(NC_GEOM | ND_DATA, mb); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 40dc1254a7d..fd3479f6fe4 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6392,6 +6392,15 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) "Enable library overrides automatic resync detection and process on file load. Disable when " "dealing with older .blend files that need manual Resync (Enforce) handling"); + prop = RNA_def_property(srna, "use_override_new_fully_editable", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_override_new_fully_editable", 1); + RNA_def_property_ui_text( + prop, + "Override New Fully Editable", + "Make all override of a hierarchy fully user-editable by default when creating a new " + "override (if that option is disabled, most overrides created as part of a hierarchy will " + "not be editable by the user by default)"); + prop = RNA_def_property(srna, "use_new_point_cloud_type", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_new_point_cloud_type", 1); RNA_def_property_ui_text( diff --git a/source/blender/makesrna/intern/rna_workspace.c b/source/blender/makesrna/intern/rna_workspace.c index 0b6c3934985..a0d89b8b15a 100644 --- a/source/blender/makesrna/intern/rna_workspace.c +++ b/source/blender/makesrna/intern/rna_workspace.c @@ -413,6 +413,14 @@ static void rna_def_workspace(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Object Mode", "Switch to this object mode when activating the workspace"); + prop = RNA_def_property(srna, "use_pin_scene", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", WORKSPACE_USE_PIN_SCENE); + RNA_def_property_ui_text(prop, + "Pin Scene", + "Remember the last used scene for the workspace and switch to it " + "whenever this workspace is activated again"); + RNA_def_property_update(prop, NC_WORKSPACE, NULL); + /* Flags */ prop = RNA_def_property(srna, "use_filter_by_owner", PROP_BOOLEAN, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index aebd62ee91b..32bc2f96365 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -323,34 +323,35 @@ typedef struct wmNotifier { #define NOTE_CATEGORY 0xFF000000 #define NC_WM (1 << 24) #define NC_WINDOW (2 << 24) -#define NC_SCREEN (3 << 24) -#define NC_SCENE (4 << 24) -#define NC_OBJECT (5 << 24) -#define NC_MATERIAL (6 << 24) -#define NC_TEXTURE (7 << 24) -#define NC_LAMP (8 << 24) -#define NC_GROUP (9 << 24) -#define NC_IMAGE (10 << 24) -#define NC_BRUSH (11 << 24) -#define NC_TEXT (12 << 24) -#define NC_WORLD (13 << 24) -#define NC_ANIMATION (14 << 24) +#define NC_WORKSPACE (3 << 24) +#define NC_SCREEN (4 << 24) +#define NC_SCENE (5 << 24) +#define NC_OBJECT (6 << 24) +#define NC_MATERIAL (7 << 24) +#define NC_TEXTURE (8 << 24) +#define NC_LAMP (9 << 24) +#define NC_GROUP (10 << 24) +#define NC_IMAGE (11 << 24) +#define NC_BRUSH (12 << 24) +#define NC_TEXT (13 << 24) +#define NC_WORLD (14 << 24) +#define NC_ANIMATION (15 << 24) /* When passing a space as reference data with this (e.g. `WM_event_add_notifier(..., space)`), * the notifier will only be sent to this space. That avoids unnecessary updates for unrelated * spaces. */ -#define NC_SPACE (15 << 24) -#define NC_GEOM (16 << 24) -#define NC_NODE (17 << 24) -#define NC_ID (18 << 24) -#define NC_PAINTCURVE (19 << 24) -#define NC_MOVIECLIP (20 << 24) -#define NC_MASK (21 << 24) -#define NC_GPENCIL (22 << 24) -#define NC_LINESTYLE (23 << 24) -#define NC_CAMERA (24 << 24) -#define NC_LIGHTPROBE (25 << 24) +#define NC_SPACE (16 << 24) +#define NC_GEOM (17 << 24) +#define NC_NODE (18 << 24) +#define NC_ID (19 << 24) +#define NC_PAINTCURVE (20 << 24) +#define NC_MOVIECLIP (21 << 24) +#define NC_MASK (22 << 24) +#define NC_GPENCIL (23 << 24) +#define NC_LINESTYLE (24 << 24) +#define NC_CAMERA (25 << 24) +#define NC_LIGHTPROBE (26 << 24) /* Changes to asset data in the current .blend. */ -#define NC_ASSET (26 << 24) +#define NC_ASSET (27 << 24) /* data type, 256 entries is enough, it can overlap */ #define NOTE_DATA 0x00FF0000 diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 40d9b0b9a35..32cec8f779c 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -81,6 +81,8 @@ static void window_manager_foreach_id(ID *id, LibraryForeachIDData *data) if (BKE_lib_query_foreachid_iter_stop(data)) { return; } + + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, win->unpinned_scene, IDWALK_CB_NOP); } if (BKE_lib_query_foreachid_process_flags_get(data) & IDWALK_INCLUDE_UI) { @@ -224,6 +226,7 @@ static void lib_link_workspace_instance_hook(BlendLibReader *reader, { WorkSpace *workspace = BKE_workspace_active_get(hook); BLO_read_id_address(reader, id->lib, &workspace); + BKE_workspace_active_set(hook, workspace); } @@ -239,6 +242,11 @@ static void window_manager_blend_read_lib(BlendLibReader *reader, ID *id) /* deprecated, but needed for versioning (will be NULL'ed then) */ BLO_read_id_address(reader, NULL, &win->screen); + /* The unpinned scene is a UI->Scene-data pointer, and should be NULL'ed on linking (like + * WorkSpace.pin_scene). But the WindowManager ID (owning the window) is never linked. */ + BLI_assert(!ID_IS_LINKED(id)); + BLO_read_id_address(reader, id->lib, &win->unpinned_scene); + LISTBASE_FOREACH (ScrArea *, area, &win->global_areas.areabase) { BKE_screen_area_blend_read_lib(reader, &wm->id, area); } |