Merge branch 'blender-v4.0-release'

This commit is contained in:
Lukas Tönne
2023-09-28 11:25:39 +02:00
5 changed files with 155 additions and 38 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

@@ -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="")

View File

@@ -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;
}
/** \} */

View File

@@ -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;
}

View File

@@ -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;