Add Extras Dropdown Menu to Constraints

Add Apply Constraint, Duplicate Constraint, and Copy To Selected
operators, and include them in a menu similar to the menu for modifiers.
The shortcuts in the extras menu are also matched to modifiers.

All the here added operators are intended to work exactly like the
analogous ones for modifiers. That means the apply operator should apply
a constraint as if it was first in the list, just like modifiers do. I
have added the same warning message as for modifiers when that happens.

The decision to use this approach of appling the constraint as if it was
first, was made for consistency with modifiers. People are already used
to how it works there. Is also provides more intricate control over the
applied transforms, then just applying all constraints up to that one.
Apply all constraints is already kinda implemented in Bake Animation.

Reviewed By: HooglyBoogly, sybren, #user_interface

Differential Revision: https://developer.blender.org/D10914
This commit is contained in:
Henrik Dick 2021-08-12 14:22:19 +02:00 committed by Sybren A. Stüvel
parent 215734bc52
commit d6891d9bee
Notes: blender-bot 2024-01-31 11:35:08 +01:00
Referenced by commit 1b1e947162, Fix T94600: Apply single shrinkwrap constraint fails
Referenced by issue #96032, Wrongly applying constraint make crashing Blender
Referenced by issue #93987, Disable Constraint Drivers Not Visible When Opening Older Files in 3.0
10 changed files with 543 additions and 20 deletions

View File

@ -775,6 +775,8 @@ def km_property_editor(_params):
# Constraint panels
("constraint.delete", {"type": 'X', "value": 'PRESS'}, {"properties": [("report", True)]}),
("constraint.delete", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}),
("constraint.copy", {"type": 'D', "value": 'PRESS', "shift": True}, None),
("constraint.apply", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("report", True)]}),
])
return keymap

View File

@ -465,6 +465,7 @@ def km_property_editor(params):
# Constraint panels
("constraint.delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, {"properties": [("report", True)]}),
("constraint.delete", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}),
("constraint.copy", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
])
return keymap

View File

@ -192,6 +192,28 @@ bool BKE_constraint_remove_ex(ListBase *list,
bool clear_dep);
bool BKE_constraint_remove(ListBase *list, struct bConstraint *con);
bool BKE_constraint_apply_for_object(struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *ob,
struct bConstraint *con);
bool BKE_constraint_apply_and_remove_for_object(struct Depsgraph *depsgraph,
struct Scene *scene,
ListBase /*bConstraint*/ *constraints,
struct Object *ob,
struct bConstraint *con);
bool BKE_constraint_apply_for_pose(struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *ob,
struct bPoseChannel *pchan,
struct bConstraint *con);
bool BKE_constraint_apply_and_remove_for_pose(struct Depsgraph *depsgraph,
struct Scene *scene,
ListBase /*bConstraint*/ *constraints,
struct Object *ob,
struct bConstraint *con,
struct bPoseChannel *pchan);
void BKE_constraint_panel_expand(struct bConstraint *con);
/* Constraints + Proxies function prototypes */

View File

