Anim: add operator to sync bone colors between selected bones

Blender's "Copy to Selected" feature only copies single properties, while
bone colors consist of four properties (palette + 3 custom colors). These
can now be copied with a single click.
This commit is contained in:
Sybren A. Stüvel
2023-09-28 11:21:02 +02:00
parent 4473bdc6ab
commit 1930ebdb4f
2 changed files with 87 additions and 2 deletions

View File

@@ -429,9 +429,87 @@ class UpdateAnimatedTransformConstraint(Operator):
return {'FINISHED'}
class ARMATURE_OT_sync_bone_color_to_selected(Operator):
"""Copy the bone color of the active bone to all selected bones"""
bl_idname = "armature.sync_bone_color_to_selected"
bl_label = "Sync to Selected"
bl_options = {'REGISTER', 'UNDO'}
_bone_type_enum = [
('EDIT', 'Edit Bone', 'Copy Edit Bone colors from the active bone to all selected bones'),
('POSE', 'Pose Bone', 'Copy Pose Bone colors from the active bone to all selected bones'),
]
bone_type: EnumProperty(
name="Type",
items=_bone_type_enum)
@classmethod
def poll(cls, context):
return context.mode in {'EDIT_ARMATURE', 'POSE'}
def execute(self, context):
match (self.bone_type, context.mode):
# Armature in edit mode:
case ('POSE', 'EDIT_ARMATURE'):
self.report({'ERROR'}, "Go to pose mode to copy pose bone colors")
return {'OPERATOR_CANCELLED'}
case ('EDIT', 'EDIT_ARMATURE'):
bone_source = context.active_bone
bones_dest = context.selected_bones
pose_bones_to_check = []
# Armature in pose mode:
case ('POSE', 'POSE'):
bone_source = context.active_pose_bone
bones_dest = context.selected_pose_bones
pose_bones_to_check = []
case ('EDIT', 'POSE'):
bone_source = context.active_bone
pose_bones_to_check = context.selected_pose_bones
bones_dest = [posebone.bone for posebone in pose_bones_to_check]
# Anything else:
case _:
self.report({'ERROR'}, "Cannot do anything in mode %r" % context.mode)
return {'CANCELLED'}
if not bone_source:
self.report({'ERROR'}, "No active bone to copy from.")
return {'CANCELLED'}
if not bones_dest:
self.report({'ERROR'}, "No selected bones to copy to.")
return {'CANCELLED'}
num_pose_color_overrides = 0
for index, bone_dest in enumerate(bones_dest):
bone_dest.color.palette = bone_source.color.palette
for custom_field in ('normal', 'select', 'active'):
color = getattr(bone_source.color.custom, custom_field)
setattr(bone_dest.color.custom, custom_field, color)
if self.bone_type == 'EDIT' and pose_bones_to_check:
pose_bone = pose_bones_to_check[index]
if pose_bone.color.palette != 'DEFAULT':
# A pose color has been set, and we're now syncing edit bone
# colors. This means that the synced color will not be
# visible. Better to let the user know about this.
num_pose_color_overrides += 1
if num_pose_color_overrides:
self.report(
{'INFO'},
"Bone colors were synced; for %d bones this will not be visible due to pose bone color overrides" %
num_pose_color_overrides)
return {'FINISHED'}
classes = (
ANIM_OT_keying_set_export,
NLA_OT_bake,
ClearUselessActions,
UpdateAnimatedTransformConstraint,
ARMATURE_OT_sync_bone_color_to_selected,
)

View File

@@ -281,9 +281,16 @@ class BONE_PT_display(BoneButtonsPanel, Panel):
# Allow the layout to use the space normally occupied by the 'set a key' diamond.
layout.use_property_decorate = False
layout.prop(bone.color, 'palette', text='Edit Bone Color')
row = layout.row(align=True)
row.prop(bone.color, 'palette', text='Edit Bone Color')
props = row.operator("armature.sync_bone_color_to_selected", text="", icon='UV_SYNC_SELECT')
props.bone_type = 'EDIT'
self.draw_bone_color_ui(layout, bone.color)
layout.prop(pose_bone.color, 'palette', text='Pose Bone Color')
row = layout.row(align=True)
row.prop(pose_bone.color, 'palette', text='Pose Bone Color')
props = row.operator("armature.sync_bone_color_to_selected", text="", icon='UV_SYNC_SELECT')
props.bone_type = 'POSE'
self.draw_bone_color_ui(layout, pose_bone.color)
def draw_edit_bone(self, context, layout):