Merge branch 'blender-v4.0-release'
This commit is contained in:
@@ -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,
|
||||
)
|
||||
|
||||
@@ -278,9 +278,19 @@ class BONE_PT_display(BoneButtonsPanel, Panel):
|
||||
return
|
||||
pose_bone = ob.pose.bones[bone.name]
|
||||
|
||||
layout.prop(bone.color, 'palette', text='Edit Bone Color')
|
||||
# Allow the layout to use the space normally occupied by the 'set a key' diamond.
|
||||
layout.use_property_decorate = False
|
||||
|
||||
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):
|
||||
@@ -297,8 +307,7 @@ class BONE_PT_display(BoneButtonsPanel, Panel):
|
||||
if not bone_color.is_custom:
|
||||
return
|
||||
|
||||
layout.use_property_split = False
|
||||
split = layout.split(factor=0.4)
|
||||
split = layout.split(factor=0.401)
|
||||
|
||||
col = split.column()
|
||||
row = col.row()
|
||||
@@ -307,6 +316,7 @@ class BONE_PT_display(BoneButtonsPanel, Panel):
|
||||
|
||||
col = split.column(align=True)
|
||||
row = col.row(align=True)
|
||||
row.use_property_split = False
|
||||
row.prop(bone_color.custom, "normal", text="")
|
||||
row.prop(bone_color.custom, "select", text="")
|
||||
row.prop(bone_color.custom, "active", text="")
|
||||
|
||||
@@ -915,4 +915,9 @@ inline bool bNodePanelState::is_parent_collapsed() const
|
||||
return flag & NODE_PANEL_PARENT_COLLAPSED;
|
||||
}
|
||||
|
||||
inline bool bNodePanelState::has_visible_content() const
|
||||
{
|
||||
return flag & NODE_PANEL_CONTENT_VISIBLE;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -631,8 +631,10 @@ struct VisibilityUpdateState {
|
||||
/* Recursive function to determine visibility of items before drawing. */
|
||||
static void node_update_panel_items_visibility_recursive(int num_items,
|
||||
const bool is_parent_collapsed,
|
||||
bNodePanelState &parent_state,
|
||||
VisibilityUpdateState &state)
|
||||
{
|
||||
parent_state.flag &= ~NODE_PANEL_CONTENT_VISIBLE;
|
||||
while (state.item_iter != state.item_end) {
|
||||
/* Stop after adding the expected number of items.
|
||||
* Root panel consumes all remaining items (num_items == -1). */
|
||||
@@ -651,14 +653,24 @@ static void node_update_panel_items_visibility_recursive(int num_items,
|
||||
const bool is_collapsed = is_parent_collapsed || item.state->is_collapsed();
|
||||
|
||||
node_update_panel_items_visibility_recursive(
|
||||
item.panel_decl->num_child_decls, is_collapsed, state);
|
||||
item.panel_decl->num_child_decls, is_collapsed, *item.state, state);
|
||||
if (item.state->flag & NODE_PANEL_CONTENT_VISIBLE) {
|
||||
/* If child panel is visible so is the parent panel. */
|
||||
parent_state.flag |= NODE_PANEL_CONTENT_VISIBLE;
|
||||
}
|
||||
}
|
||||
else if (item.is_valid_socket()) {
|
||||
if (item.input) {
|
||||
SET_FLAG_FROM_TEST(item.input->flag, is_parent_collapsed, SOCK_PANEL_COLLAPSED);
|
||||
if (item.input->is_visible()) {
|
||||
parent_state.flag |= NODE_PANEL_CONTENT_VISIBLE;
|
||||
}
|
||||
}
|
||||
if (item.output) {
|
||||
SET_FLAG_FROM_TEST(item.output->flag, is_parent_collapsed, SOCK_PANEL_COLLAPSED);
|
||||
if (item.output->is_visible()) {
|
||||
parent_state.flag |= NODE_PANEL_CONTENT_VISIBLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -719,36 +731,41 @@ static void add_panel_items_recursive(const bContext &C,
|
||||
C, ntree, node, node.typeinfo->draw_buttons, block, locy);
|
||||
}
|
||||
|
||||
if (!is_parent_collapsed) {
|
||||
locy -= NODE_DY;
|
||||
state.is_first = false;
|
||||
}
|
||||
/* Panel visible if any content is visible. */
|
||||
if (item.state->has_visible_content()) {
|
||||
if (!is_parent_collapsed) {
|
||||
locy -= NODE_DY;
|
||||
state.is_first = false;
|
||||
}
|
||||
|
||||
/* New top panel is collapsed if self or parent is collapsed. */
|
||||
const bool is_collapsed = is_parent_collapsed || item.state->is_collapsed();
|
||||
/* New top panel is collapsed if self or parent is collapsed. */
|
||||
const bool is_collapsed = is_parent_collapsed || item.state->is_collapsed();
|
||||
|
||||
/* Round the socket location to stop it from jiggling. */
|
||||
item.runtime->location_y = round(locy + NODE_DYS);
|
||||
if (!is_collapsed) {
|
||||
locy -= NODE_ITEM_SPACING_Y / 2; /* Space at bottom of panel header. */
|
||||
}
|
||||
item.runtime->max_content_y = item.runtime->min_content_y = round(locy);
|
||||
if (!is_collapsed) {
|
||||
locy -= NODE_ITEM_SPACING_Y; /* Space at top of panel contents. */
|
||||
node_update_basis_buttons(C, ntree, node, item.panel_decl->draw_buttons, block, locy);
|
||||
}
|
||||
/* Round the socket location to stop it from jiggling. */
|
||||
item.runtime->location_y = round(locy + NODE_DYS);
|
||||
if (is_collapsed) {
|
||||
item.runtime->max_content_y = item.runtime->min_content_y = round(locy);
|
||||
}
|
||||
else {
|
||||
locy -= NODE_ITEM_SPACING_Y / 2; /* Space at bottom of panel header. */
|
||||
item.runtime->max_content_y = item.runtime->min_content_y = round(locy);
|
||||
locy -= NODE_ITEM_SPACING_Y; /* Space at top of panel contents. */
|
||||
|
||||
add_panel_items_recursive(C,
|
||||
ntree,
|
||||
node,
|
||||
block,
|
||||
locx,
|
||||
locy,
|
||||
item.panel_decl->num_child_decls,
|
||||
is_collapsed,
|
||||
item.panel_decl->name.c_str(),
|
||||
item.runtime,
|
||||
state);
|
||||
node_update_basis_buttons(C, ntree, node, item.panel_decl->draw_buttons, block, locy);
|
||||
}
|
||||
|
||||
add_panel_items_recursive(C,
|
||||
ntree,
|
||||
node,
|
||||
block,
|
||||
locx,
|
||||
locy,
|
||||
item.panel_decl->num_child_decls,
|
||||
is_collapsed,
|
||||
item.panel_decl->name.c_str(),
|
||||
item.runtime,
|
||||
state);
|
||||
}
|
||||
}
|
||||
else if (item.is_valid_socket()) {
|
||||
if (item.input) {
|
||||
@@ -804,7 +821,7 @@ static void add_panel_items_recursive(const bContext &C,
|
||||
}
|
||||
locy -= NODE_ITEM_SPACING_Y / 2; /* Space at top of next panel header. */
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* Advanced drawing with panels and arbitrary input/output ordering. */
|
||||
static void node_update_basis_from_declaration(
|
||||
@@ -819,7 +836,9 @@ static void node_update_basis_from_declaration(
|
||||
|
||||
/* Update item visibility flags first. */
|
||||
VisibilityUpdateState visibility_state(item_data);
|
||||
node_update_panel_items_visibility_recursive(-1, false, visibility_state);
|
||||
/* Dummy state item to write into, unused. */
|
||||
bNodePanelState root_panel_state;
|
||||
node_update_panel_items_visibility_recursive(-1, false, root_panel_state, visibility_state);
|
||||
|
||||
/* Space at the top. */
|
||||
locy -= NODE_DYS / 2;
|
||||
@@ -2015,10 +2034,11 @@ static void node_draw_panels_background(const bNode &node, uiBlock &block)
|
||||
const bke::bNodePanelRuntime &runtime = node.runtime->panels[panel_i];
|
||||
|
||||
/* Don't draw hidden or collapsed panels. */
|
||||
const bool is_visible = !(state.is_collapsed() || state.is_parent_collapsed());
|
||||
is_last_panel_visible = is_visible;
|
||||
const bool is_background_visible = state.has_visible_content() &&
|
||||
!(state.is_collapsed() || state.is_parent_collapsed());
|
||||
is_last_panel_visible = is_background_visible;
|
||||
last_panel_content_y = runtime.max_content_y;
|
||||
if (!is_visible) {
|
||||
if (!is_background_visible) {
|
||||
++panel_i;
|
||||
continue;
|
||||
}
|
||||
@@ -2068,7 +2088,8 @@ static void node_draw_panels(bNodeTree &ntree, const bNode &node, uiBlock &block
|
||||
|
||||
const bNodePanelState &state = node.panel_states()[panel_i];
|
||||
/* Don't draw hidden panels. */
|
||||
if (state.is_parent_collapsed()) {
|
||||
const bool is_header_visible = state.has_visible_content() && !state.is_parent_collapsed();
|
||||
if (!is_header_visible) {
|
||||
++panel_i;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -315,6 +315,8 @@ typedef enum eNodePanelFlag {
|
||||
NODE_PANEL_COLLAPSED = (1 << 0),
|
||||
/* The parent panel is collapsed. */
|
||||
NODE_PANEL_PARENT_COLLAPSED = (1 << 1),
|
||||
/* The panel has visible content. */
|
||||
NODE_PANEL_CONTENT_VISIBLE = (1 << 2),
|
||||
} eNodePanelFlag;
|
||||
|
||||
typedef struct bNodePanelState {
|
||||
@@ -327,6 +329,7 @@ typedef struct bNodePanelState {
|
||||
#ifdef __cplusplus
|
||||
bool is_collapsed() const;
|
||||
bool is_parent_collapsed() const;
|
||||
bool has_visible_content() const;
|
||||
#endif
|
||||
} bNodePanelState;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user