@ -5677,6 +5677,111 @@ bool BKE_constraint_remove_ex(ListBase *list, Object *ob, bConstraint *con, bool
return false;
}
/* Apply the specified constraint in the given constraint stack */
bool BKE_constraint_apply_for_object(Depsgraph *depsgraph,
Scene *scene,
Object *ob,
bConstraint *con)
{
if (!con) {
return false;
}
const float ctime = BKE_scene_frame_get(scene);
bConstraint *new_con = BKE_constraint_duplicate_ex(con, 0, !ID_IS_LINKED(ob));
ListBase single_con = {new_con, new_con};
bConstraintOb *cob = BKE_constraints_make_evalob(
depsgraph, scene, ob, NULL, CONSTRAINT_OBTYPE_OBJECT);
/* Undo the effect of the current constraint stack evaluation. */
mul_m4_m4m4(cob->matrix, ob->constinv, cob->matrix);
/* Evaluate single constraint. */
BKE_constraints_solve(depsgraph, &single_con, cob, ctime);
/* Copy transforms back. This will leave the object in a bad state
* as ob->constinv will be wrong until next evaluation. */
BKE_constraints_clear_evalob(cob);
/* Free the copied constraint. */
BKE_constraint_free_data(new_con);
BLI_freelinkN(&single_con, new_con);
/* Apply transform from matrix. */
BKE_object_apply_mat4(ob, ob->obmat, true, true);
return true;
}
bool BKE_constraint_apply_and_remove_for_object(Depsgraph *depsgraph,
Scene *scene,
ListBase /*bConstraint*/ *constraints,
Object *ob,
bConstraint *con)
{
if (!BKE_constraint_apply_for_object(depsgraph, scene, ob, con)) {
return false;
}
return BKE_constraint_remove_ex(constraints, ob, con, true);
}
bool BKE_constraint_apply_for_pose(
Depsgraph *depsgraph, Scene *scene, Object *ob, bPoseChannel *pchan, bConstraint *con)
{
if (!con) {
return false;
}
const float ctime = BKE_scene_frame_get(scene);
bConstraint *new_con = BKE_constraint_duplicate_ex(con, 0, !ID_IS_LINKED(ob));
ListBase single_con;
single_con.first = new_con;
single_con.last = new_con;
float vec[3];
copy_v3_v3(vec, pchan->pose_mat[3]);
bConstraintOb *cob = BKE_constraints_make_evalob(
depsgraph, scene, ob, pchan, CONSTRAINT_OBTYPE_BONE);
/* Undo the effects of currently applied constraints. */
mul_m4_m4m4(cob->matrix, pchan->constinv, cob->matrix);
/* Evaluate single constraint. */
BKE_constraints_solve(depsgraph, &single_con, cob, ctime);
BKE_constraints_clear_evalob(cob);
/* Free the copied constraint. */
BKE_constraint_free_data(new_con);
BLI_freelinkN(&single_con, new_con);
/* Prevent constraints breaking a chain. */
if (pchan->bone->flag & BONE_CONNECTED) {
copy_v3_v3(pchan->pose_mat[3], vec);
}
/* Apply transform from matrix. */
float mat[4][4];
BKE_armature_mat_pose_to_bone(pchan, pchan->pose_mat, mat);
BKE_pchan_apply_mat4(pchan, mat, true);
return true;
}
bool BKE_constraint_apply_and_remove_for_pose(Depsgraph *depsgraph,
Scene *scene,
ListBase /*bConstraint*/ *constraints,
Object *ob,
bConstraint *con,
bPoseChannel *pchan)
{
if (!BKE_constraint_apply_for_pose(depsgraph, scene, ob, pchan, con)) {
return false;
}
return BKE_constraint_remove_ex(constraints, ob, con, true);
}
void BKE_constraint_panel_expand(bConstraint *con)
{
con->ui_expand_flag |= UI_PANEL_DATA_EXPAND_ROOT;

View File

@ -1357,7 +1357,8 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb,
}
}
}
BKE_constraints_clear_evalob(cob);
/* NOTE: Don't use BKE_constraints_clear_evalob here as that will reset ob->constinv. */
MEM_freeN(cob);
}
}

View File

