UI: Add Modifiers submenu to 3D view header

Add a menu similar to the "Object > Constraints" menu that allows adding,
copying, and clearing modifiers. The "copy all modifiers to selected" and
"clear modifiers" operators are new, to mirror the functionality we already
have for constraints.

The "Add" menu is the same that's used in the property editor. In the 3D
view, modifiers are always added to all selected objects.

Part of #120230

Pull Request: https://projects.blender.org/blender/blender/pulls/121286
This commit is contained in:
Hans Goudey
2024-05-01 14:15:53 +02:00
committed by Hans Goudey
parent d4a61647bf
commit e63c8bb3c2
6 changed files with 107 additions and 4 deletions

View File

@@ -56,7 +56,10 @@ class OBJECT_MT_modifier_add(ModifierAddMenu, Menu):
def draw(self, context):
layout = self.layout
ob_type = context.object.type
ob = context.object
if not ob:
return
ob_type = ob.type
geometry_nodes_supported = ob_type in {
'MESH', 'CURVE', 'CURVES',
'FONT', 'VOLUME', 'POINTCLOUD', 'GREASEPENCIL',

View File

@@ -2889,6 +2889,7 @@ class VIEW3D_MT_object(Menu):
layout.menu("VIEW3D_MT_object_relations")
layout.menu("VIEW3D_MT_object_parent")
layout.menu("VIEW3D_MT_object_constraints")
layout.menu("VIEW3D_MT_object_modifiers")
layout.menu("VIEW3D_MT_object_track")
layout.menu("VIEW3D_MT_make_links")
@@ -3357,6 +3358,20 @@ class VIEW3D_MT_object_constraints(Menu):
layout.operator("object.constraints_clear")
class VIEW3D_MT_object_modifiers(Menu):
bl_label = "Modifiers"
def draw(self, _context):
layout = self.layout
layout.menu("OBJECT_MT_modifier_add", text="Add")
layout.operator("object.modifiers_copy_to_selected", text="Copy to Selected")
layout.separator()
layout.operator("object.modifiers_clear")
class VIEW3D_MT_object_quick_effects(Menu):
bl_label = "Quick Effects"
@@ -9136,6 +9151,7 @@ classes = (
VIEW3D_MT_object_track,
VIEW3D_MT_object_collection,
VIEW3D_MT_object_constraints,
VIEW3D_MT_object_modifiers,
VIEW3D_MT_object_quick_effects,
VIEW3D_MT_object_showhide,
VIEW3D_MT_object_cleanup,

View File

@@ -338,7 +338,7 @@ static int modifier_add_asset_exec(bContext *C, wmOperator *op)
static int modifier_add_asset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
if (event->modifier & KM_ALT) {
if (event->modifier & KM_ALT || CTX_wm_view3d(C)) {
RNA_boolean_set(op->ptr, "use_selected_objects", true);
}
return modifier_add_asset_exec(C, op);

View File

@@ -195,6 +195,7 @@ ModifierData *edit_modifier_property_get(wmOperator *op, Object *ob, int type);
void OBJECT_OT_modifier_add(wmOperatorType *ot);
void OBJECT_OT_modifier_remove(wmOperatorType *ot);
void OBJECT_OT_modifiers_clear(wmOperatorType *ot);
void OBJECT_OT_modifier_move_up(wmOperatorType *ot);
void OBJECT_OT_modifier_move_down(wmOperatorType *ot);
void OBJECT_OT_modifier_move_to_index(wmOperatorType *ot);
@@ -203,6 +204,7 @@ void OBJECT_OT_modifier_apply_as_shapekey(wmOperatorType *ot);
void OBJECT_OT_modifier_convert(wmOperatorType *ot);
void OBJECT_OT_modifier_copy(wmOperatorType *ot);
void OBJECT_OT_modifier_copy_to_selected(wmOperatorType *ot);
void OBJECT_OT_modifiers_copy_to_selected(wmOperatorType *ot);
void OBJECT_OT_modifier_set_active(wmOperatorType *ot);
void OBJECT_OT_multires_subdivide(wmOperatorType *ot);
void OBJECT_OT_multires_reshape(wmOperatorType *ot);

View File

@@ -1290,7 +1290,7 @@ void modifier_register_use_selected_objects_prop(wmOperatorType *ot)
false,
"Selected Objects",
"Affect all selected objects instead of just the active object");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
}
/* ------------------------------------------------------------------- */
@@ -1321,7 +1321,7 @@ static int modifier_add_exec(bContext *C, wmOperator *op)
static int modifier_add_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
if (event->modifier & KM_ALT) {
if (event->modifier & KM_ALT || CTX_wm_view3d(C)) {
RNA_boolean_set(op->ptr, "use_selected_objects", true);
}
if (!RNA_struct_property_is_set(op->ptr, "type")) {
@@ -1641,6 +1641,32 @@ void OBJECT_OT_modifier_remove(wmOperatorType *ot)
modifier_register_use_selected_objects_prop(ot);
}
static int modifiers_clear_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
CTX_DATA_BEGIN (C, Object *, object, selected_editable_objects) {
modifiers_clear(bmain, scene, object);
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | NA_REMOVED, object);
}
CTX_DATA_END;
return OPERATOR_FINISHED;
}
void OBJECT_OT_modifiers_clear(wmOperatorType *ot)
{
ot->name = "Clear Object Modifiers";
ot->description = "Clear all modifiers from the selected objects";
ot->idname = "OBJECT_OT_modifiers_clear";
ot->exec = modifiers_clear_exec;
ot->poll = ED_operator_object_active_local_editable;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* ------------------------------------------------------------------- */
@@ -2287,6 +2313,60 @@ void OBJECT_OT_modifier_copy_to_selected(wmOperatorType *ot)
edit_modifier_properties(ot);
}
static int object_modifiers_copy_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
const Scene *scene = CTX_data_scene(C);
Object *active_object = context_active_object(C);
Vector<PointerRNA> selected_objects;
CTX_data_selected_objects(C, &selected_objects);
CTX_DATA_BEGIN (C, Object *, object, selected_objects) {
if (object == active_object) {
continue;
}
LISTBASE_FOREACH (const ModifierData *, md, &active_object->modifiers) {
if (modifier_copy_to_object(bmain, scene, active_object, md, object, op->reports)) {
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER | NA_ADDED, object);
}
}
}
CTX_DATA_END;
return OPERATOR_FINISHED;
DEG_relations_tag_update(bmain);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER | NA_ADDED, nullptr);
return OPERATOR_FINISHED;
}
static bool modifiers_copy_to_selected_poll(bContext *C)
{
if (!ED_operator_object_active_editable(C)) {
return false;
}
Object *active_object = context_active_object(C);
if (BLI_listbase_is_empty(&active_object->modifiers)) {
CTX_wm_operator_poll_msg_set(C, "Active object has no modifiers");
return false;
}
return true;
}
void OBJECT_OT_modifiers_copy_to_selected(wmOperatorType *ot)
{
ot->name = "Copy Modifiers to Selected Objects";
ot->idname = "OBJECT_OT_modifiers_copy_to_selected";
ot->description = "Copy modifiers to other selected objects";
ot->exec = object_modifiers_copy_exec;
ot->poll = modifiers_copy_to_selected_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* ------------------------------------------------------------------- */

View File

@@ -119,6 +119,7 @@ void operatortypes_object()
WM_operatortype_append(OBJECT_OT_modifier_add);
WM_operatortype_append(OBJECT_OT_modifier_remove);
WM_operatortype_append(OBJECT_OT_modifiers_clear);
WM_operatortype_append(OBJECT_OT_modifier_move_up);
WM_operatortype_append(OBJECT_OT_modifier_move_down);
WM_operatortype_append(OBJECT_OT_modifier_move_to_index);
@@ -127,6 +128,7 @@ void operatortypes_object()
WM_operatortype_append(OBJECT_OT_modifier_convert);
WM_operatortype_append(OBJECT_OT_modifier_copy);
WM_operatortype_append(OBJECT_OT_modifier_copy_to_selected);
WM_operatortype_append(OBJECT_OT_modifiers_copy_to_selected);
WM_operatortype_append(OBJECT_OT_modifier_set_active);
WM_operatortype_append(OBJECT_OT_multires_subdivide);
WM_operatortype_append(OBJECT_OT_multires_reshape);