Python API: add a method for reordering modifiers.

Add an `object.modifiers.move()` method, similar to the one
for constraints and some other collections. Currently reordering
modifiers requires using operators, which depend on context.

The implementation is straightforward, except for the need to
make the severity of errors reported by the underlying editor
code into a parameter, so that the new Python API function
reports any problems as Python exceptions, and refactoring
the code to allow aborting a blocked move before making any
changes. I also turn the negative index condition from an assert
into an error.

Differential Revision: https://developer.blender.org/D16966
This commit is contained in:
Alexander Gavrilov 2023-01-10 18:25:58 +02:00
parent 4a768a7857
commit f7dd7d5454
4 changed files with 120 additions and 32 deletions

View File

@ -10,6 +10,7 @@
#include "BLI_compiler_attrs.h"
#include "DNA_object_enums.h"
#include "DNA_userdef_enums.h"
#include "DNA_windowmanager_types.h"
#ifdef __cplusplus
extern "C" {
@ -558,15 +559,19 @@ bool ED_object_modifier_remove(struct ReportList *reports,
struct ModifierData *md);
void ED_object_modifier_clear(struct Main *bmain, struct Scene *scene, struct Object *ob);
bool ED_object_modifier_move_down(struct ReportList *reports,
eReportType error_type,
struct Object *ob,
struct ModifierData *md);
bool ED_object_modifier_move_up(struct ReportList *reports,
eReportType error_type,
struct Object *ob,
struct ModifierData *md);
bool ED_object_modifier_move_to_index(struct ReportList *reports,
eReportType error_type,
struct Object *ob,
struct ModifierData *md,
int index);
int index,
bool allow_partial);
bool ED_object_modifier_convert_psys_to_mesh(struct ReportList *reports,
struct Main *bmain,

View File

@ -415,83 +415,139 @@ void ED_object_modifier_clear(Main *bmain, Scene *scene, Object *ob)
DEG_relations_tag_update(bmain);
}
bool ED_object_modifier_move_up(ReportList *reports, Object *ob, ModifierData *md)
static bool object_modifier_check_move_before(ReportList *reports,
eReportType error_type,
ModifierData *md,
ModifierData *md_prev)
{
if (md->prev) {
if (md_prev) {
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (mti->type != eModifierTypeType_OnlyDeform) {
const ModifierTypeInfo *nmti = BKE_modifier_get_info((ModifierType)md->prev->type);
const ModifierTypeInfo *nmti = BKE_modifier_get_info((ModifierType)md_prev->type);
if (nmti->flags & eModifierTypeFlag_RequiresOriginalData) {
BKE_report(reports, RPT_WARNING, "Cannot move above a modifier requiring original data");
BKE_report(reports, error_type, "Cannot move above a modifier requiring original data");
return false;
}
}
BLI_listbase_swaplinks(&ob->modifiers, md, md->prev);
}
else {
BKE_report(reports, RPT_WARNING, "Cannot move modifier beyond the start of the list");
BKE_report(reports, error_type, "Cannot move modifier beyond the start of the list");
return false;
}
return true;
}
bool ED_object_modifier_move_down(ReportList *reports, Object *ob, ModifierData *md)
bool ED_object_modifier_move_up(ReportList *reports,
eReportType error_type,
Object *ob,
ModifierData *md)
{
if (md->next) {
if (object_modifier_check_move_before(reports, error_type, md, md->prev)) {
BLI_listbase_swaplinks(&ob->modifiers, md, md->prev);
return true;
}
return false;
}
static bool object_modifier_check_move_after(ReportList *reports,
eReportType error_type,
ModifierData *md,
ModifierData *md_next)
{
if (md_next) {
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (mti->flags & eModifierTypeFlag_RequiresOriginalData) {
const ModifierTypeInfo *nmti = BKE_modifier_get_info((ModifierType)md->next->type);
const ModifierTypeInfo *nmti = BKE_modifier_get_info((ModifierType)md_next->type);
if (nmti->type != eModifierTypeType_OnlyDeform) {
BKE_report(reports, RPT_WARNING, "Cannot move beyond a non-deforming modifier");
BKE_report(reports, error_type, "Cannot move beyond a non-deforming modifier");
return false;
}
}
BLI_listbase_swaplinks(&ob->modifiers, md, md->next);
}
else {
BKE_report(reports, RPT_WARNING, "Cannot move modifier beyond the end of the list");
BKE_report(reports, error_type, "Cannot move modifier beyond the end of the list");
return false;
}
return true;
}
bool ED_object_modifier_move_down(ReportList *reports,
eReportType error_type,
Object *ob,
ModifierData *md)
{
if (object_modifier_check_move_after(reports, error_type, md, md->next)) {
BLI_listbase_swaplinks(&ob->modifiers, md, md->next);
return true;
}
return false;
}
bool ED_object_modifier_move_to_index(ReportList *reports,
eReportType error_type,
Object *ob,
ModifierData *md,
const int index)
const int index,
bool allow_partial)
{
BLI_assert(md != nullptr);
BLI_assert(index >= 0);
if (index >= BLI_listbase_count(&ob->modifiers)) {
BKE_report(reports, RPT_WARNING, "Cannot move modifier beyond the end of the stack");
if (index < 0 || index >= BLI_listbase_count(&ob->modifiers)) {
BKE_report(reports, error_type, "Cannot move modifier beyond the end of the stack");
return false;
}
int md_index = BLI_findindex(&ob->modifiers, md);
BLI_assert(md_index != -1);
if (md_index < index) {
/* Move modifier down in list. */
for (; md_index < index; md_index++) {
if (!ED_object_modifier_move_down(reports, ob, md)) {
ModifierData *md_target = md;
for (; md_index < index; md_index++, md_target = md_target->next) {
if (!object_modifier_check_move_after(reports, error_type, md, md_target->next)) {
if (!allow_partial || md == md_target) {
return false;
}
break;
}
}
BLI_assert(md != md_target && md_target);
BLI_remlink(&ob->modifiers, md);
BLI_insertlinkafter(&ob->modifiers, md_target, md);
}
else if (md_index > index) {
/* Move modifier up in list. */
ModifierData *md_target = md;
for (; md_index > index; md_index--, md_target = md_target->prev) {
if (!object_modifier_check_move_before(reports, error_type, md, md_target->prev)) {
if (!allow_partial || md == md_target) {
return false;
}
break;
}
}
BLI_assert(md != md_target && md_target);
BLI_remlink(&ob->modifiers, md);
BLI_insertlinkbefore(&ob->modifiers, md_target, md);
}
else {
/* Move modifier up in list. */
for (; md_index > index; md_index--) {
if (!ED_object_modifier_move_up(reports, ob, md)) {
break;
}
}
return true;
}
/* NOTE: Dependency graph only uses modifier nodes for visibility updates, and exact order of
@ -1518,7 +1574,7 @@ static int modifier_move_up_exec(bContext *C, wmOperator *op)
Object *ob = ED_object_active_context(C);
ModifierData *md = edit_modifier_property_get(op, ob, 0);
if (!md || !ED_object_modifier_move_up(op->reports, ob, md)) {
if (!md || !ED_object_modifier_move_up(op->reports, RPT_WARNING, ob, md)) {
return OPERATOR_CANCELLED;
}
@ -1563,7 +1619,7 @@ static int modifier_move_down_exec(bContext *C, wmOperator *op)
Object *ob = ED_object_active_context(C);
ModifierData *md = edit_modifier_property_get(op, ob, 0);
if (!md || !ED_object_modifier_move_down(op->reports, ob, md)) {
if (!md || !ED_object_modifier_move_down(op->reports, RPT_WARNING, ob, md)) {
return OPERATOR_CANCELLED;
}
@ -1609,7 +1665,7 @@ static int modifier_move_to_index_exec(bContext *C, wmOperator *op)
ModifierData *md = edit_modifier_property_get(op, ob, 0);
int index = RNA_int_get(op->ptr, "index");
if (!(md && ED_object_modifier_move_to_index(op->reports, ob, md, index))) {
if (!(md && ED_object_modifier_move_to_index(op->reports, RPT_WARNING, ob, md, index, true))) {
return OPERATOR_CANCELLED;
}

View File

@ -1027,8 +1027,12 @@ static void datastack_drop_reorder(bContext *C, ReportList *reports, StackDropDa
}
else {
index = outliner_get_insert_index(drag_te, drop_te, insert_type, &ob->modifiers);
ED_object_modifier_move_to_index(
reports, ob, static_cast<ModifierData *>(drop_data->drag_directdata), index);
ED_object_modifier_move_to_index(reports,
RPT_WARNING,
ob,
static_cast<ModifierData *>(drop_data->drag_directdata),
index,
true);
}
break;
case TSE_CONSTRAINT:

View File

@ -1759,6 +1759,19 @@ static void rna_Object_modifier_clear(Object *object, bContext *C)
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | NA_REMOVED, object);
}
static void rna_Object_modifier_move(
Object *object, Main *bmain, ReportList *reports, int from, int to)
{
ModifierData *md = BLI_findlink(&object->modifiers, from);
if (!md) {
BKE_reportf(reports, RPT_ERROR, "Invalid original modifier index '%d'", from);
return;
}
ED_object_modifier_move_to_index(reports, RPT_ERROR, object, md, to, false);
}
static PointerRNA rna_Object_active_modifier_get(PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
@ -2635,6 +2648,16 @@ static void rna_def_object_modifiers(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
RNA_def_function_ui_description(func, "Remove all modifiers from the object");
/* move a modifier */
func = RNA_def_function(srna, "move", "rna_Object_modifier_move");
RNA_def_function_ui_description(func, "Move a modifier to a different position");
RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS);
parm = RNA_def_int(
func, "from_index", -1, INT_MIN, INT_MAX, "From Index", "Index to move", 0, 10000);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_int(func, "to_index", -1, INT_MIN, INT_MAX, "To Index", "Target index", 0, 10000);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
/* Active modifier. */
prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "Modifier");