Anim: bone collections, split ARMATURE_OT_collection_assign in two

Split the `armature.collection_assign` operator in two. Before, the
operator could do two things: assign selected bones to the active/named
bone collection, or create a new bone collection with the given name.

This is now split up, where `armature.collection_assign` only assigns to
existing bone collections, and `armature.collection_create_and_assign`
always creates a new bone collection.

This makes the purpose of each operator clearer & more predictable.
This commit is contained in:
Sybren A. Stüvel
2023-12-18 12:53:57 +01:00
parent e87b67398c
commit b70ebf96e7
4 changed files with 101 additions and 29 deletions

View File

@@ -4117,7 +4117,7 @@ class VIEW3D_MT_bone_collections(Menu):
layout.separator()
props = layout.operator("armature.collection_assign",
props = layout.operator("armature.collection_create_and_assign",
text="Assign to New Collection")
props.name = "New Collection"

View File

@@ -74,6 +74,7 @@ void ARMATURE_OT_collection_add(struct wmOperatorType *ot);
void ARMATURE_OT_collection_remove(struct wmOperatorType *ot);
void ARMATURE_OT_collection_move(struct wmOperatorType *ot);
void ARMATURE_OT_collection_assign(struct wmOperatorType *ot);
void ARMATURE_OT_collection_create_and_assign(struct wmOperatorType *ot);
void ARMATURE_OT_collection_unassign(struct wmOperatorType *ot);
void ARMATURE_OT_collection_unassign_named(struct wmOperatorType *ot);
void ARMATURE_OT_collection_select(struct wmOperatorType *ot);

View File

@@ -63,6 +63,7 @@ void ED_operatortypes_armature()
WM_operatortype_append(ARMATURE_OT_collection_remove);
WM_operatortype_append(ARMATURE_OT_collection_move);
WM_operatortype_append(ARMATURE_OT_collection_assign);
WM_operatortype_append(ARMATURE_OT_collection_create_and_assign);
WM_operatortype_append(ARMATURE_OT_collection_unassign);
WM_operatortype_append(ARMATURE_OT_collection_unassign_named);
WM_operatortype_append(ARMATURE_OT_collection_select);

View File

@@ -207,15 +207,7 @@ void ARMATURE_OT_collection_move(wmOperatorType *ot)
"Direction to move the active Bone Collection towards");
}
typedef enum eMayCreate {
FAIL_IF_MISSING = 0,
CREATE_IF_MISSING = 1,
} eMayCreate;
static BoneCollection *get_bonecoll_named_or_active(bContext * /*C*/,
wmOperator *op,
Object *ob,
const eMayCreate may_create)
static BoneCollection *get_bonecoll_named_or_active(bContext * /*C*/, wmOperator *op, Object *ob)
{
bArmature *armature = static_cast<bArmature *>(ob->data);
@@ -227,21 +219,12 @@ static BoneCollection *get_bonecoll_named_or_active(bContext * /*C*/,
}
BoneCollection *bcoll = ANIM_armature_bonecoll_get_by_name(armature, bcoll_name);
if (bcoll) {
return bcoll;
if (!bcoll) {
WM_reportf(RPT_ERROR, "No bone collection named '%s'", bcoll_name);
return nullptr;
}
switch (may_create) {
case CREATE_IF_MISSING:
bcoll = ANIM_armature_bonecoll_new(armature, bcoll_name);
ANIM_armature_bonecoll_active_set(armature, bcoll);
return bcoll;
case FAIL_IF_MISSING:
WM_reportf(RPT_ERROR, "No bone collection named '%s'", bcoll_name);
return nullptr;
}
return nullptr;
return bcoll;
}
using assign_bone_func = bool (*)(BoneCollection *bcoll, Bone *bone);
@@ -417,10 +400,7 @@ static int bone_collection_assign_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
/* NOTE: this operator can be called through the M/Shift+M shortcuts, which
* allow assigning to a newly-created bone collection. */
BoneCollection *bcoll = get_bonecoll_named_or_active(C, op, ob, CREATE_IF_MISSING);
BLI_assert_msg(bcoll, "Bone Collection should always be created");
BoneCollection *bcoll = get_bonecoll_named_or_active(C, op, ob);
if (bcoll == nullptr) {
return OPERATOR_CANCELLED;
}
@@ -485,6 +465,96 @@ void ARMATURE_OT_collection_assign(wmOperatorType *ot)
"active bone collection");
}
static bool bone_collection_create_and_assign_poll(bContext *C)
{
Object *ob = ED_object_context(C);
if (ob == 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<bArmature *>(ob->data);
if (ID_IS_LINKED(armature) && !ID_IS_OVERRIDE_LIBRARY(armature)) {
CTX_wm_operator_poll_msg_set(
C, "Cannot edit bone collections on linked Armatures without override");
return false;
}
return true;
}
/* Assign selected pchans to the bone collection that the user selects */
static int bone_collection_create_and_assign_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
if (ob == nullptr) {
return OPERATOR_CANCELLED;
}
bArmature *armature = static_cast<bArmature *>(ob->data);
char bcoll_name[MAX_NAME];
RNA_string_get(op->ptr, "name", bcoll_name);
/* Note that this bone collection can be removed later on, if the assignment part of this
* operation failed. */
BoneCollection *bcoll = ANIM_armature_bonecoll_new(armature, bcoll_name);
bool made_any_changes = false;
bool had_bones_to_assign = false;
const bool mode_is_supported = bone_collection_assign_mode_specific(
C,
ob,
bcoll,
ANIM_armature_bonecoll_assign,
ANIM_armature_bonecoll_assign_editbone,
&made_any_changes,
&had_bones_to_assign);
if (!mode_is_supported) {
WM_report(RPT_ERROR, "This operator only works in pose mode and armature edit mode");
ANIM_armature_bonecoll_remove(armature, bcoll);
return OPERATOR_CANCELLED;
}
if (!had_bones_to_assign) {
WM_report(RPT_WARNING, "No bones selected, nothing to assign to bone collection");
return OPERATOR_FINISHED;
}
/* Not checking for `made_any_changes`, as if there were any bones to assign, they never could
* have already been assigned to this brand new bone collection. */
ANIM_armature_bonecoll_active_set(armature, bcoll);
WM_main_add_notifier(NC_OBJECT | ND_BONE_COLLECTION, &ob->id);
return OPERATOR_FINISHED;
}
void ARMATURE_OT_collection_create_and_assign(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Selected Bones to New Collection";
ot->idname = "ARMATURE_OT_collection_create_and_assign";
ot->description = "Create a new bone collection and assign all selected bones";
/* api callbacks */
ot->exec = bone_collection_create_and_assign_exec;
ot->poll = bone_collection_create_and_assign_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_string(ot->srna,
"name",
nullptr,
MAX_NAME,
"Bone Collection",
"Name of the bone collection to create");
}
static int bone_collection_unassign_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
@@ -492,7 +562,7 @@ static int bone_collection_unassign_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
BoneCollection *bcoll = get_bonecoll_named_or_active(C, op, ob, FAIL_IF_MISSING);
BoneCollection *bcoll = get_bonecoll_named_or_active(C, op, ob);
if (bcoll == nullptr) {
return OPERATOR_CANCELLED;
}
@@ -553,7 +623,7 @@ static int bone_collection_unassign_named_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
BoneCollection *bcoll = get_bonecoll_named_or_active(C, op, ob, FAIL_IF_MISSING);
BoneCollection *bcoll = get_bonecoll_named_or_active(C, op, ob);
if (bcoll == nullptr) {
return OPERATOR_CANCELLED;
}