diff --git a/scripts/startup/bl_operators/anim.py b/scripts/startup/bl_operators/anim.py index 6b97f41a96a..b4a24e5324c 100644 --- a/scripts/startup/bl_operators/anim.py +++ b/scripts/startup/bl_operators/anim.py @@ -529,6 +529,15 @@ class ARMATURE_OT_copy_bone_color_to_selected(Operator): return {'FINISHED'} +def _armature_from_context(context): + pin_armature = getattr(context, 'armature', None) + if pin_armature: + return pin_armature + if context.object and context.object.type == 'ARMATURE': + return context.object.data + return None + + class ARMATURE_OT_collection_show_all(Operator): """Show all bone collections""" bl_idname = "armature.collection_show_all" @@ -537,10 +546,10 @@ class ARMATURE_OT_collection_show_all(Operator): @classmethod def poll(cls, context): - return context.object and context.object.type == 'ARMATURE' and context.object.data + return _armature_from_context(context) is not None def execute(self, context): - arm = context.object.data + arm = _armature_from_context(context) for bcoll in arm.collections_all: bcoll.is_visible = True return {'FINISHED'} @@ -554,15 +563,16 @@ class ARMATURE_OT_collection_unsolo_all(Operator): @classmethod def poll(cls, context): - if not (context.object and context.object.type == 'ARMATURE' and context.object.data): + armature = _armature_from_context(context) + if not armature: return False - if not context.object.data.collections.is_solo_active: + if not armature.collections.is_solo_active: cls.poll_message_set("None of the bone collections is marked 'solo'") return False return True def execute(self, context): - arm = context.object.data + arm = _armature_from_context(context) for bcoll in arm.collections_all: bcoll.is_solo = False return {'FINISHED'} @@ -578,16 +588,16 @@ class ARMATURE_OT_collection_remove_unused(Operator): @classmethod def poll(cls, context): - if not context.object or context.object.type != 'ARMATURE': + armature = _armature_from_context(context) + if not armature: return False - arm = context.object.data - return len(arm.collections) > 0 + return len(armature.collections) > 0 def execute(self, context): - if context.object.mode == 'EDIT': + if context.mode == 'EDIT_ARMATURE': return self.execute_edit_mode(context) - armature = context.object.data + armature = _armature_from_context(context) # Build a set of bone collections that don't contain any bones, and # whose children also don't contain any bones. @@ -608,7 +618,7 @@ class ARMATURE_OT_collection_remove_unused(Operator): # edit mode, because that has a completely separate list of edit bones. # This is why edit mode needs separate handling. - armature = context.object.data + armature = _armature_from_context(context) bcolls_with_bones = { bcoll for ebone in armature.edit_bones diff --git a/source/blender/editors/armature/armature_edit.cc b/source/blender/editors/armature/armature_edit.cc index 1469f09a8fc..85f1bae380f 100644 --- a/source/blender/editors/armature/armature_edit.cc +++ b/source/blender/editors/armature/armature_edit.cc @@ -35,6 +35,7 @@ #include "RNA_access.hh" #include "RNA_define.hh" +#include "RNA_prototypes.h" #include "UI_interface_icons.hh" @@ -42,6 +43,7 @@ #include "WM_types.hh" #include "ED_armature.hh" +#include "ED_object.hh" #include "ED_outliner.hh" #include "ED_screen.hh" #include "ED_view3d.hh" @@ -58,6 +60,21 @@ using blender::Vector; /** \name Object Tools Public API * \{ */ +bArmature *ED_armature_context(const bContext *C) +{ + bArmature *armature = static_cast( + CTX_data_pointer_get_type(C, "armature", &RNA_Armature).data); + + if (armature == nullptr) { + Object *object = ED_object_active_context(C); + if (object && object->type == OB_ARMATURE) { + armature = static_cast(object->data); + } + } + + return armature; +} + /* NOTE: these functions are exported to the Object module to be called from the tools there */ void ED_armature_edit_transform(bArmature *arm, const float mat[4][4], const bool do_props) diff --git a/source/blender/editors/armature/bone_collections.cc b/source/blender/editors/armature/bone_collections.cc index 761eca63af3..b2bfc325cba 100644 --- a/source/blender/editors/armature/bone_collections.cc +++ b/source/blender/editors/armature/bone_collections.cc @@ -45,23 +45,18 @@ struct wmOperator; static bool bone_collection_add_poll(bContext *C) { - Object *ob = ED_object_context(C); - if (ob == nullptr) { + bArmature *armature = ED_armature_context(C); + if (armature == nullptr) { return false; } - if (ob->type != OB_ARMATURE) { - CTX_wm_operator_poll_msg_set(C, "Bone collections can only be added to an Armature"); - return false; - } - - if (ID_IS_LINKED(ob->data)) { + if (ID_IS_LINKED(&armature->id)) { CTX_wm_operator_poll_msg_set( C, "Cannot add bone collections to a linked Armature without an override"); return false; } - if (BKE_lib_override_library_is_system_defined(nullptr, reinterpret_cast(ob->data))) { + if (BKE_lib_override_library_is_system_defined(nullptr, &armature->id)) { CTX_wm_operator_poll_msg_set(C, "Cannot add bone collections to a linked Armature with a system " "override; explicitly create an override on the Armature"); @@ -74,17 +69,11 @@ static bool bone_collection_add_poll(bContext *C) /** Allow edits of local bone collection only (full local or local override). */ static bool active_bone_collection_poll(bContext *C) { - Object *ob = ED_object_context(C); - if (ob == nullptr) { + bArmature *armature = ED_armature_context(C); + if (armature == nullptr) { return false; } - if (ob->type != OB_ARMATURE) { - CTX_wm_operator_poll_msg_set(C, "Bone collections can only be edited on an Armature"); - return false; - } - - bArmature *armature = static_cast(ob->data); if (BKE_lib_override_library_is_system_defined(nullptr, &armature->id)) { CTX_wm_operator_poll_msg_set(C, "Cannot update a linked Armature with a system override; " @@ -110,12 +99,7 @@ static int bone_collection_add_exec(bContext *C, wmOperator * /*op*/) { using namespace blender::animrig; - Object *ob = ED_object_context(C); - if (ob == nullptr) { - return OPERATOR_CANCELLED; - } - - bArmature *armature = static_cast(ob->data); + bArmature *armature = ED_armature_context(C); /* If there is an active bone collection, create the new one as a sibling. */ const int parent_index = armature_bonecoll_find_parent_index( @@ -132,7 +116,7 @@ static int bone_collection_add_exec(bContext *C, wmOperator * /*op*/) ANIM_armature_bonecoll_active_set(armature, bcoll); /* TODO: ensure the ancestors of the new bone collection are all expanded. */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr); return OPERATOR_FINISHED; } @@ -153,17 +137,12 @@ void ARMATURE_OT_collection_add(wmOperatorType *ot) static int bone_collection_remove_exec(bContext *C, wmOperator * /*op*/) { - Object *ob = ED_object_context(C); - if (ob == nullptr) { - return OPERATOR_CANCELLED; - } - /* The poll function ensures armature->active_collection is not NULL. */ - bArmature *armature = static_cast(ob->data); + bArmature *armature = ED_armature_context(C); ANIM_armature_bonecoll_remove(armature, armature->runtime.active_collection); /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr); DEG_id_tag_update(&armature->id, ID_RECALC_SELECT); return OPERATOR_FINISHED; @@ -186,14 +165,10 @@ void ARMATURE_OT_collection_remove(wmOperatorType *ot) static int bone_collection_move_exec(bContext *C, wmOperator *op) { - Object *ob = ED_object_context(C); - if (ob == nullptr) { - return OPERATOR_CANCELLED; - } const int direction = RNA_enum_get(op->ptr, "direction"); /* Poll function makes sure this is valid. */ - bArmature *armature = static_cast(ob->data); + bArmature *armature = ED_armature_context(C); const bool ok = ANIM_armature_bonecoll_move( armature, armature->runtime.active_collection, direction); @@ -203,7 +178,7 @@ static int bone_collection_move_exec(bContext *C, wmOperator *op) ANIM_armature_bonecoll_active_runtime_refresh(armature); - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_COLLECTION, ob); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_COLLECTION, nullptr); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/armature/pose_edit.cc b/source/blender/editors/armature/pose_edit.cc index 4275b488c6e..1d0f760d7b6 100644 --- a/source/blender/editors/armature/pose_edit.cc +++ b/source/blender/editors/armature/pose_edit.cc @@ -66,7 +66,7 @@ Object *ED_pose_object_from_context(bContext *C) /* Since this call may also be used from the buttons window, * we need to check for where to get the object. */ if (area && area->spacetype == SPACE_PROPERTIES) { - ob = ED_object_context(C); + ob = ED_object_active_context(C); } else { ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); diff --git a/source/blender/editors/include/ED_armature.hh b/source/blender/editors/include/ED_armature.hh index b7b5f699b5b..ffdaf5ae438 100644 --- a/source/blender/editors/include/ED_armature.hh +++ b/source/blender/editors/include/ED_armature.hh @@ -65,7 +65,10 @@ EditBone *ED_armature_ebone_add_primitive(Object *obedit_arm, float length, bool void ED_armature_ebone_copy(EditBone *dest, const EditBone *source); -/* `armature_edit.cc` */ +/** + * Get current armature from the context, including properties editor pinning. + **/ +bArmature *ED_armature_context(const bContext *C); /** * Adjust bone roll to align Z axis with vector `align_axis` is in local space and is normalized. diff --git a/source/blender/editors/interface/interface_template_bone_collection_tree.cc b/source/blender/editors/interface/interface_template_bone_collection_tree.cc index 4e168e1a4c4..bda7d1cf8ee 100644 --- a/source/blender/editors/interface/interface_template_bone_collection_tree.cc +++ b/source/blender/editors/interface/interface_template_bone_collection_tree.cc @@ -19,6 +19,7 @@ #include "RNA_access.hh" #include "RNA_prototypes.h" +#include "ED_armature.hh" #include "ED_undo.hh" #include "WM_api.hh" @@ -460,20 +461,18 @@ void uiTemplateBoneCollectionTree(uiLayout *layout, bContext *C) { using namespace blender; - Object *object = CTX_data_active_object(C); - if (!object || object->type != OB_ARMATURE) { + bArmature *armature = ED_armature_context(C); + if (armature == nullptr) { return; } - - bArmature *arm = static_cast(object->data); - BLI_assert(GS(arm->id.name) == ID_AR); + BLI_assert(GS(armature->id.name) == ID_AR); uiBlock *block = uiLayoutGetBlock(layout); ui::AbstractTreeView *tree_view = UI_block_add_view( *block, "Bone Collection Tree View", - std::make_unique(*arm)); + std::make_unique(*armature)); tree_view->set_min_rows(3); ui::TreeViewBuilder::build_tree_view(*tree_view, *layout); diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 6b511aefdbb..6fea1b50c30 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -292,11 +292,12 @@ static int dyntopo_warning_popup(bContext *C, wmOperatorType *ot, enum WarnFlag static bool dyntopo_supports_layer(const CustomDataLayer &layer) { + if (layer.type == CD_PROP_FLOAT && STREQ(layer.name, ".sculpt_mask")) { + return true; + } if (CD_TYPE_AS_MASK(layer.type) & CD_MASK_PROP_ALL) { - /* Some data is stored as generic attributes on #Mesh but in flags or fields on #BMesh. */ return BM_attribute_stored_in_bmesh_builtin(layer.name); } - /* Some layers just encode #Mesh topology or are handled as special cases for dyntopo. */ return ELEM(layer.type, CD_ORIGINDEX); }