Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2020-06-19 19:40:48 +0300
committerHans Goudey <h.goudey@me.com>2020-06-19 19:40:48 +0300
commiteaa44afe703eeb785f4590719b39392b66d6a312 (patch)
tree8dbf73119d330d51e9b1181af1ede4a98e69f90c
parent27fb75fec265fbea31f3976e829b405a87b9970e (diff)
UI: Drag and Drop Constraints, Layout Updates
This patch implements the list panel system D7490 for constraints. In this case the panels are still defined in Python. The layouts are also updated to use subpanels and the a more organized single column layout. There may be more tweaks necessary for the layouts. Reviewed By: Severin, billreynish, Mets Differential Revision: https://developer.blender.org/D7499
-rw-r--r--release/scripts/startup/bl_ui/properties_constraint.py2076
-rw-r--r--source/blender/blenkernel/BKE_constraint.h3
-rw-r--r--source/blender/blenkernel/intern/constraint.c9
-rw-r--r--source/blender/blenloader/intern/versioning_290.c15
-rw-r--r--source/blender/draw/engines/overlay/overlay_extra.c2
-rw-r--r--source/blender/editors/include/UI_interface.h4
-rw-r--r--source/blender/editors/interface/interface_panel.c14
-rw-r--r--source/blender/editors/interface/interface_templates.c297
-rw-r--r--source/blender/editors/object/object_constraint.c71
-rw-r--r--source/blender/editors/object/object_intern.h1
-rw-r--r--source/blender/editors/object/object_ops.c1
-rw-r--r--source/blender/makesdna/DNA_constraint_types.h7
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c10
-rw-r--r--source/blender/makesrna/intern/rna_ui_api.c15
14 files changed, 1678 insertions, 847 deletions
diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py
index 3fc54ff6d12..04d43c28c27 100644
--- a/release/scripts/startup/bl_ui/properties_constraint.py
+++ b/release/scripts/startup/bl_ui/properties_constraint.py
@@ -20,894 +20,753 @@
from bpy.types import Panel
-class ConstraintButtonsPanel:
+class ObjectConstraintPanel(Panel):
+ bl_context = "constraint"
+
+ @classmethod
+ def poll(cls, context):
+ return (context.object)
+
+
+class BoneConstraintPanel(Panel):
+ bl_context = "bone_constraint"
+
+ @classmethod
+ def poll(cls, context):
+ return (context.pose_bone)
+
+
+class OBJECT_PT_constraints(ObjectConstraintPanel):
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
- bl_context = "constraint"
+ bl_label = "Object Constraints"
+ bl_options = {'HIDE_HEADER'}
- def draw_constraint(self, context, con):
+ def draw(self, context):
layout = self.layout
- box = layout.template_constraint(con)
+ layout.operator_menu_enum("object.constraint_add", "type", text="Add Object Constraint")
- if box:
- # match enum type to our functions, avoids a lookup table.
- getattr(self, con.type)(context, box, con)
+ layout.template_constraints(use_bone_constraints=False)
- if con.type in {'RIGID_BODY_JOINT', 'NULL'}:
- return
- if con.type in {'IK', 'SPLINE_IK'}:
- # constraint.disable_keep_transform doesn't work well
- # for these constraints.
- box.prop(con, "influence")
- else:
- row = box.row(align=True)
- row.prop(con, "influence")
- row.operator("constraint.disable_keep_transform", text="", icon='CANCEL')
+class BONE_PT_constraints(BoneConstraintPanel):
+ bl_space_type = 'PROPERTIES'
+ bl_region_type = 'WINDOW'
+ bl_label = "Bone Constraints"
+ bl_options = {'HIDE_HEADER'}
- @staticmethod
- def space_template(layout, con, target=True, owner=True):
- if target or owner:
+ def draw(self, context):
+ layout = self.layout
- split = layout.split(factor=0.2)
+ layout.operator_menu_enum("pose.constraint_add", "type", text="Add Bone Constraint")
- split.label(text="Space:")
- row = split.row()
+ layout.template_constraints(use_bone_constraints=True)
- if target:
- row.prop(con, "target_space", text="")
- if target and owner:
- row.label(icon='ARROW_LEFTRIGHT')
+# Parent class for constraint panels, with templates and drawing methods
+# shared between the bone and object constraint panels
+class ConstraintButtonsPanel(Panel):
+ bl_space_type = 'PROPERTIES'
+ bl_region_type = 'WINDOW'
+ bl_label = ""
+ bl_options = {'INSTANCED', 'HEADER_LAYOUT_EXPAND', 'DRAW_BOX'}
+ @staticmethod
+ def draw_influence(layout, con):
+ layout.separator()
+ if con.type in {'IK', 'SPLINE_IK'}:
+ # constraint.disable_keep_transform doesn't work well
+ # for these constraints.
+ layout.prop(con, "influence")
+ else:
+ row = layout.row(align=True)
+ row.prop(con, "influence")
+ row.operator("constraint.disable_keep_transform", text="", icon='CANCEL')
+
+ @staticmethod
+ def space_template(layout, con, target=True, owner=True):
+ if target or owner:
+ layout.separator()
+ if target:
+ layout.prop(con, "target_space", text="Target")
if owner:
- row.prop(con, "owner_space", text="")
+ layout.prop(con, "owner_space", text="Owner")
@staticmethod
def target_template(layout, con, subtargets=True):
- layout.prop(con, "target") # XXX limiting settings for only 'curves' or some type of object
+ col = layout.column()
+ col.prop(con, "target") # XXX limiting settings for only 'curves' or some type of object
if con.target and subtargets:
if con.target.type == 'ARMATURE':
- layout.prop_search(con, "subtarget", con.target.data, "bones", text="Bone")
+ col.prop_search(con, "subtarget", con.target.data, "bones", text="Bone")
if hasattr(con, "head_tail"):
- row = layout.row(align=True)
- row.label(text="Head/Tail:")
+ row = col.row(align=True, heading="Head/Tail")
+ row.use_property_decorate = False
row.prop(con, "head_tail", text="")
# XXX icon, and only when bone has segments?
row.prop(con, "use_bbone_shape", text="", icon='IPO_BEZIER')
elif con.target.type in {'MESH', 'LATTICE'}:
- layout.prop_search(con, "subtarget", con.target, "vertex_groups", text="Vertex Group")
+ col.prop_search(con, "subtarget", con.target, "vertex_groups", text="Vertex Group")
- @staticmethod
- def ik_template(layout, con):
- # only used for iTaSC
- layout.prop(con, "pole_target")
+ def get_constraint(self, context):
+ con = None
+ if context.pose_bone:
+ con = context.pose_bone.constraints[self.list_panel_index]
+ else:
+ con = context.object.constraints[self.list_panel_index]
+ self.layout.context_pointer_set("constraint", con)
+ return con
- if con.pole_target and con.pole_target.type == 'ARMATURE':
- layout.prop_search(con, "pole_subtarget", con.pole_target.data, "bones", text="Bone")
+ def draw_header(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
- if con.pole_target:
- row = layout.row()
- row.label()
- row.prop(con, "pole_angle")
+ layout.template_constraint_header(con)
- split = layout.split(factor=0.33)
- col = split.column()
- col.prop(con, "use_tail")
- col.prop(con, "use_stretch")
+ # Drawing methods for specific constraints. (Shared by object and bone constraint panels)
- col = split.column()
- col.prop(con, "chain_count")
+ def draw_childof(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- def CHILD_OF(self, _context, layout, con):
self.target_template(layout, con)
- split = layout.split()
-
- col = split.column()
- col.label(text="Location:")
- col.prop(con, "use_location_x", text="X")
- col.prop(con, "use_location_y", text="Y")
- col.prop(con, "use_location_z", text="Z")
-
- col = split.column()
- col.label(text="Rotation:")
- col.prop(con, "use_rotation_x", text="X")
- col.prop(con, "use_rotation_y", text="Y")
- col.prop(con, "use_rotation_z", text="Z")
-
- col = split.column()
- col.label(text="Scale:")
- col.prop(con, "use_scale_x", text="X")
- col.prop(con, "use_scale_y", text="Y")
- col.prop(con, "use_scale_z", text="Z")
+ row = layout.row(heading="Location")
+ row.use_property_decorate = False
+ row.prop(con, "use_location_x", text="X", toggle=True)
+ row.prop(con, "use_location_y", text="Y", toggle=True)
+ row.prop(con, "use_location_z", text="Z", toggle=True)
+ row.label(icon='BLANK1')
+
+ row = layout.row(heading="Rotation")
+ row.use_property_decorate = False
+ row.prop(con, "use_rotation_x", text="X", toggle=True)
+ row.prop(con, "use_rotation_y", text="Y", toggle=True)
+ row.prop(con, "use_rotation_z", text="Z", toggle=True)
+ row.label(icon='BLANK1')
+
+ row = layout.row(heading="Scale")
+ row.use_property_decorate = False
+ row.prop(con, "use_scale_x", text="X", toggle=True)
+ row.prop(con, "use_scale_y", text="Y", toggle=True)
+ row.prop(con, "use_scale_z", text="Z", toggle=True)
+ row.label(icon='BLANK1')
row = layout.row()
row.operator("constraint.childof_set_inverse")
row.operator("constraint.childof_clear_inverse")
- def TRACK_TO(self, _context, layout, con):
- self.target_template(layout, con)
-
- row = layout.row()
- row.label(text="To:")
- row.prop(con, "track_axis", expand=True)
-
- row = layout.row()
- row.prop(con, "up_axis", text="Up")
- row.prop(con, "use_target_z")
-
- self.space_template(layout, con)
-
- def IK(self, context, layout, con):
- if context.object.pose.ik_solver == 'ITASC':
- layout.prop(con, "ik_type")
- getattr(self, 'IK_' + con.ik_type)(context, layout, con)
- else:
- # Standard IK constraint
- self.target_template(layout, con)
- layout.prop(con, "pole_target")
-
- if con.pole_target and con.pole_target.type == 'ARMATURE':
- layout.prop_search(con, "pole_subtarget", con.pole_target.data, "bones", text="Bone")
-
- if con.pole_target:
- row = layout.row()
- row.prop(con, "pole_angle")
- row.label()
-
- split = layout.split()
- col = split.column()
- col.prop(con, "iterations")
- col.prop(con, "chain_count")
-
- col = split.column()
- col.prop(con, "use_tail")
- col.prop(con, "use_stretch")
-
- layout.label(text="Weight:")
-
- split = layout.split()
- col = split.column()
- row = col.row(align=True)
- row.prop(con, "use_location", text="")
- sub = row.row(align=True)
- sub.active = con.use_location
- sub.prop(con, "weight", text="Position", slider=True)
-
- col = split.column()
- row = col.row(align=True)
- row.prop(con, "use_rotation", text="")
- sub = row.row(align=True)
- sub.active = con.use_rotation
- sub.prop(con, "orient_weight", text="Rotation", slider=True)
+ self.draw_influence(layout, con)
- def IK_COPY_POSE(self, _context, layout, con):
- self.target_template(layout, con)
- self.ik_template(layout, con)
+ def draw_trackto(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- row = layout.row()
- row.label(text="Axis Ref:")
- row.prop(con, "reference_axis", expand=True)
- split = layout.split(factor=0.33)
- split.row().prop(con, "use_location")
- row = split.row()
- row.prop(con, "weight", text="Weight", slider=True)
- row.active = con.use_location
- split = layout.split(factor=0.33)
- row = split.row()
- row.label(text="Lock:")
- row = split.row()
- row.prop(con, "lock_location_x", text="X")
- row.prop(con, "lock_location_y", text="Y")
- row.prop(con, "lock_location_z", text="Z")
- split.active = con.use_location
-
- split = layout.split(factor=0.33)
- split.row().prop(con, "use_rotation")
- row = split.row()
- row.prop(con, "orient_weight", text="Weight", slider=True)
- row.active = con.use_rotation
- split = layout.split(factor=0.33)
- row = split.row()
- row.label(text="Lock:")
- row = split.row()
- row.prop(con, "lock_rotation_x", text="X")
- row.prop(con, "lock_rotation_y", text="Y")
- row.prop(con, "lock_rotation_z", text="Z")
- split.active = con.use_rotation
-
- def IK_DISTANCE(self, _context, layout, con):
self.target_template(layout, con)
- self.ik_template(layout, con)
- layout.prop(con, "limit_mode")
+ layout.prop(con, "track_axis", expand=True)
+ layout.prop(con, "up_axis", text="Up", expand=True)
+ layout.prop(con, "use_target_z")
- row = layout.row()
- row.prop(con, "weight", text="Weight", slider=True)
- row.prop(con, "distance", text="Distance", slider=True)
+ self.space_template(layout, con)
- def FOLLOW_PATH(self, _context, layout, con):
- self.target_template(layout, con)
- layout.operator("constraint.followpath_path_animate", text="Animate Path", icon='ANIM_DATA')
+ self.draw_influence(layout, con)
- split = layout.split()
+ def draw_follow_path(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- col = split.column()
- col.prop(con, "use_curve_follow")
- col.prop(con, "use_curve_radius")
+ self.target_template(layout, con)
- col = split.column()
- col.prop(con, "use_fixed_location")
if con.use_fixed_location:
- col.prop(con, "offset_factor", text="Offset")
+ layout.prop(con, "offset_factor", text="Offset Factor")
else:
- col.prop(con, "offset")
+ layout.prop(con, "offset")
- row = layout.row()
- row.label(text="Forward:")
- row.prop(con, "forward_axis", expand=True)
+ layout.prop(con, "forward_axis", expand=True)
+ layout.prop(con, "up_axis", expand=True)
- row = layout.row()
- row.prop(con, "up_axis", text="Up")
- row.label()
+ col = layout.column()
+ col.prop(con, "use_fixed_location")
+ col.prop(con, "use_curve_radius")
+ col.prop(con, "use_curve_follow")
- def LIMIT_ROTATION(self, _context, layout, con):
- split = layout.split()
+ layout.operator("constraint.followpath_path_animate", text="Animate Path", icon='ANIM_DATA')
- col = split.column(align=True)
- col.prop(con, "use_limit_x")
- sub = col.column(align=True)
+ self.draw_influence(layout, con)
+
+ def draw_rot_limit(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ # Decorators and property split are really buggy with these properties
+ row = layout.row(heading="Limit X", align=True)
+ row.use_property_decorate = False
+ row.prop(con, "use_limit_x", text="")
+ sub = row.column(align=True)
sub.active = con.use_limit_x
sub.prop(con, "min_x", text="Min")
sub.prop(con, "max_x", text="Max")
+ row.label(icon="BLANK1")
- col = split.column(align=True)
- col.prop(con, "use_limit_y")
- sub = col.column(align=True)
+ row = layout.row(heading="Y", align=True)
+ row.use_property_decorate = False
+ row.prop(con, "use_limit_y", text="")
+ sub = row.column(align=True)
sub.active = con.use_limit_y
sub.prop(con, "min_y", text="Min")
sub.prop(con, "max_y", text="Max")
+ row.label(icon="BLANK1")
- col = split.column(align=True)
- col.prop(con, "use_limit_z")
- sub = col.column(align=True)
+ row = layout.row(heading="Z", align=True)
+ row.use_property_decorate = False
+ row.prop(con, "use_limit_z", text="")
+ sub = row.column(align=True)
sub.active = con.use_limit_z
sub.prop(con, "min_z", text="Min")
sub.prop(con, "max_z", text="Max")
+ row.label(icon="BLANK1")
layout.prop(con, "use_transform_limit")
+ layout.prop(con, "owner_space")
- row = layout.row()
- row.label(text="Convert:")
- row.prop(con, "owner_space", text="")
-
- def LIMIT_LOCATION(self, _context, layout, con):
- split = layout.split()
-
- col = split.column()
- col.prop(con, "use_min_x")
- sub = col.column()
- sub.active = con.use_min_x
- sub.prop(con, "min_x", text="")
- col.prop(con, "use_max_x")
- sub = col.column()
- sub.active = con.use_max_x
- sub.prop(con, "max_x", text="")
-
- col = split.column()
- col.prop(con, "use_min_y")
- sub = col.column()
- sub.active = con.use_min_y
- sub.prop(con, "min_y", text="")
- col.prop(con, "use_max_y")
- sub = col.column()
- sub.active = con.use_max_y
- sub.prop(con, "max_y", text="")
-
- col = split.column()
- col.prop(con, "use_min_z")
- sub = col.column()
- sub.active = con.use_min_z
- sub.prop(con, "min_z", text="")
- col.prop(con, "use_max_z")
- sub = col.column()
- sub.active = con.use_max_z
- sub.prop(con, "max_z", text="")
+ self.draw_influence(layout, con)
- row = layout.row()
- row.prop(con, "use_transform_limit")
- row.label()
+ def draw_loc_limit(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- row = layout.row()
- row.label(text="Convert:")
- row.prop(con, "owner_space", text="")
-
- def LIMIT_SCALE(self, _context, layout, con):
- split = layout.split()
-
- col = split.column()
- col.prop(con, "use_min_x")
- sub = col.column()
- sub.active = con.use_min_x
- sub.prop(con, "min_x", text="")
- col.prop(con, "use_max_x")
- sub = col.column()
- sub.active = con.use_max_x
- sub.prop(con, "max_x", text="")
-
- col = split.column()
- col.prop(con, "use_min_y")
- sub = col.column()
- sub.active = con.use_min_y
- sub.prop(con, "min_y", text="")
- col.prop(con, "use_max_y")
- sub = col.column()
- sub.active = con.use_max_y
- sub.prop(con, "max_y", text="")
-
- col = split.column()
- col.prop(con, "use_min_z")
- sub = col.column()
- sub.active = con.use_min_z
- sub.prop(con, "min_z", text="")
- col.prop(con, "use_max_z")
- sub = col.column()
- sub.active = con.use_max_z
- sub.prop(con, "max_z", text="")
+ col = layout.column()
- row = layout.row()
- row.prop(con, "use_transform_limit")
- row.label()
+ row = col.row(heading="Minimum X", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_min_x", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_min_x
+ subsub.prop(con, "min_x", text="")
+ row.prop_decorator(con, "min_x")
+
+ row = col.row(heading="Y", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_min_y", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_min_y
+ subsub.prop(con, "min_y", text="")
+ row.prop_decorator(con, "min_y")
+
+ row = col.row(heading="Z", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_min_z", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_min_z
+ subsub.prop(con, "min_z", text="")
+ row.prop_decorator(con, "min_z")
+
+ col.separator()
+
+ row = col.row(heading="Maximum X", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_max_x", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_max_x
+ subsub.prop(con, "max_x", text="")
+ row.prop_decorator(con, "max_x")
+
+ row = col.row(heading="Y", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_max_y", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_max_y
+ subsub.prop(con, "max_y", text="")
+ row.prop_decorator(con, "max_y")
+
+ row = col.row(heading="Z", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_max_z", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_max_z
+ subsub.prop(con, "max_z", text="")
+ row.prop_decorator(con, "max_z")
- row = layout.row()
- row.label(text="Convert:")
- row.prop(con, "owner_space", text="")
+ layout.prop(con, "use_transform_limit")
+ layout.prop(con, "owner_space")
- def COPY_ROTATION(self, _context, layout, con):
- self.target_template(layout, con)
+ self.draw_influence(layout, con)
- layout.prop(con, "euler_order", text="Order")
+ def draw_size_limit(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ col = layout.column()
+
+ row = col.row(heading="Minimum X", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_min_x", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_min_x
+ subsub.prop(con, "min_x", text="")
+ row.prop_decorator(con, "min_x")
+
+ row = col.row(heading="Y", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_min_y", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_min_y
+ subsub.prop(con, "min_y", text="")
+ row.prop_decorator(con, "min_y")
+
+ row = col.row(heading="Z", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_min_z", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_min_z
+ subsub.prop(con, "min_z", text="")
+ row.prop_decorator(con, "min_z")
+
+ col.separator()
+
+ row = col.row(heading="Maximum X", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_max_x", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_max_x
+ subsub.prop(con, "max_x", text="")
+ row.prop_decorator(con, "max_x")
+
+ row = col.row(heading="Y", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_max_y", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_max_y
+ subsub.prop(con, "max_y", text="")
+ row.prop_decorator(con, "max_y")
+
+ row = col.row(heading="Z", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_max_z", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_max_z
+ subsub.prop(con, "max_z", text="")
+ row.prop_decorator(con, "max_z")
- split = layout.split()
- col = split.column()
- col.prop(con, "use_x", text="X")
- sub = col.column()
- sub.active = con.use_x
- sub.prop(con, "invert_x", text="Invert")
+ layout.prop(con, "use_transform_limit")
+ layout.prop(con, "owner_space")
- col = split.column()
- col.prop(con, "use_y", text="Y")
- sub = col.column()
- sub.active = con.use_y
- sub.prop(con, "invert_y", text="Invert")
+ self.draw_influence(layout, con)
- col = split.column()
- col.prop(con, "use_z", text="Z")
- sub = col.column()
- sub.active = con.use_z
- sub.prop(con, "invert_z", text="Invert")
+ def draw_rotate_like(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ self.target_template(layout, con)
+
+ layout.prop(con, "euler_order", text="Order")
+
+ row = layout.row(heading="Axis")
+ row.use_property_decorate = False
+ row.prop(con, "use_x", text="X", toggle=True)
+ row.prop(con, "use_y", text="Y", toggle=True)
+ row.prop(con, "use_z", text="Z", toggle=True)
+ row.label(icon='BLANK1')
+
+ row = layout.row(heading="Invert")
+ row.use_property_decorate = False
+ row.prop(con, "invert_x", text="X", toggle=True)
+ row.prop(con, "invert_y", text="Y", toggle=True)
+ row.prop(con, "invert_z", text="Z", toggle=True)
+ row.label(icon='BLANK1')
layout.prop(con, "mix_mode", text="Mix")
self.space_template(layout, con)
- def COPY_LOCATION(self, _context, layout, con):
- self.target_template(layout, con)
+ self.draw_influence(layout, con)
- split = layout.split()
+ def draw_locate_like(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- col = split.column()
- col.prop(con, "use_x", text="X")
- sub = col.column()
- sub.active = con.use_x
- sub.prop(con, "invert_x", text="Invert")
+ self.target_template(layout, con)
- col = split.column()
- col.prop(con, "use_y", text="Y")
- sub = col.column()
- sub.active = con.use_y
- sub.prop(con, "invert_y", text="Invert")
+ row = layout.row(heading="Axis")
+ row.use_property_decorate = False
+ row.prop(con, "use_x", text="X", toggle=True)
+ row.prop(con, "use_y", text="Y", toggle=True)
+ row.prop(con, "use_z", text="Z", toggle=True)
+ row.label(icon='BLANK1')
- col = split.column()
- col.prop(con, "use_z", text="Z")
- sub = col.column()
- sub.active = con.use_z
- sub.prop(con, "invert_z", text="Invert")
+ row = layout.row(heading="Invert")
+ row.use_property_decorate = False
+ row.prop(con, "invert_x", text="X", toggle=True)
+ row.prop(con, "invert_y", text="Y", toggle=True)
+ row.prop(con, "invert_z", text="Z", toggle=True)
+ row.label(icon='BLANK1')
layout.prop(con, "use_offset")
self.space_template(layout, con)
- def COPY_SCALE(self, _context, layout, con):
+ self.draw_influence(layout, con)
+
+ def draw_size_like(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
self.target_template(layout, con)
- row = layout.row(align=True)
- row.prop(con, "use_x", text="X")
- row.prop(con, "use_y", text="Y")
- row.prop(con, "use_z", text="Z")
+ row = layout.row(heading="Axis")
+ row.prop(con, "use_x", text="X", toggle=True)
+ row.prop(con, "use_y", text="Y", toggle=True)
+ row.prop(con, "use_z", text="Z", toggle=True)
- layout.prop(con, "power")
- layout.prop(con, "use_make_uniform")
+ col = layout.column()
+ col.prop(con, "power")
+ col.prop(con, "use_make_uniform")
- row = layout.row()
- row.prop(con, "use_offset")
- row = row.row()
+ col.prop(con, "use_offset")
+ row = col.row()
row.active = con.use_offset
row.prop(con, "use_add")
self.space_template(layout, con)
- def MAINTAIN_VOLUME(self, _context, layout, con):
+ self.draw_influence(layout, con)
+
+ def draw_same_volume(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
layout.prop(con, "mode")
- row = layout.row()
- row.label(text="Free:")
+ row = layout.row(heading="Free Axis")
row.prop(con, "free_axis", expand=True)
layout.prop(con, "volume")
- row = layout.row()
- row.label(text="Convert:")
- row.prop(con, "owner_space", text="")
-
- def COPY_TRANSFORMS(self, _context, layout, con):
- self.target_template(layout, con)
+ layout.prop(con, "owner_space")
- layout.prop(con, "mix_mode", text="Mix")
-
- self.space_template(layout, con)
+ self.draw_influence(layout, con)
- # def SCRIPT(self, context, layout, con):
+ def draw_trans_like(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- def ACTION(self, _context, layout, con):
self.target_template(layout, con)
- split = layout.split()
-
- col = split.column()
- col.label(text="From Target:")
- col.prop(con, "transform_channel", text="")
- col.prop(con, "target_space", text="")
-
- col = split.column()
- col.label(text="To Action:")
- col.prop(con, "action", text="")
- col.prop(con, "use_bone_object_action")
-
- split = layout.split()
-
- col = split.column(align=True)
- col.label(text="Target Range:")
- col.prop(con, "min", text="Min")
- col.prop(con, "max", text="Max")
-
- col = split.column(align=True)
- col.label(text="Action Range:")
- col.prop(con, "frame_start", text="Start")
- col.prop(con, "frame_end", text="End")
-
layout.prop(con, "mix_mode", text="Mix")
- def LOCKED_TRACK(self, _context, layout, con):
- self.target_template(layout, con)
+ self.space_template(layout, con)
- row = layout.row()
- row.label(text="To:")
- row.prop(con, "track_axis", expand=True)
+ self.draw_influence(layout, con)
- row = layout.row()
- row.label(text="Lock:")
- row.prop(con, "lock_axis", expand=True)
+ def draw_action(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- def LIMIT_DISTANCE(self, _context, layout, con):
self.target_template(layout, con)
- col = layout.column(align=True)
- col.prop(con, "distance")
- col.operator("constraint.limitdistance_reset")
-
- row = layout.row()
- row.label(text="Clamp Region:")
- row.prop(con, "limit_mode", text="")
+ layout.prop(con, "mix_mode", text="Mix")
- row = layout.row()
- row.prop(con, "use_transform_limit")
- row.label()
+ self.draw_influence(layout, con)
- self.space_template(layout, con)
+ def draw_lock_track(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- def STRETCH_TO(self, _context, layout, con):
self.target_template(layout, con)
- row = layout.row()
- row.prop(con, "rest_length", text="Rest Length")
- row.operator("constraint.stretchto_reset", text="Reset")
-
- layout.prop(con, "bulge", text="Volume Variation")
- split = layout.split()
- col = split.column(align=True)
- col.prop(con, "use_bulge_min", text="Volume Min")
- sub = col.column()
- sub.active = con.use_bulge_min
- sub.prop(con, "bulge_min", text="")
- col = split.column(align=True)
- col.prop(con, "use_bulge_max", text="Volume Max")
- sub = col.column()
- sub.active = con.use_bulge_max
- sub.prop(con, "bulge_max", text="")
- col = layout.column()
- col.active = con.use_bulge_min or con.use_bulge_max
- col.prop(con, "bulge_smooth", text="Smooth")
+ layout.prop(con, "track_axis", expand=True)
+ layout.prop(con, "lock_axis", expand=True)
- split = layout.split(factor=0.3)
- split.label(text="Volume:")
- row = split.row()
- row.prop(con, "volume", expand=True)
+ self.draw_influence(layout, con)
- split = layout.split(factor=0.3)
- split.label(text="Rotation:")
- row = split.row()
- row.prop(con, "keep_axis", expand=True)
+ def draw_dist_limit(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- def FLOOR(self, _context, layout, con):
self.target_template(layout, con)
- layout.prop(con, "use_rotation")
- layout.prop(con, "offset")
-
row = layout.row()
- row.label(text="Min/Max:")
- row.prop(con, "floor_location", expand=True)
+ row.prop(con, "distance")
+ row.operator("constraint.limitdistance_reset", text="", icon="X")
- self.space_template(layout, con)
-
- def RIGID_BODY_JOINT(self, _context, layout, con):
- self.target_template(layout, con, subtargets=False)
+ layout.prop(con, "limit_mode", text="Clamp Region")
- layout.prop(con, "pivot_type")
- layout.prop(con, "child")
+ layout.prop(con, "use_transform_limit")
- row = layout.row()
- row.prop(con, "use_linked_collision", text="Linked Collision")
- row.prop(con, "show_pivot", text="Display Pivot")
-
- split = layout.split()
-
- col = split.column(align=True)
- col.label(text="Pivot:")
- col.prop(con, "pivot_x", text="X")
- col.prop(con, "pivot_y", text="Y")
- col.prop(con, "pivot_z", text="Z")
-
- col = split.column(align=True)
- col.label(text="Axis:")
- col.prop(con, "axis_x", text="X")
- col.prop(con, "axis_y", text="Y")
- col.prop(con, "axis_z", text="Z")
-
- if con.pivot_type == 'CONE_TWIST':
- layout.label(text="Limits:")
- split = layout.split()
-
- col = split.column()
- col.prop(con, "use_angular_limit_x", text="Angle X")
- sub = col.column()
- sub.active = con.use_angular_limit_x
- sub.prop(con, "limit_angle_max_x", text="")
-
- col = split.column()
- col.prop(con, "use_angular_limit_y", text="Angle Y")
- sub = col.column()
- sub.active = con.use_angular_limit_y
- sub.prop(con, "limit_angle_max_y", text="")
-
- col = split.column()
- col.prop(con, "use_angular_limit_z", text="Angle Z")
- sub = col.column()
- sub.active = con.use_angular_limit_z
- sub.prop(con, "limit_angle_max_z", text="")
-
- elif con.pivot_type == 'GENERIC_6_DOF':
- layout.label(text="Limits:")
- split = layout.split()
-
- col = split.column(align=True)
- col.prop(con, "use_limit_x", text="X")
- sub = col.column(align=True)
- sub.active = con.use_limit_x
- sub.prop(con, "limit_min_x", text="Min")
- sub.prop(con, "limit_max_x", text="Max")
-
- col = split.column(align=True)
- col.prop(con, "use_limit_y", text="Y")
- sub = col.column(align=True)
- sub.active = con.use_limit_y
- sub.prop(con, "limit_min_y", text="Min")
- sub.prop(con, "limit_max_y", text="Max")
-
- col = split.column(align=True)
- col.prop(con, "use_limit_z", text="Z")
- sub = col.column(align=True)
- sub.active = con.use_limit_z
- sub.prop(con, "limit_min_z", text="Min")
- sub.prop(con, "limit_max_z", text="Max")
-
- split = layout.split()
-
- col = split.column(align=True)
- col.prop(con, "use_angular_limit_x", text="Angle X")
- sub = col.column(align=True)
- sub.active = con.use_angular_limit_x
- sub.prop(con, "limit_angle_min_x", text="Min")
- sub.prop(con, "limit_angle_max_x", text="Max")
-
- col = split.column(align=True)
- col.prop(con, "use_angular_limit_y", text="Angle Y")
- sub = col.column(align=True)
- sub.active = con.use_angular_limit_y
- sub.prop(con, "limit_angle_min_y", text="Min")
- sub.prop(con, "limit_angle_max_y", text="Max")
-
- col = split.column(align=True)
- col.prop(con, "use_angular_limit_z", text="Angle Z")
- sub = col.column(align=True)
- sub.active = con.use_angular_limit_z
- sub.prop(con, "limit_angle_min_z", text="Min")
- sub.prop(con, "limit_angle_max_z", text="Max")
-
- elif con.pivot_type == 'HINGE':
- layout.label(text="Limits:")
- split = layout.split()
+ self.space_template(layout, con)
- row = split.row(align=True)
- col = row.column()
- col.prop(con, "use_angular_limit_x", text="Angle X")
+ self.draw_influence(layout, con)
- col = row.column()
- col.active = con.use_angular_limit_x
- col.prop(con, "limit_angle_min_x", text="Min")
- col = row.column()
- col.active = con.use_angular_limit_x
- col.prop(con, "limit_angle_max_x", text="Max")
+ def draw_stretch_to(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- def CLAMP_TO(self, _context, layout, con):
self.target_template(layout, con)
row = layout.row()
- row.label(text="Main Axis:")
- row.prop(con, "main_axis", expand=True)
-
- layout.prop(con, "use_cyclic")
+ row.prop(con, "rest_length")
+ row.operator("constraint.stretchto_reset", text="", icon="X")
- def TRANSFORM(self, _context, layout, con):
- self.target_template(layout, con)
-
- layout.prop(con, "use_motion_extrapolate", text="Extrapolate")
+ layout.separator()
col = layout.column()
- col.row().label(text="Source:")
- col.row().prop(con, "map_from", expand=True)
+ col.prop(con, "bulge", text="Volume Variation")
+
+ row = col.row(heading="Volume Min", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_bulge_min", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_bulge_min
+ subsub.prop(con, "bulge_min", text="")
+ row.prop_decorator(con, "bulge_min")
+
+ row = col.row(heading="Max", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_bulge_max", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_bulge_max
+ subsub.prop(con, "bulge_max", text="")
+ row.prop_decorator(con, "bulge_max")
- if con.map_from == 'ROTATION':
- layout.prop(con, "from_rotation_mode", text="Mode")
-
- split = layout.split()
- ext = "" if con.map_from == 'LOCATION' else "_rot" if con.map_from == 'ROTATION' else "_scale"
-
- sub = split.column(align=True)
- sub.label(text="X:")
- sub.prop(con, "from_min_x" + ext, text="Min")
- sub.prop(con, "from_max_x" + ext, text="Max")
+ row = col.row()
+ row.active = con.use_bulge_min or con.use_bulge_max
+ row.prop(con, "bulge_smooth", text="Smooth")
- sub = split.column(align=True)
- sub.label(text="Y:")
- sub.prop(con, "from_min_y" + ext, text="Min")
- sub.prop(con, "from_max_y" + ext, text="Max")
+ layout.prop(con, "volume", expand=True)
+ layout.prop(con, "keep_axis", text="Rotation", expand=True)
- sub = split.column(align=True)
- sub.label(text="Z:")
- sub.prop(con, "from_min_z" + ext, text="Min")
- sub.prop(con, "from_max_z" + ext, text="Max")
+ self.draw_influence(layout, con)
- col = layout.column()
- row = col.row()
- row.label(text="Source to Destination Mapping:")
+ def draw_min_max(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- # note: chr(187) is the ASCII arrow ( >> ). Blender Text Editor can't
- # open it. Thus we are using the hard-coded value instead.
- row = col.row()
- row.prop(con, "map_to_x_from", expand=False, text="")
- row.label(text=" %s X" % chr(187))
+ self.target_template(layout, con)
- row = col.row()
- row.prop(con, "map_to_y_from", expand=False, text="")
- row.label(text=" %s Y" % chr(187))
+ layout.prop(con, "offset")
+ layout.prop(con, "floor_location", expand=True, text="Min/Max")
+ layout.prop(con, "use_rotation")
- row = col.row()
- row.prop(con, "map_to_z_from", expand=False, text="")
- row.label(text=" %s Z" % chr(187))
+ self.space_template(layout, con)
- split = layout.split()
+ self.draw_influence(layout, con)
- col = split.column()
- col.label(text="Destination:")
- col.row().prop(con, "map_to", expand=True)
+ def draw_clamp_to(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- if con.map_to == 'ROTATION':
- layout.prop(con, "to_euler_order", text="Order")
+ self.target_template(layout, con)
- split = layout.split()
- ext = "" if con.map_to == 'LOCATION' else "_rot" if con.map_to == 'ROTATION' else "_scale"
+ layout.prop(con, "main_axis", expand=True)
- col = split.column()
- col.label(text="X:")
+ layout.prop(con, "use_cyclic")
- sub = col.column(align=True)
- sub.prop(con, "to_min_x" + ext, text="Min")
- sub.prop(con, "to_max_x" + ext, text="Max")
+ self.draw_influence(layout, con)
- col = split.column()
- col.label(text="Y:")
+ def draw_transform(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- sub = col.column(align=True)
- sub.prop(con, "to_min_y" + ext, text="Min")
- sub.prop(con, "to_max_y" + ext, text="Max")
+ self.target_template(layout, con)
- col = split.column()
- col.label(text="Z:")
+ layout.prop(con, "use_motion_extrapolate", text="Extrapolate")
- sub = col.column(align=True)
- sub.prop(con, "to_min_z" + ext, text="Min")
- sub.prop(con, "to_max_z" + ext, text="Max")
+ self.space_template(layout, con)
- layout.prop(con, "mix_mode" + ext, text="Mix")
+ self.draw_influence(layout, con)
- self.space_template(layout, con)
+ def draw_shrinkwrap(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- def SHRINKWRAP(self, _context, layout, con):
self.target_template(layout, con, False)
layout.prop(con, "distance")
- layout.prop(con, "shrinkwrap_type")
+ layout.prop(con, "shrinkwrap_type", text="Mode")
- if con.shrinkwrap_type in {'PROJECT', 'NEAREST_SURFACE', 'TARGET_PROJECT'}:
- layout.prop(con, "wrap_mode", text="Snap Mode")
+ layout.separator()
if con.shrinkwrap_type == 'PROJECT':
- row = layout.row(align=True)
- row.prop(con, "project_axis", expand=True)
- split = layout.split(factor=0.4)
- split.label(text="Axis Space:")
- rowsub = split.row()
- rowsub.prop(con, "project_axis_space", text="")
- split = layout.split(factor=0.4)
- split.label(text="Face Culling:")
- rowsub = split.row()
- rowsub.prop(con, "cull_face", expand=True)
- row = layout.row()
- row.prop(con, "use_project_opposite")
- rowsub = row.row()
- rowsub.active = con.use_project_opposite and con.cull_face != 'OFF'
- rowsub.prop(con, "use_invert_cull")
- layout.prop(con, "project_limit")
+ layout.prop(con, "project_axis", expand=True, text="Project Axis")
+ layout.prop(con, "project_axis_space", text="Space")
+ layout.prop(con, "project_limit", text="Distance")
+ layout.prop(con, "use_project_opposite")
- if con.shrinkwrap_type in {'PROJECT', 'NEAREST_SURFACE', 'TARGET_PROJECT'}:
- layout.prop(con, "use_track_normal")
+ layout.separator()
- row = layout.row(align=True)
- row.active = con.use_track_normal
- row.prop(con, "track_axis", expand=True)
+ col = layout.column()
+ row = col.row()
+ row.prop(con, "cull_face", expand=True)
+ row = col.row()
+ row.active = con.use_project_opposite and con.cull_face != 'OFF'
+ row.prop(con, "use_invert_cull")
- def DAMPED_TRACK(self, _context, layout, con):
- self.target_template(layout, con)
+ layout.separator()
- row = layout.row()
- row.label(text="To:")
- row.prop(con, "track_axis", expand=True)
+ if con.shrinkwrap_type in {'PROJECT', 'NEAREST_SURFACE', 'TARGET_PROJECT'}:
+ layout.prop(con, "wrap_mode", text="Snap Mode")
+ row = layout.row(heading="Align to Normal", align=True)
+ row.use_property_decorate = False
+ sub = row.row(align=True)
+ sub.prop(con, "use_track_normal", text="")
+ subsub = sub.row(align=True)
+ subsub.active = con.use_track_normal
+ subsub.prop(con, "track_axis", text="")
+ row.prop_decorator(con, "track_axis")
+
+ self.draw_influence(layout, con)
+
+ def draw_damp_track(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- def SPLINE_IK(self, _context, layout, con):
self.target_template(layout, con)
- col = layout.column()
- col.label(text="Spline Fitting:")
- col.prop(con, "chain_count")
- col.prop(con, "use_even_divisions")
- col.prop(con, "use_chain_offset")
+ layout.prop(con, "track_axis", expand=True)
- col = layout.column()
- col.label(text="Chain Scaling:")
- col.prop(con, "use_curve_radius")
+ self.draw_influence(layout, con)
- layout.prop(con, "y_scale_mode")
- layout.prop(con, "xz_scale_mode")
+ def draw_spline_ik(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- if con.xz_scale_mode in {'INVERSE_PRESERVE', 'VOLUME_PRESERVE'}:
- layout.prop(con, "use_original_scale")
+ self.target_template(layout, con)
- if con.xz_scale_mode == 'VOLUME_PRESERVE':
- layout.prop(con, "bulge", text="Volume Variation")
- split = layout.split()
- col = split.column(align=True)
- col.prop(con, "use_bulge_min", text="Volume Min")
- sub = col.column()
- sub.active = con.use_bulge_min
- sub.prop(con, "bulge_min", text="")
- col = split.column(align=True)
- col.prop(con, "use_bulge_max", text="Volume Max")
- sub = col.column()
- sub.active = con.use_bulge_max
- sub.prop(con, "bulge_max", text="")
- col = layout.column()
- col.active = con.use_bulge_min or con.use_bulge_max
- col.prop(con, "bulge_smooth", text="Smooth")
+ self.draw_influence(layout, con)
+
+ def draw_pivot(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- def PIVOT(self, _context, layout, con):
self.target_template(layout, con)
if con.target:
- col = layout.column()
- col.prop(con, "offset", text="Pivot Offset")
+ layout.prop(con, "offset", text="Pivot Offset")
else:
- col = layout.column()
- col.prop(con, "use_relative_location")
+ layout.prop(con, "use_relative_location")
if con.use_relative_location:
- col.prop(con, "offset", text="Relative Pivot Point")
+ layout.prop(con, "offset", text="Pivot Point")
else:
- col.prop(con, "offset", text="Absolute Pivot Point")
+ layout.prop(con, "offset", text="Pivot Point")
col = layout.column()
- col.prop(con, "rotation_range", text="Pivot When")
+ col.prop(con, "rotation_range", text="Rotation Range")
- @staticmethod
- def _getConstraintClip(context, con):
- if not con.use_active_clip:
- return con.clip
+ self.draw_influence(layout, con)
+
+ def draw_follow_track(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ clip = None
+ if con.use_active_clip:
+ clip = context.scene.active_clip
else:
- return context.scene.active_clip
+ clip = con.clip
- def FOLLOW_TRACK(self, context, layout, con):
- clip = self._getConstraintClip(context, con)
+ layout.prop(con, "use_active_clip")
+ layout.prop(con, "use_3d_position")
row = layout.row()
- row.prop(con, "use_active_clip")
- row.prop(con, "use_3d_position")
-
- sub = row.column()
- sub.active = not con.use_3d_position
- sub.prop(con, "use_undistorted_position")
+ row.active = not con.use_3d_position
+ row.prop(con, "use_undistorted_position")
- col = layout.column()
if not con.use_active_clip:
- col.prop(con, "clip")
+ layout.prop(con, "clip")
- row = col.row()
- row.prop(con, "frame_method", expand=True)
+ layout.prop(con, "frame_method")
if clip:
tracking = clip.tracking
- col.prop_search(con, "object", tracking, "objects", icon='OBJECT_DATA')
+ layout.prop_search(con, "object", tracking, "objects", icon='OBJECT_DATA')
tracking_object = tracking.objects.get(con.object, tracking.objects[0])
- col.prop_search(con, "track", tracking_object, "tracks", icon='ANIM_DATA')
+ layout.prop_search(con, "track", tracking_object, "tracks", icon='ANIM_DATA')
- col.prop(con, "camera")
+ layout.prop(con, "camera")
- row = col.row()
+ row = layout.row()
row.active = not con.use_3d_position
row.prop(con, "depth_object")
layout.operator("clip.constraint_to_fcurve")
- def CAMERA_SOLVER(self, _context, layout, con):
+ self.draw_influence(layout, con)
+
+ def draw_camera_solver(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
layout.prop(con, "use_active_clip")
if not con.use_active_clip:
@@ -915,8 +774,19 @@ class ConstraintButtonsPanel:
layout.operator("clip.constraint_to_fcurve")
- def OBJECT_SOLVER(self, context, layout, con):
- clip = self._getConstraintClip(context, con)
+ self.draw_influence(layout, con)
+
+ def draw_object_solver(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ clip = None
+ if con.use_active_clip:
+ clip = context.scene.active_clip
+ else:
+ clip = con.clip
layout.prop(con, "use_active_clip")
@@ -934,36 +804,236 @@ class ConstraintButtonsPanel:
layout.operator("clip.constraint_to_fcurve")
- def TRANSFORM_CACHE(self, _context, layout, con):
- layout.label(text="Cache File Properties:")
- box = layout.box()
- box.template_cache_file(con, "cache_file")
+ self.draw_influence(layout, con)
- cache_file = con.cache_file
+ def draw_transform_cache(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ layout.template_cache_file(con, "cache_file")
- layout.label(text="Constraint Properties:")
- box = layout.box()
+ cache_file = con.cache_file
if cache_file is not None:
- box.prop_search(con, "object_path", cache_file, "object_paths")
+ layout.prop_search(con, "object_path", cache_file, "object_paths")
- def SCRIPT(self, _context, layout, _con):
+ self.draw_influence(layout, con)
+
+ def draw_python_constraint(self, context):
+ layout = self.layout
layout.label(text="Blender 2.6 doesn't support python constraints yet")
- def ARMATURE(self, context, layout, con):
- topcol = layout.column()
- topcol.use_property_split = True
- topcol.operator("constraint.add_target", text="Add Target Bone")
+ def draw_armature(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ col = layout.column()
+ col.prop(con, "use_deform_preserve_volume")
+ col.prop(con, "use_bone_envelopes")
+
+ if context.pose_bone:
+ col.prop(con, "use_current_location")
+
+ layout.operator("constraint.add_target", text="Add Target Bone")
+
+ layout.operator("constraint.normalize_target_weights")
+
+ self.draw_influence(layout, con)
if not con.targets:
- box = topcol.box()
- box.label(text="No target bones were added", icon='ERROR')
+ layout.label(text="No target bones added", icon='ERROR')
- for i, tgt in enumerate(con.targets):
- box = topcol.box()
+ def draw_kinematic(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ self.target_template(layout, con)
+
+ if context.object.pose.ik_solver == 'ITASC':
+ layout.prop(con, "ik_type")
+
+ layout.prop(con, "pole_target")
+
+ if con.pole_target and con.pole_target.type == 'ARMATURE':
+ layout.prop_search(con, "pole_subtarget", con.pole_target.data, "bones", text="Bone")
+
+ if con.pole_target:
+ row = layout.row()
+ row.label()
+ row.prop(con, "pole_angle")
+
+ col = layout.column()
+ col.prop(con, "use_tail")
+ col.prop(con, "use_stretch")
+ col.prop(con, "chain_count")
+
+ if con.ik_type == 'COPY_POSE':
+ layout.prop(con, "reference_axis", expand=True)
+
+ col = layout.column()
+ col.prop(con, "use_location")
+
+ sub = col.column()
+ sub.active = con.use_location
+ sub.prop(con, "weight", text="Weight", slider=True)
+ row = sub.row(heading="Lock")
+ row.use_property_decorate = False
+ row.prop(con, "lock_location_x", text="X", toggle=True)
+ row.prop(con, "lock_location_y", text="Y", toggle=True)
+ row.prop(con, "lock_location_z", text="Z", toggle=True)
+ row.label(icon='BLANK1')
+
+ col = layout.column()
+ col.prop(con, "use_rotation")
+
+ sub = col.column()
+ sub.active = con.use_rotation
+ sub.prop(con, "orient_weight", text="Weight", slider=True)
+ row = sub.row(heading="Lock")
+ row.use_property_decorate = False
+ row.prop(con, "lock_rotation_x", text="X", toggle=True)
+ row.prop(con, "lock_rotation_y", text="Y", toggle=True)
+ row.prop(con, "lock_rotation_z", text="Z", toggle=True)
+ row.label(icon='BLANK1')
+
+ elif con.ik_type == 'DISTANCE':
+ layout.prop(con, "limit_mode")
+
+ col = layout.column()
+ col.prop(con, "weight", text="Weight", slider=True)
+ col.prop(con, "distance", text="Distance", slider=True)
+ else:
+ # Standard IK constraint
+ layout.prop(con, "pole_target")
+
+ if con.pole_target and con.pole_target.type == 'ARMATURE':
+ layout.prop_search(con, "pole_subtarget", con.pole_target.data, "bones", text="Bone")
+
+ if con.pole_target:
+ row = layout.row()
+ row.prop(con, "pole_angle")
+ row.label()
+
+ col = layout.column()
+ col.prop(con, "iterations")
+ col.prop(con, "chain_count")
+ col.prop(con, "use_tail")
+ col.prop(con, "use_stretch")
+
+ col = layout.column()
+ row = col.row(align=True, heading="Weight Position")
+ row.prop(con, "use_location", text="")
+ sub = row.row(align=True)
+ sub.active = con.use_location
+ sub.prop(con, "weight", text="", slider=True)
+
+ row = col.row(align=True, heading="Rotation")
+ row.prop(con, "use_rotation", text="")
+ sub = row.row(align=True)
+ sub.active = con.use_rotation
+ sub.prop(con, "orient_weight", text="", slider=True)
+
+ self.draw_influence(layout, con)
+
+# Parent class for constraint subpanels
+class ConstraintButtonsSubPanel(Panel):
+ bl_space_type = 'PROPERTIES'
+ bl_region_type = 'WINDOW'
+ bl_label = ""
+ bl_options = {'DRAW_BOX'}
+
+ def get_constraint(self, context):
+ con = None
+ if context.pose_bone:
+ con = context.pose_bone.constraints[self.list_panel_index]
+ else:
+ con = context.active.constraints[self.list_panel_index]
+ self.layout.context_pointer_set("constraint", con)
+ return con
+
+ def draw_transform_source(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+
+ layout.prop(con, "map_from", expand=True)
+
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ if con.map_from == 'ROTATION':
+ layout.prop(con, "from_rotation_mode", text="Mode")
+
+ ext = "" if con.map_from == 'LOCATION' else "_rot" if con.map_from == 'ROTATION' else "_scale"
+
+ col = layout.column(align=True)
+ col.prop(con, "from_min_x" + ext, text="X Min")
+ col.prop(con, "from_max_x" + ext, text="Max")
+
+ col = layout.column(align=True)
+ col.prop(con, "from_min_y" + ext, text="Y Min")
+ col.prop(con, "from_max_y" + ext, text="Max")
+
+ col = layout.column(align=True)
+ col.prop(con, "from_min_z" + ext, text="Z Min")
+ col.prop(con, "from_max_z" + ext, text="Max")
+
+ def draw_transform_mapping(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ layout.prop(con, "map_to_x_from", expand=False, text="Source Axis X")
+
+ layout.prop(con, "map_to_y_from", expand=False, text="Y")
+
+ layout.prop(con, "map_to_z_from", expand=False, text="Z")
+
+ def draw_transform_destination(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+
+ layout.prop(con, "map_to", expand=True)
+
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ if con.map_to == 'ROTATION':
+ layout.prop(con, "to_euler_order", text="Order")
+
+ ext = "" if con.map_to == 'LOCATION' else "_rot" if con.map_to == 'ROTATION' else "_scale"
+
+ col = layout.column(align=True)
+ col.prop(con, "to_min_x" + ext, text="X Min")
+ col.prop(con, "to_max_x" + ext, text="Max")
+
+ col = layout.column(align=True)
+ col.prop(con, "to_min_y" + ext, text="Y Min")
+ col.prop(con, "to_max_y" + ext, text="Max")
+
+ col = layout.column(align=True)
+ col.prop(con, "to_min_z" + ext, text="Z Min")
+ col.prop(con, "to_max_z" + ext, text="Max")
+
+ layout.prop(con, "mix_mode" + ext, text="Mix")
+
+ def draw_armature_bones(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
+
+ for i, tgt in enumerate(con.targets):
has_target = tgt.target is not None
+ box = layout.box()
header = box.row()
header.use_property_split = False
@@ -977,61 +1047,615 @@ class ConstraintButtonsPanel:
else:
row.prop(tgt, "subtarget", text="", icon='BONE_DATA')
- header.operator("constraint.remove_target", text="", icon='REMOVE').index = i
+ header.operator("constraint.remove_target", text="", icon='X').index = i
- col = box.column()
- col.active = has_target and tgt.subtarget != ""
- col.prop(tgt, "weight", slider=True)
+ row = box.row()
+ row.active = has_target and tgt.subtarget != ""
+ row.prop(tgt, "weight", slider=True, text="Weight")
- topcol.operator("constraint.normalize_target_weights")
- topcol.prop(con, "use_deform_preserve_volume")
- topcol.prop(con, "use_bone_envelopes")
+ def draw_spline_ik_fitting(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- if context.pose_bone:
- topcol.prop(con, "use_current_location")
+ col = layout.column()
+ col.prop(con, "chain_count")
+ col.prop(con, "use_even_divisions")
+ col.prop(con, "use_chain_offset")
+ def draw_spline_ik_chain_scaling(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
-class OBJECT_PT_constraints(ConstraintButtonsPanel, Panel):
- bl_label = "Object Constraints"
- bl_context = "constraint"
- bl_options = {'HIDE_HEADER'}
+ layout.prop(con, "use_curve_radius")
- @classmethod
- def poll(cls, context):
- return (context.object)
+ layout.prop(con, "y_scale_mode")
+ layout.prop(con, "xz_scale_mode")
- def draw(self, context):
+ if con.xz_scale_mode in {'INVERSE_PRESERVE', 'VOLUME_PRESERVE'}:
+ layout.prop(con, "use_original_scale")
+
+ if con.xz_scale_mode == 'VOLUME_PRESERVE':
+ col = layout.column()
+ col.prop(con, "bulge", text="Volume Variation")
+
+ row = col.row(heading="Volume Min")
+ row.prop(con, "use_bulge_min", text="")
+ sub = row.row()
+ sub.active = con.use_bulge_min
+ sub.prop(con, "bulge_min", text="")
+
+ row = col.row(heading="Max")
+ row.prop(con, "use_bulge_max", text="")
+ sub = row.row()
+ sub.active = con.use_bulge_max
+ sub.prop(con, "bulge_max", text="")
+
+ row = layout.row()
+ row.active = con.use_bulge_min or con.use_bulge_max
+ row.prop(con, "bulge_smooth", text="Smooth")
+
+ def draw_action_target(self, context):
layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
- obj = context.object
+ layout.prop(con, "transform_channel", text="Channel")
+ layout.prop(con, "target_space")
- layout.operator_menu_enum("object.constraint_add", "type", text="Add Object Constraint")
+ col = layout.column(align=True)
+ col.prop(con, "min", text="Range Min")
+ col.prop(con, "max", text="Max")
- for con in obj.constraints:
- self.draw_constraint(context, con)
+ def draw_action_action(self, context):
+ layout = self.layout
+ con = self.get_constraint(context)
+ layout.use_property_split = True
+ layout.use_property_decorate = True
-class BONE_PT_constraints(ConstraintButtonsPanel, Panel):
- bl_label = "Bone Constraints"
- bl_context = "bone_constraint"
- bl_options = {'HIDE_HEADER'}
+ layout.prop(con, "action")
+ layout.prop(con, "use_bone_object_action")
- @classmethod
- def poll(cls, context):
- return (context.pose_bone)
+ col = layout.column(align=True)
+ col.prop(con, "frame_start", text="Frame Start")
+ col.prop(con, "frame_end", text="End")
+
+# Child Of Constraint
+
+class OBJECT_PT_bChildOfConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
def draw(self, context):
- layout = self.layout
+ self.draw_childof(context)
- layout.operator_menu_enum("pose.constraint_add", "type", text="Add Bone Constraint")
- for con in context.pose_bone.constraints:
- self.draw_constraint(context, con)
+class BONE_PT_bChildOfConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_childof(context)
+
+# Track To Constraint
+
+class OBJECT_PT_bTrackToConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_trackto(context)
+
+
+class BONE_PT_bTrackToConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_trackto(context)
+
+# Follow Path Constraint
+
+class OBJECT_PT_bFollowPathConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_follow_path(context)
+
+
+class BONE_PT_bFollowPathConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_follow_path(context)
+
+
+# Roation Limit Constraint
+
+class OBJECT_PT_bRotLimitConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_rot_limit(context)
+
+
+class BONE_PT_bRotLimitConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_rot_limit(context)
+
+
+# Location Limit Constraint
+
+class OBJECT_PT_bLocLimitConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_loc_limit(context)
+
+
+class BONE_PT_bLocLimitConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_loc_limit(context)
+
+
+# Size Limit Constraint
+
+class OBJECT_PT_bSizeLimitConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_size_limit(context)
+
+
+class BONE_PT_bSizeLimitConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_size_limit(context)
+
+
+# Rotate Like Constraint
+
+class OBJECT_PT_bRotateLikeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_rotate_like(context)
+
+
+class BONE_PT_bRotateLikeConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_rotate_like(context)
+
+
+# Locate Like Constraint
+
+class OBJECT_PT_bLocateLikeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_locate_like(context)
+
+
+class BONE_PT_bLocateLikeConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_locate_like(context)
+
+
+# Size Like Constraint
+
+class OBJECT_PT_bSizeLikeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_size_like(context)
+
+
+class BONE_PT_bSizeLikeConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_size_like(context)
+
+
+# Same Volume Constraint
+
+class OBJECT_PT_bSameVolumeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_same_volume(context)
+
+
+class BONE_PT_bSameVolumeConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_same_volume(context)
+
+
+# Trans Like Constraint
+
+class OBJECT_PT_bTransLikeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_trans_like(context)
+
+
+class BONE_PT_bTransLikeConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_trans_like(context)
+
+
+# Action Constraint
+
+class OBJECT_PT_bActionConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_action(context)
+
+
+class BONE_PT_bActionConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_action(context)
+
+
+class OBJECT_PT_bActionConstraint_target(ObjectConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "OBJECT_PT_bActionConstraint"
+ bl_label = "Target"
+
+ def draw(self, context):
+ self.draw_action_target(context)
+
+
+class BONE_PT_bActionConstraint_target(BoneConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "BONE_PT_bActionConstraint"
+ bl_label = "Target"
+
+ def draw(self, context):
+ self.draw_action_target(context)
+
+
+class OBJECT_PT_bActionConstraint_action(ObjectConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "OBJECT_PT_bActionConstraint"
+ bl_label = "Action"
+
+ def draw(self, context):
+ self.draw_action_action(context)
+
+
+class BONE_PT_bActionConstraint_action(BoneConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "BONE_PT_bActionConstraint"
+ bl_label = "Action"
+
+ def draw(self, context):
+ self.draw_action_action(context)
+
+
+# Lock Track Constraint
+
+class OBJECT_PT_bLockTrackConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_lock_track(context)
+
+
+class BONE_PT_bLockTrackConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_lock_track(context)
+
+
+# Disance Limit Constraint
+
+class OBJECT_PT_bDistLimitConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_dist_limit(context)
+
+
+class BONE_PT_bDistLimitConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_dist_limit(context)
+
+
+# Stretch To Constraint
+
+class OBJECT_PT_bStretchToConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_stretch_to(context)
+
+
+class BONE_PT_bStretchToConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_stretch_to(context)
+
+
+# Min Max Constraint
+
+class OBJECT_PT_bMinMaxConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_min_max(context)
+
+
+class BONE_PT_bMinMaxConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_min_max(context)
+
+
+# Clamp To Constraint
+
+class OBJECT_PT_bClampToConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_clamp_to(context)
+
+
+class BONE_PT_bClampToConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_clamp_to(context)
+
+
+# Transform Constraint
+
+class OBJECT_PT_bTransformConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_transform(context)
+
+
+class BONE_PT_bTransformConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_transform(context)
+
+
+class OBJECT_PT_bTransformConstraint_source(ObjectConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "OBJECT_PT_bTransformConstraint"
+ bl_label = "Source"
+
+ def draw(self, context):
+ self.draw_transform_source(context)
+
+
+class BONE_PT_bTransformConstraint_source(BoneConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "BONE_PT_bTransformConstraint"
+ bl_label = "Source"
+
+ def draw(self, context):
+ self.draw_transform_source(context)
+
+
+class OBJECT_PT_bTransformConstraint_mapping(ObjectConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "OBJECT_PT_bTransformConstraint"
+ bl_label = "Mapping"
+
+ def draw(self, context):
+ self.draw_transform_mapping(context)
+
+
+class BONE_PT_bTransformConstraint_mapping(BoneConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "BONE_PT_bTransformConstraint"
+ bl_label = "Mapping"
+
+ def draw(self, context):
+ self.draw_transform_mapping(context)
+
+
+class OBJECT_PT_bTransformConstraint_destination(ObjectConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "OBJECT_PT_bTransformConstraint"
+ bl_label = "Destination"
+
+ def draw(self, context):
+ self.draw_transform_destination(context)
+
+
+class BONE_PT_bTransformConstraint_destination(BoneConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "BONE_PT_bTransformConstraint"
+ bl_label = "Destination"
+
+ def draw(self, context):
+ self.draw_transform_destination(context)
+
+
+# Shrinkwrap Constraint
+
+class OBJECT_PT_bShrinkwrapConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_shrinkwrap(context)
+
+
+class BONE_PT_bShrinkwrapConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_shrinkwrap(context)
+
+
+# Damp Track Constraint
+
+class OBJECT_PT_bDampTrackConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_damp_track(context)
+
+
+class BONE_PT_bDampTrackConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_damp_track(context)
+
+
+# Spline IK Constraint
+
+class BONE_PT_bSplineIKConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_spline_ik(context)
+
+
+class BONE_PT_bSplineIKConstraint_fitting(BoneConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "BONE_PT_bSplineIKConstraint"
+ bl_label = "Fitting"
+
+ def draw(self, context):
+ self.draw_spline_ik_fitting(context)
+
+
+class BONE_PT_bSplineIKConstraint_chain_scaling(BoneConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "BONE_PT_bSplineIKConstraint"
+ bl_label = "Chain Scaling"
+
+ def draw(self, context):
+ self.draw_spline_ik_chain_scaling(context)
+
+
+# Pivot Constraint
+
+class OBJECT_PT_bPivotConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_pivot(context)
+
+
+class BONE_PT_bPivotConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_pivot(context)
+
+
+# Follow Track Constraint
+
+class OBJECT_PT_bFollowTrackConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_follow_track(context)
+
+
+class BONE_PT_bFollowTrackConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_follow_track(context)
+
+
+# Camera Solver Constraint
+
+class OBJECT_PT_bCameraSolverConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_camera_solver(context)
+
+
+class BONE_PT_bCameraSolverConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_camera_solver(context)
+
+
+# Object Solver Constraint
+
+class OBJECT_PT_bObjectSolverConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_object_solver(context)
+
+
+class BONE_PT_bObjectSolverConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_object_solver(context)
+
+
+# Transform Cache Constraint
+
+class OBJECT_PT_bTransformCacheConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_transform_cache(context)
+
+
+class BONE_PT_bTransformCacheConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_transform_cache(context)
+
+
+# Python Constraint
+
+class OBJECT_PT_bPythonConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_python_constraint(context)
+
+class BONE_PT_bPythonConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_python_constraint(context)
+
+
+
+# Armature Constraint
+
+class OBJECT_PT_bArmatureConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_armature(context)
+
+
+class BONE_PT_bArmatureConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_armature(context)
+
+
+class OBJECT_PT_bArmatureConstraint_bones(ObjectConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "OBJECT_PT_bArmatureConstraint"
+ bl_label = "Bones"
+
+ def draw(self, context):
+ self.draw_armature_bones(context)
+
+
+class BONE_PT_bArmatureConstraint_bones(BoneConstraintPanel, ConstraintButtonsSubPanel):
+ bl_parent_id = "BONE_PT_bArmatureConstraint"
+ bl_label = "Bones"
+
+ def draw(self, context):
+ self.draw_armature_bones(context)
+
+
+# Inverse Kinematic Constraint
+
+class OBJECT_PT_bKinematicConstraint(ObjectConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_kinematic(context)
+
+
+class BONE_PT_bKinematicConstraint(BoneConstraintPanel, ConstraintButtonsPanel):
+ def draw(self, context):
+ self.draw_kinematic(context)
+
classes = (
+ # Object Panels
OBJECT_PT_constraints,
BONE_PT_constraints,
+ OBJECT_PT_bChildOfConstraint,
+ OBJECT_PT_bTrackToConstraint,
+ OBJECT_PT_bKinematicConstraint,
+ OBJECT_PT_bFollowPathConstraint,
+ OBJECT_PT_bRotLimitConstraint,
+ OBJECT_PT_bLocLimitConstraint,
+ OBJECT_PT_bSizeLimitConstraint,
+ OBJECT_PT_bRotateLikeConstraint,
+ OBJECT_PT_bLocateLikeConstraint,
+ OBJECT_PT_bSizeLikeConstraint,
+ OBJECT_PT_bSameVolumeConstraint,
+ OBJECT_PT_bTransLikeConstraint,
+ OBJECT_PT_bActionConstraint,
+ OBJECT_PT_bActionConstraint_target,
+ OBJECT_PT_bActionConstraint_action,
+ OBJECT_PT_bLockTrackConstraint,
+ OBJECT_PT_bDistLimitConstraint,
+ OBJECT_PT_bStretchToConstraint,
+ OBJECT_PT_bMinMaxConstraint,
+ OBJECT_PT_bClampToConstraint,
+ OBJECT_PT_bTransformConstraint,
+ OBJECT_PT_bTransformConstraint_source,
+ OBJECT_PT_bTransformConstraint_mapping,
+ OBJECT_PT_bTransformConstraint_destination,
+ OBJECT_PT_bShrinkwrapConstraint,
+ OBJECT_PT_bDampTrackConstraint,
+ OBJECT_PT_bPivotConstraint,
+ OBJECT_PT_bFollowTrackConstraint,
+ OBJECT_PT_bCameraSolverConstraint,
+ OBJECT_PT_bObjectSolverConstraint,
+ OBJECT_PT_bTransformCacheConstraint,
+ OBJECT_PT_bPythonConstraint,
+ OBJECT_PT_bArmatureConstraint,
+ OBJECT_PT_bArmatureConstraint_bones,
+ # Bone panels
+ BONE_PT_bChildOfConstraint,
+ BONE_PT_bTrackToConstraint,
+ BONE_PT_bKinematicConstraint,
+ BONE_PT_bFollowPathConstraint,
+ BONE_PT_bRotLimitConstraint,
+ BONE_PT_bLocLimitConstraint,
+ BONE_PT_bSizeLimitConstraint,
+ BONE_PT_bRotateLikeConstraint,
+ BONE_PT_bLocateLikeConstraint,
+ BONE_PT_bSizeLikeConstraint,
+ BONE_PT_bSameVolumeConstraint,
+ BONE_PT_bTransLikeConstraint,
+ BONE_PT_bActionConstraint,
+ BONE_PT_bActionConstraint_target,
+ BONE_PT_bActionConstraint_action,
+ BONE_PT_bLockTrackConstraint,
+ BONE_PT_bDistLimitConstraint,
+ BONE_PT_bStretchToConstraint,
+ BONE_PT_bMinMaxConstraint,
+ BONE_PT_bClampToConstraint,
+ BONE_PT_bTransformConstraint,
+ BONE_PT_bTransformConstraint_source,
+ BONE_PT_bTransformConstraint_mapping,
+ BONE_PT_bTransformConstraint_destination,
+ BONE_PT_bShrinkwrapConstraint,
+ BONE_PT_bDampTrackConstraint,
+ BONE_PT_bSplineIKConstraint,
+ BONE_PT_bSplineIKConstraint_fitting,
+ BONE_PT_bSplineIKConstraint_chain_scaling,
+ BONE_PT_bPivotConstraint,
+ BONE_PT_bFollowTrackConstraint,
+ BONE_PT_bCameraSolverConstraint,
+ BONE_PT_bObjectSolverConstraint,
+ BONE_PT_bTransformCacheConstraint,
+ BONE_PT_bPythonConstraint,
+ BONE_PT_bArmatureConstraint,
+ BONE_PT_bArmatureConstraint_bones,
)
if __name__ == "__main__": # only for live edit.
diff --git a/source/blender/blenkernel/BKE_constraint.h b/source/blender/blenkernel/BKE_constraint.h
index 8fe3bd77a26..020b0f8b913 100644
--- a/source/blender/blenkernel/BKE_constraint.h
+++ b/source/blender/blenkernel/BKE_constraint.h
@@ -136,6 +136,9 @@ typedef struct bConstraintTypeInfo {
const bConstraintTypeInfo *BKE_constraint_typeinfo_get(struct bConstraint *con);
const bConstraintTypeInfo *BKE_constraint_typeinfo_from_type(int type);
+void BKE_constraint_panel_id(int type, char *r_name);
+void BKE_constraint_bone_panel_id(int type, char *r_name);
+
/* ---------------------------------------------------------------------------- */
/* Constraint function prototypes */
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 050e8d434ae..f215297c8a2 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -5412,9 +5412,16 @@ static bConstraint *add_new_constraint_internal(const char *name, short type)
/* Set up a generic constraint data-block. */
con->type = type;
- con->flag |= CONSTRAINT_EXPAND | CONSTRAINT_OVERRIDE_LIBRARY_LOCAL;
+ con->flag |= CONSTRAINT_OVERRIDE_LIBRARY_LOCAL;
con->enforce = 1.0f;
+ /* Only open the main panel when constraints are created, not the subpanels. */
+ con->ui_expand_flag = (1 << 0);
+ if (type == CONSTRAINT_TYPE_ACTION || CONSTRAINT_TYPE_SPLINEIK) {
+ /* Expand the two subpanels in the cases where the main panel barely has any properties. */
+ con->ui_expand_flag |= (1 << 1) | (1 << 2);
+ }
+
/* Determine a basic name, and info */
if (cti) {
/* initialize constraint data */
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index 8d57ef25c4d..0dd1ecd0008 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -24,6 +24,7 @@
#include "BLI_utildefines.h"
#include "DNA_brush_types.h"
+#include "DNA_constraint_types.h"
#include "DNA_genfile.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_modifier_types.h"
@@ -287,5 +288,19 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
scene->eevee.motion_blur_max = 32;
}
}
+
+ /* Transition to saving expansion for all of a constraint's subpanels. */
+ if (!DNA_struct_elem_find(fd->filesdna, "bConstraint", "short", "ui_expand_flag")) {
+ for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
+ LISTBASE_FOREACH (bConstraint *, con, &object->constraints) {
+ if (con->flag & CONSTRAINT_EXPAND_DEPRECATED) {
+ con->ui_expand_flag = 1;
+ }
+ else {
+ con->ui_expand_flag = 0;
+ }
+ }
+ }
+ }
}
}
diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c
index 15bf26a5fa8..e591f54c750 100644
--- a/source/blender/draw/engines/overlay/overlay_extra.c
+++ b/source/blender/draw/engines/overlay/overlay_extra.c
@@ -1318,7 +1318,7 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb,
else {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon);
- if ((cti && cti->get_constraint_targets) && (curcon->flag & CONSTRAINT_EXPAND)) {
+ if ((cti && cti->get_constraint_targets) && (curcon->ui_expand_flag && (1 << 0))) {
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 59dcc9a8ace..d89e590122c 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -2004,6 +2004,8 @@ void uiTemplatePathBuilder(uiLayout *layout,
struct PointerRNA *root_ptr,
const char *text);
void uiTemplateModifiers(uiLayout *layout, struct bContext *C);
+void uiTemplateConstraints(uiLayout *layout, struct bContext *C, bool use_bone_constraints);
+
uiLayout *uiTemplateGpencilModifier(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr);
void uiTemplateGpencilColorPreview(uiLayout *layout,
struct bContext *C,
@@ -2018,7 +2020,7 @@ uiLayout *uiTemplateShaderFx(uiLayout *layout, struct bContext *C, struct Pointe
void uiTemplateOperatorRedoProperties(uiLayout *layout, const struct bContext *C);
-uiLayout *uiTemplateConstraint(uiLayout *layout, struct PointerRNA *ptr);
+void uiTemplateConstraintHeader(uiLayout *layout, struct PointerRNA *ptr);
void uiTemplatePreview(uiLayout *layout,
struct bContext *C,
struct ID *id,
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
index 4fff80d5def..924b886f167 100644
--- a/source/blender/editors/interface/interface_panel.c
+++ b/source/blender/editors/interface/interface_panel.c
@@ -387,9 +387,19 @@ bool UI_panel_list_matches_data(ARegion *region,
ListBase *data,
uiListPanelIDFromDataFunc panel_idname_func)
{
- int data_len = BLI_listbase_count(data);
+ /* Check for NULL data. */
+ int data_len = 0;
+ Link *data_link = NULL;
+ if (data == NULL) {
+ data_len = 0;
+ data_link = NULL;
+ }
+ else {
+ data_len = BLI_listbase_count(data);
+ data_link = data->first;
+ }
+
int i = 0;
- Link *data_link = data->first;
LISTBASE_FOREACH (Panel *, panel, &region->panels) {
if (panel->type != NULL && panel->type->flag & PNL_INSTANCED) {
/* The panels were reordered by drag and drop. */
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 1ce1e2950d5..189441a1f10 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -1823,6 +1823,20 @@ void uiTemplatePathBuilder(uiLayout *layout,
* Template for building the panel layout for the active object's modifiers.
* \{ */
+/**
+ * Get the active object or the property region's pinned object.
+ */
+static Object *get_context_object(const bContext *C)
+{
+ SpaceProperties *sbuts = CTX_wm_space_properties(C);
+ if (sbuts != NULL && (sbuts->pinid != NULL) && GS(sbuts->pinid->name) == ID_OB) {
+ return (Object *)sbuts->pinid;
+ }
+ else {
+ return CTX_data_active_object(C);
+ }
+}
+
static void modifier_panel_id(void *md_link, char *r_name)
{
ModifierData *md = (ModifierData *)md_link;
@@ -1834,14 +1848,7 @@ void uiTemplateModifiers(uiLayout *UNUSED(layout), bContext *C)
ScrArea *sa = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
- Object *ob;
- SpaceProperties *sbuts = CTX_wm_space_properties(C);
- if (sbuts != NULL && (sbuts->pinid != NULL) && GS(sbuts->pinid->name) == ID_OB) {
- ob = (Object *)sbuts->pinid;
- }
- else {
- ob = CTX_data_active_object(C);
- }
+ Object *ob = get_context_object(C);
ListBase *modifiers = &ob->modifiers;
bool panels_match = UI_panel_list_matches_data(region, modifiers, modifier_panel_id);
@@ -1874,6 +1881,162 @@ void uiTemplateModifiers(uiLayout *UNUSED(layout), bContext *C)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Constraints Template
+ *
+ * Template for building the panel layout for the active object or bone's constraints.
+ * \{ */
+
+/** For building the panel UI for constraints. */
+#define CONSTRAINT_TYPE_PANEL_PREFIX "OBJECT_PT_"
+#define CONSTRAINT_BONE_TYPE_PANEL_PREFIX "BONE_PT_"
+
+/**
+ * Check if the panel's ID starts with 'BONE', meaning it is a bone constraint.
+ */
+static bool constraint_panel_is_bone(Panel *panel)
+{
+ return (panel->panelname[0] == 'B') && (panel->panelname[1] == 'O') &&
+ (panel->panelname[2] == 'N') && (panel->panelname[3] == 'E');
+}
+
+/**
+ * Get the constraints for the active pose bone or the active / pinned object.
+ */
+static ListBase *get_constraints(const bContext *C, bool use_bone_constraints)
+{
+ ListBase *constraints = {NULL};
+ if (use_bone_constraints) {
+ bPoseChannel *pose_bone = CTX_data_pointer_get(C, "pose_bone").data;
+ if (pose_bone != NULL) {
+ constraints = &pose_bone->constraints;
+ }
+ }
+ else {
+ Object *ob = get_context_object(C);
+ if (ob != NULL) {
+ constraints = &ob->constraints;
+ }
+ }
+ return constraints;
+}
+
+/**
+ * Move a constraint to the index it's moved to after a drag and drop.
+ */
+static void constraint_reorder(bContext *C, Panel *panel, int new_index)
+{
+ bool constraint_from_bone = constraint_panel_is_bone(panel);
+ ListBase *lb = get_constraints(C, constraint_from_bone);
+
+ bConstraint *con = BLI_findlink(lb, panel->runtime.list_index);
+ PointerRNA props_ptr;
+ wmOperatorType *ot = WM_operatortype_find("CONSTRAINT_OT_move_to_index", false);
+ WM_operator_properties_create_ptr(&props_ptr, ot);
+ RNA_string_set(&props_ptr, "constraint", con->name);
+ RNA_int_set(&props_ptr, "index", new_index);
+ /* Set owner to #EDIT_CONSTRAINT_OWNER_OBJECT or #EDIT_CONSTRAINT_OWNER_BONE. */
+ RNA_enum_set(&props_ptr, "owner", constraint_from_bone ? 1 : 0);
+ WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr);
+ WM_operator_properties_free(&props_ptr);
+}
+
+/**
+ * Get the expand flag from the active constraint to use for the panel.
+ */
+static short get_constraint_expand_flag(const bContext *C, Panel *panel)
+{
+ bool constraint_from_bone = constraint_panel_is_bone(panel);
+ ListBase *lb = get_constraints(C, constraint_from_bone);
+
+ bConstraint *con = BLI_findlink(lb, panel->runtime.list_index);
+ return con->ui_expand_flag;
+}
+
+/**
+ * Save the expand flag for the panel and subpanels to the constraint.
+ */
+static void set_constraint_expand_flag(const bContext *C, Panel *panel, short expand_flag)
+{
+ bool constraint_from_bone = constraint_panel_is_bone(panel);
+ ListBase *lb = get_constraints(C, constraint_from_bone);
+
+ bConstraint *con = BLI_findlink(lb, panel->runtime.list_index);
+ con->ui_expand_flag = expand_flag;
+}
+
+/**
+ * Function with void * argument for #uiListPanelIDFromDataFunc.
+ *
+ * \note: Constraint panel types are assumed to be named with the struct name field
+ * concatenated to the defined prefix.
+ */
+static void object_constraint_panel_id(void *md_link, char *r_name)
+{
+ bConstraint *con = (bConstraint *)md_link;
+ const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(con->type);
+
+ strcpy(r_name, CONSTRAINT_TYPE_PANEL_PREFIX);
+ strcat(r_name, cti->structName);
+}
+
+static void bone_constraint_panel_id(void *md_link, char *r_name)
+{
+ bConstraint *con = (bConstraint *)md_link;
+ const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(con->type);
+
+ strcpy(r_name, CONSTRAINT_BONE_TYPE_PANEL_PREFIX);
+ strcat(r_name, cti->structName);
+}
+
+/**
+ * Check if the constraint panels don't match the data and rebuild the panels if so.
+ */
+void uiTemplateConstraints(uiLayout *UNUSED(layout), bContext *C, bool use_bone_constraints)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ ARegion *region = CTX_wm_region(C);
+
+ ListBase *constraints = get_constraints(C, use_bone_constraints);
+
+ /* Switch between the bone panel ID function and the object panel ID function. */
+ uiListPanelIDFromDataFunc panel_id_func = use_bone_constraints ? bone_constraint_panel_id :
+ object_constraint_panel_id;
+
+ bool panels_match = UI_panel_list_matches_data(region, constraints, panel_id_func);
+
+ if (!panels_match) {
+ UI_panels_free_instanced(C, region);
+ bConstraint *con = (constraints == NULL) ? NULL : constraints->first;
+ for (int i = 0; con; i++, con = con->next) {
+ char panel_idname[MAX_NAME];
+ panel_id_func(con, panel_idname);
+
+ Panel *new_panel = UI_panel_add_instanced(sa, region, &region->panels, panel_idname, i);
+ if (new_panel) {
+ /* Set the list panel functionality function pointers since we don't do it with python. */
+ new_panel->type->set_list_data_expand_flag = set_constraint_expand_flag;
+ new_panel->type->get_list_data_expand_flag = get_constraint_expand_flag;
+ new_panel->type->reorder = constraint_reorder;
+
+ UI_panel_set_expand_from_list_data(C, new_panel);
+ }
+ }
+ }
+ else {
+ /* The expansion might have been changed elsewhere, so we still need to set it. */
+ LISTBASE_FOREACH (Panel *, panel, &region->panels) {
+ if ((panel->type != NULL) && (panel->type->flag & PNL_INSTANCED))
+ UI_panel_set_expand_from_list_data(C, panel);
+ }
+ }
+}
+
+#undef CONSTRAINT_TYPE_PANEL_PREFIX
+#undef CONSTRAINT_BONE_TYPE_PANEL_PREFIX
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Grease Pencil Modifier Template
* \{ */
@@ -2441,7 +2604,7 @@ void uiTemplateOperatorRedoProperties(uiLayout *layout, const bContext *C)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Constraint Template
+/** \name Constraint Header Template
* \{ */
static void constraint_active_func(bContext *UNUSED(C), void *ob_v, void *con_v)
@@ -2449,30 +2612,15 @@ static void constraint_active_func(bContext *UNUSED(C), void *ob_v, void *con_v)
ED_object_constraint_active_set(ob_v, con_v);
}
-/* draw panel showing settings for a constraint */
-static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con)
+static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *con)
{
bPoseChannel *pchan = BKE_pose_channel_active(ob);
- const bConstraintTypeInfo *cti;
uiBlock *block;
- uiLayout *result = NULL, *col, *box, *row;
+ uiLayout *sub;
PointerRNA ptr;
- char typestr[32];
short proxy_protected, xco = 0, yco = 0;
// int rb_col; // UNUSED
- /* get constraint typeinfo */
- cti = BKE_constraint_typeinfo_get(con);
- if (cti == NULL) {
- /* exception for 'Null' constraint - it doesn't have constraint typeinfo! */
- BLI_strncpy(typestr,
- (con->type == CONSTRAINT_TYPE_NULL) ? IFACE_("Null") : IFACE_("Unknown"),
- sizeof(typestr));
- }
- else {
- BLI_strncpy(typestr, IFACE_(cti->name), sizeof(typestr));
- }
-
/* determine whether constraint is proxy protected or not */
if (BKE_constraints_proxylocked_owner(ob, pchan)) {
proxy_protected = (con->flag & CONSTRAINT_PROXY_LOCAL) == 0;
@@ -2487,36 +2635,23 @@ static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con)
RNA_pointer_create(&ob->id, &RNA_Constraint, con, &ptr);
- col = uiLayoutColumn(layout, true);
- uiLayoutSetContextPointer(col, "constraint", &ptr);
-
- box = uiLayoutBox(col);
- row = uiLayoutRow(box, false);
- block = uiLayoutGetBlock(box);
-
- /* Draw constraint header */
+ uiLayoutSetContextPointer(layout, "constraint", &ptr);
- /* open/close */
- UI_block_emboss_set(block, UI_EMBOSS_NONE);
- uiItemR(row, &ptr, "show_expanded", 0, "", ICON_NONE);
+ /* Constraint type icon. */
+ sub = uiLayoutRow(layout, false);
+ uiLayoutSetEmboss(sub, false);
+ uiLayoutSetRedAlert(sub, (con->flag & CONSTRAINT_DISABLE));
+ uiItemL(sub, "", RNA_struct_ui_icon(ptr.type));
- /* constraint-type icon */
- uiItemL(row, "", RNA_struct_ui_icon(ptr.type));
UI_block_emboss_set(block, UI_EMBOSS);
- if (con->flag & CONSTRAINT_DISABLE) {
- uiLayoutSetRedAlert(row, true);
- }
-
if (proxy_protected == 0) {
- uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
+ uiItemR(layout, &ptr, "name", 0, "", ICON_NONE);
}
else {
- uiItemL(row, con->name, ICON_NONE);
+ uiItemL(layout, con->name, ICON_NONE);
}
- uiLayoutSetRedAlert(row, false);
-
/* proxy-protected constraints cannot be edited, so hide up/down + close buttons */
if (proxy_protected) {
UI_block_emboss_set(block, UI_EMBOSS_NONE);
@@ -2555,54 +2690,20 @@ static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con)
UI_block_emboss_set(block, UI_EMBOSS);
}
else {
- short prev_proxylock, show_upbut, show_downbut;
-
- /* Up/Down buttons:
- * Proxy-constraints are not allowed to occur after local (non-proxy) constraints
- * as that poses problems when restoring them, so disable the "up" button where
- * it may cause this situation.
- *
- * Up/Down buttons should only be shown (or not grayed - todo) if they serve some purpose.
- */
- if (BKE_constraints_proxylocked_owner(ob, pchan)) {
- if (con->prev) {
- prev_proxylock = (con->prev->flag & CONSTRAINT_PROXY_LOCAL) ? 0 : 1;
- }
- else {
- prev_proxylock = 0;
- }
- }
- else {
- prev_proxylock = 0;
- }
-
- show_upbut = ((prev_proxylock == 0) && (con->prev));
- show_downbut = (con->next) ? 1 : 0;
-
/* enabled */
UI_block_emboss_set(block, UI_EMBOSS_NONE);
- uiItemR(row, &ptr, "mute", 0, "", 0);
+ uiItemR(layout, &ptr, "mute", 0, "", 0);
UI_block_emboss_set(block, UI_EMBOSS);
- uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT);
-
- /* up/down */
- if (show_upbut || show_downbut) {
- UI_block_align_begin(block);
- if (show_upbut) {
- uiItemO(row, "", ICON_TRIA_UP, "CONSTRAINT_OT_move_up");
- }
-
- if (show_downbut) {
- uiItemO(row, "", ICON_TRIA_DOWN, "CONSTRAINT_OT_move_down");
- }
- UI_block_align_end(block);
- }
+ uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
/* Close 'button' - emboss calls here disable drawing of 'button' behind X */
UI_block_emboss_set(block, UI_EMBOSS_NONE);
- uiItemO(row, "", ICON_X, "CONSTRAINT_OT_delete");
+ uiItemO(layout, "", ICON_X, "CONSTRAINT_OT_delete");
UI_block_emboss_set(block, UI_EMBOSS);
+
+ /* Some extra padding at the end, so the 'x' icon isn't too close to drag button. */
+ uiItemS(layout);
}
/* Set but-locks for protected settings (magic numbers are used here!) */
@@ -2610,23 +2711,11 @@ static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con)
UI_block_lock_set(block, true, TIP_("Cannot edit Proxy-Protected Constraint"));
}
- /* Draw constraint data */
- if ((con->flag & CONSTRAINT_EXPAND) == 0) {
- (yco) -= 10.5f * UI_UNIT_Y;
- }
- else {
- box = uiLayoutBox(col);
- block = uiLayoutAbsoluteBlock(box);
- result = box;
- }
-
/* clear any locks set up for proxies/lib-linking */
UI_block_lock_clear(block);
-
- return result;
}
-uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr)
+void uiTemplateConstraintHeader(uiLayout *layout, PointerRNA *ptr)
{
Object *ob;
bConstraint *con;
@@ -2634,7 +2723,7 @@ uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr)
/* verify we have valid data */
if (!RNA_struct_is_a(ptr->type, &RNA_Constraint)) {
RNA_warning("Expected constraint on object");
- return NULL;
+ return;
}
ob = (Object *)ptr->owner_id;
@@ -2642,7 +2731,7 @@ uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr)
if (!ob || !(GS(ob->id.name) == ID_OB)) {
RNA_warning("Expected constraint on object");
- return NULL;
+ return;
}
UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE);
@@ -2651,11 +2740,11 @@ uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr)
if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
bKinematicConstraint *data = con->data;
if (data->flag & CONSTRAINT_IK_TEMP) {
- return NULL;
+ return;
}
}
- return draw_constraint(layout, ob, con);
+ draw_constraint_header(layout, ob, con);
}
/** \} */
diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c
index a2d33ffe413..5746480e3f8 100644
--- a/source/blender/editors/object/object_constraint.c
+++ b/source/blender/editors/object/object_constraint.c
@@ -1557,6 +1557,77 @@ void CONSTRAINT_OT_move_up(wmOperatorType *ot)
/** \} */
/* ------------------------------------------------------------------- */
+/** \name Move Constraint To Index Operator
+ * \{ */
+
+static int constraint_move_to_index_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = ED_object_active_context(C);
+ bConstraint *con = edit_constraint_property_get(op, ob, 0);
+
+ int new_index = RNA_int_get(op->ptr, "index");
+ if (new_index < 0) {
+ new_index = 0;
+ }
+
+ if (con) {
+ ListBase *conlist = ED_object_constraint_list_from_constraint(ob, con, NULL);
+ int current_index = BLI_findindex(conlist, con);
+ BLI_assert(current_index >= 0);
+
+ BLI_listbase_link_move(conlist, con, new_index - current_index);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
+
+ return OPERATOR_FINISHED;
+ }
+
+ return OPERATOR_CANCELLED;
+}
+
+static int constraint_move_to_index_invoke(bContext *C,
+ wmOperator *op,
+ const wmEvent *UNUSED(event))
+{
+ if (edit_constraint_invoke_properties(C, op)) {
+ return constraint_move_to_index_exec(C, op);
+ }
+ else {
+ return OPERATOR_CANCELLED;
+ }
+}
+
+void CONSTRAINT_OT_move_to_index(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Move Constraint To Index";
+ ot->idname = "CONSTRAINT_OT_move_to_index";
+ ot->description =
+ "Change the constraint's position in the list so it evaluates after the set number of "
+ "others";
+
+ /* callbacks */
+ ot->exec = constraint_move_to_index_exec;
+ ot->invoke = constraint_move_to_index_invoke;
+ ot->poll = edit_constraint_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ edit_constraint_properties(ot);
+ RNA_def_int(ot->srna,
+ "index",
+ 0,
+ 0,
+ INT_MAX,
+ "Index",
+ "The index to move the constraint to",
+ 0,
+ INT_MAX);
+}
+
+/** \} */
+
+/* ------------------------------------------------------------------- */
/** \name Clear Pose Constraints Operator
* \{ */
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index c5a6e38fbcb..1cd91f7c1f3 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -217,6 +217,7 @@ void POSE_OT_ik_clear(struct wmOperatorType *ot);
void CONSTRAINT_OT_delete(struct wmOperatorType *ot);
void CONSTRAINT_OT_move_up(struct wmOperatorType *ot);
+void CONSTRAINT_OT_move_to_index(struct wmOperatorType *ot);
void CONSTRAINT_OT_move_down(struct wmOperatorType *ot);
void CONSTRAINT_OT_stretchto_reset(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index 05f1ced8615..84bc10f8064 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -179,6 +179,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(CONSTRAINT_OT_delete);
WM_operatortype_append(CONSTRAINT_OT_move_up);
WM_operatortype_append(CONSTRAINT_OT_move_down);
+ WM_operatortype_append(CONSTRAINT_OT_move_to_index);
WM_operatortype_append(CONSTRAINT_OT_stretchto_reset);
WM_operatortype_append(CONSTRAINT_OT_limitdistance_reset);
WM_operatortype_append(CONSTRAINT_OT_childof_set_inverse);
diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h
index 65087a6d459..85d9a04a902 100644
--- a/source/blender/makesdna/DNA_constraint_types.h
+++ b/source/blender/makesdna/DNA_constraint_types.h
@@ -61,7 +61,8 @@ typedef struct bConstraint {
/** Constraint name, MAX_NAME. */
char name[64];
- char _pad[2];
+ /* Flag for panel and subpanel closed / open state in the UI. */
+ short ui_expand_flag;
/** Amount of influence exherted by constraint (0.0-1.0). */
float enforce;
@@ -689,8 +690,8 @@ typedef enum eBConstraint_Types {
/* flag 0x20 (1 << 5) was used to indicate that a constraint was evaluated
* using a 'local' hack for posebones only. */
typedef enum eBConstraint_Flags {
- /* expand for UI */
- CONSTRAINT_EXPAND = (1 << 0),
+ /* Expansion for old box constraint layouts. Just for versioning. */
+ CONSTRAINT_EXPAND_DEPRECATED = (1 << 0),
/* pre-check for illegal object name or bone name */
CONSTRAINT_DISABLE = (1 << 2),
/* to indicate which Ipo should be shown, maybe for 3d access later too */
diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c
index 5405cb4e24a..af300f6e088 100644
--- a/source/blender/makesrna/intern/rna_constraint.c
+++ b/source/blender/makesrna/intern/rna_constraint.c
@@ -2483,7 +2483,7 @@ static void rna_def_constraint_location_limit(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_transform_limit", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag2", LIMIT_TRANSFORM);
RNA_def_property_ui_text(
- prop, "For Transform", "Transforms are affected by this constraint as well");
+ prop, "Affect Transform", "Transforms are affected by this constraint as well");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
RNA_define_lib_overridable(false);
@@ -2556,7 +2556,7 @@ static void rna_def_constraint_rotation_limit(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_transform_limit", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag2", LIMIT_TRANSFORM);
RNA_def_property_ui_text(
- prop, "For Transform", "Transforms are affected by this constraint as well");
+ prop, "Affect Transform", "Transforms are affected by this constraint as well");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
RNA_define_lib_overridable(false);
@@ -2644,7 +2644,7 @@ static void rna_def_constraint_size_limit(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_transform_limit", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag2", LIMIT_TRANSFORM);
RNA_def_property_ui_text(
- prop, "For Transform", "Transforms are affected by this constraint as well");
+ prop, "Affect Transform", "Transforms are affected by this constraint as well");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
RNA_define_lib_overridable(false);
@@ -2684,7 +2684,7 @@ static void rna_def_constraint_distance_limit(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_transform_limit", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", LIMITDIST_TRANSFORM);
RNA_def_property_ui_text(
- prop, "For Transform", "Transforms are affected by this constraint as well");
+ prop, "Affect Transform", "Transforms are affected by this constraint as well");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
RNA_define_lib_overridable(false);
@@ -3380,7 +3380,7 @@ void RNA_def_constraint(BlenderRNA *brna)
prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_EXPAND);
+ RNA_def_property_boolean_sdna(prop, NULL, "ui_expand_flag", 0);
RNA_def_property_ui_text(prop, "Expanded", "Constraint's panel is expanded in UI");
RNA_def_property_ui_icon(prop, ICON_DISCLOSURE_TRI_RIGHT, 1);
diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c
index fd65b713d15..8bfee4cc26c 100644
--- a/source/blender/makesrna/intern/rna_ui_api.c
+++ b/source/blender/makesrna/intern/rna_ui_api.c
@@ -1213,6 +1213,15 @@ void RNA_api_ui_layout(StructRNA *srna)
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
RNA_def_function_ui_description(func, "Generates the UI layout for the modifier stack");
+ func = RNA_def_function(srna, "template_constraints", "uiTemplateConstraints");
+ RNA_def_function_flag(func, FUNC_USE_CONTEXT);
+ RNA_def_function_ui_description(func, "Generates the panels for the constraint stack");
+ RNA_def_boolean(func,
+ "use_bone_constraints",
+ true,
+ "",
+ "Add panels for bone constraints instead of object constraints");
+
func = RNA_def_function(srna, "template_greasepencil_modifier", "uiTemplateGpencilModifier");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
RNA_def_function_ui_description(func, "Generates the UI layout for grease pencil modifiers");
@@ -1251,12 +1260,10 @@ void RNA_api_ui_layout(StructRNA *srna)
"",
"Optionally limit the items which can be selected");
- func = RNA_def_function(srna, "template_constraint", "uiTemplateConstraint");
- RNA_def_function_ui_description(func, "Generates the UI layout for constraints");
+ func = RNA_def_function(srna, "template_constraint_header", "uiTemplateConstraintHeader");
+ RNA_def_function_ui_description(func, "Generates the header for constraint panels");
parm = RNA_def_pointer(func, "data", "Constraint", "", "Constraint data");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
- parm = RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in");
- RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "template_preview", "uiTemplatePreview");
RNA_def_function_ui_description(