@ -2621,6 +2621,72 @@ static void constraint_active_func(bContext *UNUSED(C), void *ob_v, void *con_v)
ED_object_constraint_active_set(ob_v, con_v);
}
static void constraint_ops_extra_draw(bContext *C, uiLayout *layout, void *con_v)
{
PointerRNA op_ptr;
uiLayout *row;
bConstraint *con = (bConstraint *)con_v;
PointerRNA ptr;
Object *ob = ED_object_active_context(C);
RNA_pointer_create(&ob->id, &RNA_Constraint, con, &ptr);
uiLayoutSetContextPointer(layout, "constraint", &ptr);
uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
uiLayoutSetUnitsX(layout, 4.0f);
/* Apply. */
uiItemO(layout,
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"),
ICON_CHECKMARK,
"CONSTRAINT_OT_apply");
/* Duplicate. */
uiItemO(layout,
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Duplicate"),
ICON_DUPLICATE,
"CONSTRAINT_OT_copy");
uiItemO(layout,
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy to Selected"),
0,
"CONSTRAINT_OT_copy_to_selected");
uiItemS(layout);
/* Move to first. */
row = uiLayoutColumn(layout, false);
uiItemFullO(row,
"CONSTRAINT_OT_move_to_index",
IFACE_("Move to First"),
ICON_TRIA_UP,
NULL,
WM_OP_INVOKE_DEFAULT,
0,
&op_ptr);
RNA_int_set(&op_ptr, "index", 0);
if (!con->prev) {
uiLayoutSetEnabled(row, false);
}
/* Move to last. */
row = uiLayoutColumn(layout, false);
uiItemFullO(row,
"CONSTRAINT_OT_move_to_index",
IFACE_("Move to Last"),
ICON_TRIA_DOWN,
NULL,
WM_OP_INVOKE_DEFAULT,
0,
&op_ptr);
ListBase *constraint_list = ED_object_constraint_list_from_constraint(ob, con, NULL);
RNA_int_set(&op_ptr, "index", BLI_listbase_count(constraint_list) - 1);
if (!con->next) {
uiLayoutSetEnabled(row, false);
}
}
static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *con)
{
bPoseChannel *pchan = BKE_pose_channel_active(ob);
@ -2652,11 +2718,13 @@ static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *co
UI_block_emboss_set(block, UI_EMBOSS);
uiLayout *row = uiLayoutRow(layout, true);
if (proxy_protected == 0) {
uiItemR(layout, &ptr, "name", 0, "", ICON_NONE);
uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
}
else {
uiItemL(layout, con->name, ICON_NONE);
uiItemL(row, con->name, ICON_NONE);
}
/* proxy-protected constraints cannot be edited, so hide up/down + close buttons */
@ -2697,22 +2765,22 @@ static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *co
UI_block_emboss_set(block, UI_EMBOSS);
}
else {
/* enabled */
UI_block_emboss_set(block, UI_EMBOSS_NONE_OR_STATUS);
uiItemR(layout, &ptr, "mute", 0, "", 0);
UI_block_emboss_set(block, UI_EMBOSS);
/* Enabled eye icon. */
uiItemR(row, &ptr, "enabled", 0, "", ICON_NONE);
uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
/* Extra operators menu. */
uiItemMenuF(row, "", ICON_DOWNARROW_HLT, constraint_ops_extra_draw, con);
/* Close 'button' - emboss calls here disable drawing of 'button' behind X */
UI_block_emboss_set(block, UI_EMBOSS_NONE);
uiItemO(layout, "", ICON_X, "CONSTRAINT_OT_delete");
UI_block_emboss_set(block, UI_EMBOSS);
/* Some extra padding at the end, so the 'x' icon isn't too close to drag button. */
uiItemS(layout);
sub = uiLayoutRow(row, false);
uiLayoutSetEmboss(sub, UI_EMBOSS_NONE);
uiLayoutSetOperatorContext(sub, WM_OP_INVOKE_DEFAULT);
uiItemO(sub, "", ICON_X, "CONSTRAINT_OT_delete");
}
/* Some extra padding at the end, so the 'x' icon isn't too close to drag button. */
uiItemS(layout);
/* Set but-locks for protected settings (magic numbers are used here!) */
if (proxy_protected) {
UI_block_lock_set(block, true, TIP_("Cannot edit Proxy-Protected Constraint"));

View File

@ -1483,13 +1483,11 @@ static int constraint_delete_exec(bContext *C, wmOperator *op)
/* free the constraint */
if (BKE_constraint_remove_ex(lb, ob, con, true)) {
/* there's no active constraint now, so make sure this is the case */
BKE_constraints_active_set(&ob->constraints, NULL);
/* needed to set the flags on posebones correctly */
ED_object_constraint_update(bmain, ob);
/* relations */
DEG_relations_tag_update(CTX_data_main(C));
DEG_relations_tag_update(bmain);
/* notifiers */
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob);
@ -1507,10 +1505,10 @@ static int constraint_delete_exec(bContext *C, wmOperator *op)
static int constraint_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int retval;
if (edit_constraint_invoke_properties(C, op, event, &retval)) {
return constraint_delete_exec(C, op);
if (!edit_constraint_invoke_properties(C, op, event, &retval)) {
return OPERATOR_CANCELLED;
}
return OPERATOR_CANCELLED;
return constraint_delete_exec(C, op);
}
void CONSTRAINT_OT_delete(wmOperatorType *ot)
@ -1533,6 +1531,320 @@ void CONSTRAINT_OT_delete(wmOperatorType *ot)
/** \} */
/* ------------------------------------------------------------------- */
/** \name Apply Constraint Operator
* \{ */
static int constraint_apply_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Main *bmain = CTX_data_main(C);
Object *ob = ED_object_active_context(C);
bConstraint *con = edit_constraint_property_get(C, op, ob, 0);
bPoseChannel *pchan;
ListBase *constraints = ED_object_constraint_list_from_constraint(ob, con, &pchan);
/* Store name temporarily for report. */
char name[MAX_NAME];
strcpy(name, con->name);
const bool is_first_constraint = con != constraints->first;
/* Copy the constraint. */
bool success;
if (pchan) {
success = BKE_constraint_apply_and_remove_for_pose(
depsgraph, scene, constraints, ob, con, pchan);
}
else {
success = BKE_constraint_apply_and_remove_for_object(depsgraph, scene, constraints, ob, con);
}
if (!success) {
/* Couldn't remove due to some invalid data. */
return OPERATOR_CANCELLED;
}
/* Update for any children that may get moved. */
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
/* Needed to set the flags on posebones correctly. */
ED_object_constraint_update(bmain, ob);
DEG_relations_tag_update(bmain);
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob);
if (pchan) {
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
}
else {
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob);
}
if (RNA_boolean_get(op->ptr, "report")) {
if (is_first_constraint) {
BKE_report(op->reports,
RPT_INFO,
"Applied constraint was not first, result may not be as expected");
}
else {
/* Only add this report if the operator didn't cause another one. The purpose here is
* to alert that something happened, and the previous report will do that anyway. */
BKE_reportf(op->reports, RPT_INFO, "Applied constraint: %s", name);
}
}
return OPERATOR_FINISHED;
}
static int constraint_apply_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int retval;
if (!edit_constraint_invoke_properties(C, op, event, &retval)) {
return OPERATOR_CANCELLED;
}
return constraint_apply_exec(C, op);
}
void CONSTRAINT_OT_apply(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Apply Constraint";
ot->idname = "CONSTRAINT_OT_apply";
ot->description = "Apply constraint and remove from the stack";
/* callbacks */
ot->invoke = constraint_apply_invoke;
ot->exec = constraint_apply_exec;
ot->poll = edit_constraint_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
edit_constraint_properties(ot);
edit_constraint_report_property(ot);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Copy Constraint Operator
* \{ */
static int constraint_copy_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *ob = ED_object_active_context(C);
bConstraint *con = edit_constraint_property_get(C, op, ob, 0);
bPoseChannel *pchan;
ListBase *constraints = ED_object_constraint_list_from_constraint(ob, con, &pchan);
/* Store name temporarily for report. */
char name[MAX_NAME];
strcpy(name, con->name);
/* Copy the constraint. */
bConstraint *copy_con;
if (pchan) {
copy_con = BKE_constraint_copy_for_pose(ob, pchan, con);
}
else {
copy_con = BKE_constraint_copy_for_object(ob, con);
}
if (!copy_con) {
/* Couldn't remove due to some invalid data. */
return OPERATOR_CANCELLED;
}
/* Move constraint to correct position. */
const int new_index = BLI_findindex(constraints, con) + 1;
const int current_index = BLI_findindex(constraints, copy_con);
BLI_assert(new_index >= 0);
BLI_assert(current_index >= 0);
BLI_listbase_link_move(constraints, copy_con, new_index - current_index);
/* Needed to set the flags on posebones correctly. */
ED_object_constraint_update(bmain, ob);
DEG_relations_tag_update(bmain);
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_ADDED, ob);
if (RNA_boolean_get(op->ptr, "report")) {
BKE_reportf(op->reports, RPT_INFO, "Copied constraint: %s", name);
}
return OPERATOR_FINISHED;
}
static int constraint_copy_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int retval;
if (!edit_constraint_invoke_properties(C, op, event, &retval)) {
return OPERATOR_CANCELLED;
}
return constraint_copy_exec(C, op);
}
void CONSTRAINT_OT_copy(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Duplicate Constraint";
ot->idname = "CONSTRAINT_OT_copy";
ot->description = "Duplicate constraint at the same position in the stack";
/* callbacks */
ot->invoke = constraint_copy_invoke;
ot->exec = constraint_copy_exec;
ot->poll = edit_constraint_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
edit_constraint_properties(ot);
edit_constraint_report_property(ot);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Copy Constraint To Selected Operator
* \{ */
static int constraint_copy_to_selected_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *obact = ED_object_active_context(C);
bConstraint *con = edit_constraint_property_get(C, op, obact, 0);
bPoseChannel *pchan;
ED_object_constraint_list_from_constraint(obact, con, &pchan);
if (pchan) {
/* Don't do anything if bone doesn't exist or doesn't have any constraints. */
if (pchan->constraints.first == NULL) {
BKE_report(op->reports, RPT_ERROR, "No constraints for copying");
return OPERATOR_CANCELLED;
}
Object *prev_ob = NULL;
/* Copy all constraints from active posebone to all selected posebones. */
CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, ob) {
/* If we're not handling the object we're copying from, copy all constraints over. */
if (pchan == chan) {
continue;
}
BKE_constraint_copy_for_pose(ob, chan, con);
/* Update flags (need to add here, not just copy). */
chan->constflag |= pchan->constflag;
if (prev_ob == ob) {
continue;
}
BKE_pose_tag_recalc(bmain, ob->pose);
DEG_id_tag_update((ID *)ob, ID_RECALC_GEOMETRY);
prev_ob = ob;
}
CTX_DATA_END;
}
else {
/* Copy all constraints from active object to all selected objects. */
CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
/* If we're not handling the object we're copying from, copy all constraints over. */
if (obact == ob) {
continue;
}
BKE_constraint_copy_for_object(ob, con);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_TRANSFORM);
}
CTX_DATA_END;
}
/* Force depsgraph to get recalculated since new relationships added. */
DEG_relations_tag_update(bmain);
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, NULL);
return OPERATOR_FINISHED;
}
static int constraint_copy_to_selected_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int retval;
if (!edit_constraint_invoke_properties(C, op, event, &retval)) {
return retval;
}
return constraint_copy_to_selected_exec(C, op);
}
static bool constraint_copy_to_selected_poll(bContext *C)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
Object *obact = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C);
bConstraint *con = ptr.data;
bPoseChannel *pchan;
ED_object_constraint_list_from_constraint(obact, con, &pchan);
if (pchan) {
bool found = false;
CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, UNUSED(ob)) {
if (pchan != chan) {
/** NOTE: Can not return here, because CTX_DATA_BEGIN_WITH_ID allocated
* a list that needs to be freed by CTX_DATA_END. */
found = true;
break;
}
}
CTX_DATA_END;
if (found) {
return true;
}
CTX_wm_operator_poll_msg_set(C, "No other bones are selected");
return false;
}
if (!obact) {
CTX_wm_operator_poll_msg_set(C, "No selected object to copy from");
return false;
}
bool found = false;
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
if (ob != obact) {
/** NOTE: Can not return here, because CTX_DATA_BEGIN allocated
* a list that needs to be freed by CTX_DATA_END. */
found = true;
break;
}
}
CTX_DATA_END;
if (found) {
return true;
}
CTX_wm_operator_poll_msg_set(C, "No other objects are selected");
return false;
}
void CONSTRAINT_OT_copy_to_selected(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Copy Constraint To Selected";
ot->idname = "CONSTRAINT_OT_copy_to_selected";
ot->description = "Copy constraint to other selected objects/bones";
/* api callbacks */
ot->exec = constraint_copy_to_selected_exec;
ot->invoke = constraint_copy_to_selected_invoke;
ot->poll = constraint_copy_to_selected_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
edit_constraint_properties(ot);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Move Down Constraint Operator
* \{ */

View File

@ -226,6 +226,9 @@ void POSE_OT_ik_add(struct wmOperatorType *ot);
void POSE_OT_ik_clear(struct wmOperatorType *ot);
void CONSTRAINT_OT_delete(struct wmOperatorType *ot);
void CONSTRAINT_OT_apply(struct wmOperatorType *ot);
void CONSTRAINT_OT_copy(struct wmOperatorType *ot);
void CONSTRAINT_OT_copy_to_selected(struct wmOperatorType *ot);
void CONSTRAINT_OT_move_up(struct wmOperatorType *ot);
void CONSTRAINT_OT_move_to_index(struct wmOperatorType *ot);

View File

@ -183,6 +183,9 @@ void ED_operatortypes_object(void)
WM_operatortype_append(POSE_OT_ik_add);
WM_operatortype_append(POSE_OT_ik_clear);
WM_operatortype_append(CONSTRAINT_OT_delete);
WM_operatortype_append(CONSTRAINT_OT_apply);
WM_operatortype_append(CONSTRAINT_OT_copy);
WM_operatortype_append(CONSTRAINT_OT_copy_to_selected);
WM_operatortype_append(CONSTRAINT_OT_move_up);
WM_operatortype_append(CONSTRAINT_OT_move_down);
WM_operatortype_append(CONSTRAINT_OT_move_to_index);

View File

@ -3509,6 +3509,12 @@ void RNA_def_constraint(BlenderRNA *brna)
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
RNA_def_property_ui_icon(prop, ICON_HIDE_OFF, -1);
prop = RNA_def_property(srna, "enabled", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", CONSTRAINT_OFF);
RNA_def_property_ui_text(prop, "Enabled", "Use the results of this constraint");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
RNA_def_property_ui_icon(prop, ICON_HIDE_ON, 1);
prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
RNA_def_property_boolean_sdna(prop, NULL, "ui_expand_flag", 0);