Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjon denning <gfxcoder@gmail.com>2022-07-07 21:51:22 +0300
committerjon denning <gfxcoder@gmail.com>2022-07-07 21:51:22 +0300
commit7502bc583c6dfb39dd9e548f47c8e427cfaec6d0 (patch)
tree53914cd88ddaefc83ad7f4d0b25ba5f4e01bb097
parent089870ab3a8fdf40139a6737c25dc0662fca24f5 (diff)
parent52fa0c4251e7c92b2df665e9e3c150a54066389f (diff)
Merge branch 'master' into transform_api
-rw-r--r--release/scripts/startup/bl_ui/properties_workspace.py2
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py1
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py2
-rw-r--r--source/blender/blenkernel/BKE_mball.h22
-rw-r--r--source/blender/blenkernel/intern/mball.c118
-rw-r--r--source/blender/blenkernel/intern/workspace.c11
-rw-r--r--source/blender/blenlib/BLI_generic_span.hh66
-rw-r--r--source/blender/blenlib/BLI_generic_virtual_array.hh4
-rw-r--r--source/blender/blenlib/BLI_virtual_array.hh23
-rw-r--r--source/blender/blenlib/intern/generic_virtual_array.cc32
-rw-r--r--source/blender/blenlib/tests/BLI_virtual_array_test.cc33
-rw-r--r--source/blender/blenloader/intern/readfile.c3
-rw-r--r--source/blender/editors/interface/interface_templates.c59
-rw-r--r--source/blender/editors/object/object_relations.c9
-rw-r--r--source/blender/editors/screen/workspace_edit.c107
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c3
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.cc26
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display.hh1
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc264
-rw-r--r--source/blender/editors/space_topbar/space_topbar.c3
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c4
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h2
-rw-r--r--source/blender/makesdna/DNA_windowmanager_types.h3
-rw-r--r--source/blender/makesdna/DNA_workspace_types.h5
-rw-r--r--source/blender/makesrna/intern/rna_access.c1
-rw-r--r--source/blender/makesrna/intern/rna_meta.c14
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c9
-rw-r--r--source/blender/makesrna/intern/rna_workspace.c8
-rw-r--r--source/blender/windowmanager/WM_types.h49
-rw-r--r--source/blender/windowmanager/intern/wm.c8
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);
}