Files
test2/scripts/startup/bl_ui/properties_data_modifier.py
Hans Goudey 9a7f4a3b58 Modifiers: Affect all selected objects when holding alt
Support holding alt while invoking modifier operations for add, apply,
remove, and move to index in the property editor. This affects all selected
editable objects instead of just the active object.

Though the alt key is not that visible, it's consistent with the
existing multi-object property editing shortcut. If/when multi-
object editing is every made the default, the alt key could
be reversed here too.

Changes as part of #120230.

Pull Request: https://projects.blender.org/blender/blender/pulls/120695
2024-04-18 17:40:50 +02:00

300 lines
13 KiB
Python

# SPDX-FileCopyrightText: 2009-2023 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from bpy.types import Panel, Menu, Operator
class ModifierButtonsPanel:
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "modifier"
bl_options = {'HIDE_HEADER'}
class ModifierAddMenu:
MODIFIER_TYPES_TO_LABELS = {
enum_it.identifier: enum_it.name
for enum_it in bpy.types.Modifier.bl_rna.properties["type"].enum_items_static
}
MODIFIER_TYPES_TO_ICONS = {
enum_it.identifier: enum_it.icon
for enum_it in bpy.types.Modifier.bl_rna.properties["type"].enum_items_static
}
MODIFIER_TYPES_I18N_CONTEXT = bpy.types.Modifier.bl_rna.properties["type"].translation_context
@classmethod
def operator_modifier_add(cls, layout, mod_type):
layout.operator(
"object.modifier_add",
text=cls.MODIFIER_TYPES_TO_LABELS[mod_type],
# Although these are operators, the label actually comes from an (enum) property,
# so the property's translation context must be used here.
text_ctxt=cls.MODIFIER_TYPES_I18N_CONTEXT,
icon=cls.MODIFIER_TYPES_TO_ICONS[mod_type],
).type = mod_type
class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
bl_label = "Modifiers"
@classmethod
def poll(cls, context):
ob = context.object
return ob and ob.type != 'GPENCIL'
def draw(self, _context):
layout = self.layout
layout.operator("wm.call_menu", text="Add Modifier", icon='ADD').name = "OBJECT_MT_modifier_add"
layout.template_modifiers()
class OBJECT_MT_modifier_add(ModifierAddMenu, Menu):
bl_label = "Add Modifier"
bl_options = {'SEARCH_ON_KEY_PRESS'}
def draw(self, context):
layout = self.layout
ob_type = context.object.type
geometry_nodes_supported = ob_type in {'MESH', 'CURVE', 'CURVES',
'FONT', 'VOLUME', 'POINTCLOUD', 'GREASEPENCIL'}
if layout.operator_context == 'EXEC_REGION_WIN':
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("WM_OT_search_single_menu", text="Search...",
icon='VIEWZOOM').menu_idname = "OBJECT_MT_modifier_add"
layout.separator()
layout.operator_context = 'INVOKE_REGION_WIN'
if geometry_nodes_supported:
self.operator_modifier_add(layout, 'NODES')
layout.separator()
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE', 'GREASEPENCIL'}:
layout.menu("OBJECT_MT_modifier_add_edit")
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'VOLUME', 'GREASEPENCIL'}:
layout.menu("OBJECT_MT_modifier_add_generate")
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE', 'VOLUME', 'GREASEPENCIL'}:
layout.menu("OBJECT_MT_modifier_add_deform")
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
layout.menu("OBJECT_MT_modifier_add_physics")
if ob_type in {'GREASEPENCIL'}:
layout.menu("OBJECT_MT_modifier_add_color")
if geometry_nodes_supported:
layout.menu_contents("OBJECT_MT_modifier_add_root_catalogs")
class OBJECT_MT_modifier_add_edit(ModifierAddMenu, Menu):
bl_label = "Edit"
bl_options = {'SEARCH_ON_KEY_PRESS'}
def draw(self, context):
layout = self.layout
ob_type = context.object.type
if ob_type == 'MESH':
self.operator_modifier_add(layout, 'DATA_TRANSFER')
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
self.operator_modifier_add(layout, 'MESH_CACHE')
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
self.operator_modifier_add(layout, 'MESH_SEQUENCE_CACHE')
if ob_type == 'MESH':
self.operator_modifier_add(layout, 'NORMAL_EDIT')
self.operator_modifier_add(layout, 'WEIGHTED_NORMAL')
self.operator_modifier_add(layout, 'UV_PROJECT')
self.operator_modifier_add(layout, 'UV_WARP')
self.operator_modifier_add(layout, 'VERTEX_WEIGHT_EDIT')
self.operator_modifier_add(layout, 'VERTEX_WEIGHT_MIX')
self.operator_modifier_add(layout, 'VERTEX_WEIGHT_PROXIMITY')
if ob_type == 'GREASEPENCIL':
self.operator_modifier_add(layout, 'GREASE_PENCIL_TEXTURE')
self.operator_modifier_add(layout, 'GREASE_PENCIL_TIME')
self.operator_modifier_add(layout, 'GREASE_PENCIL_VERTEX_WEIGHT_PROXIMITY')
self.operator_modifier_add(layout, 'GREASE_PENCIL_VERTEX_WEIGHT_ANGLE')
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)
class OBJECT_MT_modifier_add_generate(ModifierAddMenu, Menu):
bl_label = "Generate"
bl_options = {'SEARCH_ON_KEY_PRESS'}
def draw(self, context):
layout = self.layout
ob_type = context.object.type
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
self.operator_modifier_add(layout, 'ARRAY')
self.operator_modifier_add(layout, 'BEVEL')
if ob_type == 'MESH':
self.operator_modifier_add(layout, 'BOOLEAN')
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
self.operator_modifier_add(layout, 'BUILD')
self.operator_modifier_add(layout, 'DECIMATE')
self.operator_modifier_add(layout, 'EDGE_SPLIT')
if ob_type == 'MESH':
self.operator_modifier_add(layout, 'MASK')
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
self.operator_modifier_add(layout, 'MIRROR')
if ob_type == 'VOLUME':
self.operator_modifier_add(layout, 'MESH_TO_VOLUME')
if ob_type == 'MESH':
self.operator_modifier_add(layout, 'MULTIRES')
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
self.operator_modifier_add(layout, 'REMESH')
self.operator_modifier_add(layout, 'SCREW')
if ob_type == 'MESH':
self.operator_modifier_add(layout, 'SKIN')
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
self.operator_modifier_add(layout, 'SOLIDIFY')
self.operator_modifier_add(layout, 'SUBSURF')
self.operator_modifier_add(layout, 'TRIANGULATE')
if ob_type == 'MESH':
self.operator_modifier_add(layout, 'VOLUME_TO_MESH')
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
self.operator_modifier_add(layout, 'WELD')
if ob_type == 'MESH':
self.operator_modifier_add(layout, 'WIREFRAME')
if ob_type == 'GREASEPENCIL':
self.operator_modifier_add(layout, 'GREASE_PENCIL_ARRAY')
self.operator_modifier_add(layout, 'GREASE_PENCIL_BUILD')
self.operator_modifier_add(layout, 'GREASE_PENCIL_DASH')
self.operator_modifier_add(layout, 'GREASE_PENCIL_ENVELOPE')
self.operator_modifier_add(layout, 'GREASE_PENCIL_LENGTH')
self.operator_modifier_add(layout, 'GREASE_PENCIL_MIRROR')
self.operator_modifier_add(layout, 'GREASE_PENCIL_MULTIPLY')
self.operator_modifier_add(layout, 'GREASE_PENCIL_OUTLINE')
self.operator_modifier_add(layout, 'GREASE_PENCIL_SIMPLIFY')
self.operator_modifier_add(layout, 'GREASE_PENCIL_SUBDIV')
self.operator_modifier_add(layout, 'LINEART')
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)
class OBJECT_MT_modifier_add_deform(ModifierAddMenu, Menu):
bl_label = "Deform"
bl_options = {'SEARCH_ON_KEY_PRESS'}
def draw(self, context):
layout = self.layout
ob_type = context.object.type
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
self.operator_modifier_add(layout, 'ARMATURE')
self.operator_modifier_add(layout, 'CAST')
self.operator_modifier_add(layout, 'CURVE')
if ob_type == 'MESH':
self.operator_modifier_add(layout, 'DISPLACE')
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
self.operator_modifier_add(layout, 'HOOK')
if ob_type == 'MESH':
self.operator_modifier_add(layout, 'LAPLACIANDEFORM')
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
self.operator_modifier_add(layout, 'LATTICE')
self.operator_modifier_add(layout, 'MESH_DEFORM')
self.operator_modifier_add(layout, 'SHRINKWRAP')
self.operator_modifier_add(layout, 'SIMPLE_DEFORM')
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE'}:
self.operator_modifier_add(layout, 'SMOOTH')
if ob_type == 'MESH':
self.operator_modifier_add(layout, 'CORRECTIVE_SMOOTH')
self.operator_modifier_add(layout, 'LAPLACIANSMOOTH')
self.operator_modifier_add(layout, 'SURFACE_DEFORM')
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
self.operator_modifier_add(layout, 'WARP')
self.operator_modifier_add(layout, 'WAVE')
if ob_type == 'VOLUME':
self.operator_modifier_add(layout, 'VOLUME_DISPLACE')
if ob_type == 'GREASEPENCIL':
self.operator_modifier_add(layout, 'GREASE_PENCIL_ARMATURE')
self.operator_modifier_add(layout, 'GREASE_PENCIL_HOOK')
self.operator_modifier_add(layout, 'GREASE_PENCIL_LATTICE')
self.operator_modifier_add(layout, 'GREASE_PENCIL_NOISE')
self.operator_modifier_add(layout, 'GREASE_PENCIL_OFFSET')
self.operator_modifier_add(layout, 'GREASE_PENCIL_SHRINKWRAP')
self.operator_modifier_add(layout, 'GREASE_PENCIL_SMOOTH')
self.operator_modifier_add(layout, 'GREASE_PENCIL_THICKNESS')
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)
class OBJECT_MT_modifier_add_physics(ModifierAddMenu, Menu):
bl_label = "Physics"
bl_options = {'SEARCH_ON_KEY_PRESS'}
def draw(self, context):
layout = self.layout
ob_type = context.object.type
if ob_type == 'MESH':
self.operator_modifier_add(layout, 'CLOTH')
self.operator_modifier_add(layout, 'COLLISION')
self.operator_modifier_add(layout, 'DYNAMIC_PAINT')
self.operator_modifier_add(layout, 'EXPLODE')
self.operator_modifier_add(layout, 'FLUID')
self.operator_modifier_add(layout, 'OCEAN')
self.operator_modifier_add(layout, 'PARTICLE_INSTANCE')
self.operator_modifier_add(layout, 'PARTICLE_SYSTEM')
if ob_type in {'MESH', 'CURVE', 'FONT', 'SURFACE', 'LATTICE'}:
self.operator_modifier_add(layout, 'SOFT_BODY')
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)
class OBJECT_MT_modifier_add_color(ModifierAddMenu, Menu):
bl_label = "Color"
bl_options = {'SEARCH_ON_KEY_PRESS'}
def draw(self, context):
layout = self.layout
ob_type = context.object.type
if ob_type == 'GREASEPENCIL':
self.operator_modifier_add(layout, 'GREASE_PENCIL_COLOR')
self.operator_modifier_add(layout, 'GREASE_PENCIL_TINT')
self.operator_modifier_add(layout, 'GREASE_PENCIL_OPACITY')
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)
class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel):
bl_label = "Modifiers"
@classmethod
def poll(cls, context):
ob = context.object
return ob and ob.type == 'GPENCIL'
def draw(self, _context):
layout = self.layout
layout.operator_menu_enum("object.gpencil_modifier_add", "type")
layout.template_grease_pencil_modifiers()
class AddModifierMenu(Operator):
bl_idname = "object.add_modifier_menu"
bl_label = "Add Modifier"
@classmethod
def poll(cls, context):
# NOTE: This operator only exists to add a poll to the add modifier shortcut in the property editor.
object = context.object
space = context.space_data
if object and object.type == 'GPENCIL':
return False
return space and space.type == 'PROPERTIES' and space.context == 'MODIFIER'
def invoke(self, context, event):
return bpy.ops.wm.call_menu(name="OBJECT_MT_modifier_add")
classes = (
DATA_PT_modifiers,
OBJECT_MT_modifier_add,
OBJECT_MT_modifier_add_edit,
OBJECT_MT_modifier_add_generate,
OBJECT_MT_modifier_add_deform,
OBJECT_MT_modifier_add_physics,
OBJECT_MT_modifier_add_color,
DATA_PT_gpencil_modifiers,
AddModifierMenu,
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)