Files
blender/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
Campbell Barton 19d493ee10 Moving classes to separate listing broke panel order
Although this wasn't so obvious since it
only showed up for factory settings and in the preferences window.

Panel display order depends on registration order,
Sorry for the noise. On the bright side we no longer need to move
classes around to re-arrange panels.
2017-03-20 02:37:55 +11:00

1179 lines
42 KiB
Python

# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
from bpy.types import Menu, UIList
from bpy.app.translations import pgettext_iface as iface_
def gpencil_stroke_placement_settings(context, layout):
if context.space_data.type == 'VIEW_3D':
propname = "gpencil_stroke_placement_view3d"
elif context.space_data.type == 'SEQUENCE_EDITOR':
propname = "gpencil_stroke_placement_sequencer_preview"
elif context.space_data.type == 'IMAGE_EDITOR':
propname = "gpencil_stroke_placement_image_editor"
else:
propname = "gpencil_stroke_placement_view2d"
ts = context.tool_settings
col = layout.column(align=True)
col.label(text="Stroke Placement:")
row = col.row(align=True)
row.prop_enum(ts, propname, 'VIEW')
row.prop_enum(ts, propname, 'CURSOR')
if context.space_data.type == 'VIEW_3D':
row = col.row(align=True)
row.prop_enum(ts, propname, 'SURFACE')
row.prop_enum(ts, propname, 'STROKE')
row = col.row(align=False)
row.active = getattr(ts, propname) in {'SURFACE', 'STROKE'}
row.prop(ts, "use_gpencil_stroke_endpoints")
if context.scene.tool_settings.gpencil_stroke_placement_view3d == 'CURSOR':
row = col.row(align=True)
row.label("Lock axis:")
row = col.row(align=True)
row.prop(ts.gpencil_sculpt, "lockaxis", expand=True)
def gpencil_active_brush_settings_simple(context, layout):
brush = context.active_gpencil_brush
if brush is None:
layout.label("No Active Brush")
return
col = layout.column()
col.label("Active Brush: ")
row = col.row(align=True)
row.operator_context = 'EXEC_REGION_WIN'
row.operator_menu_enum("gpencil.brush_change", "brush", text="", icon='BRUSH_DATA')
row.prop(brush, "name", text="")
col.prop(brush, "line_width", slider=True)
row = col.row(align=True)
row.prop(brush, "use_random_pressure", text='', icon='RNDCURVE')
row.prop(brush, "pen_sensitivity_factor", slider=True)
row.prop(brush, "use_pressure", text='', icon='STYLUS_PRESSURE')
row = col.row(align=True)
row.prop(brush, "use_random_strength", text='', icon='RNDCURVE')
row.prop(brush, "strength", slider=True)
row.prop(brush, "use_strength_pressure", text='', icon='STYLUS_PRESSURE')
row = col.row(align=True)
row.prop(brush, "jitter", slider=True)
row.prop(brush, "use_jitter_pressure", text='', icon='STYLUS_PRESSURE')
row = col.row()
row.prop(brush, "angle", slider=True)
row.prop(brush, "angle_factor", text="Factor", slider=True)
class GreasePencilDrawingToolsPanel:
# subclass must set
# bl_space_type = 'IMAGE_EDITOR'
bl_label = "Grease Pencil"
bl_category = "Grease Pencil"
bl_region_type = 'TOOLS'
@staticmethod
def draw(self, context):
layout = self.layout
is_3d_view = context.space_data.type == 'VIEW_3D'
is_clip_editor = context.space_data.type == 'CLIP_EDITOR'
col = layout.column(align=True)
col.label(text="Draw:")
row = col.row(align=True)
row.operator("gpencil.draw", icon='GREASEPENCIL', text="Draw").mode = 'DRAW'
row.operator("gpencil.draw", icon='FORCE_CURVE', text="Erase").mode = 'ERASER' # XXX: Needs a dedicated icon
row = col.row(align=True)
row.operator("gpencil.draw", icon='LINE_DATA', text="Line").mode = 'DRAW_STRAIGHT'
row.operator("gpencil.draw", icon='MESH_DATA', text="Poly").mode = 'DRAW_POLY'
col.separator()
sub = col.column(align=True)
sub.operator("gpencil.blank_frame_add", icon='NEW')
sub.operator("gpencil.active_frames_delete_all", icon='X', text="Delete Frame(s)")
sub = col.column(align=True)
sub.prop(context.tool_settings, "use_gpencil_additive_drawing", text="Additive Drawing")
sub.prop(context.tool_settings, "use_gpencil_continuous_drawing", text="Continuous Drawing")
sub.prop(context.tool_settings, "use_gpencil_draw_onback", text="Draw on Back")
col.separator()
col.separator()
if context.space_data.type in {'VIEW_3D', 'CLIP_EDITOR'}:
col.separator()
col.label("Data Source:")
row = col.row(align=True)
if is_3d_view:
row.prop(context.tool_settings, "grease_pencil_source", expand=True)
elif is_clip_editor:
row.prop(context.space_data, "grease_pencil_source", expand=True)
col.separator()
col.separator()
gpencil_stroke_placement_settings(context, col)
gpd = context.gpencil_data
if gpd and not is_3d_view:
layout.separator()
layout.separator()
col = layout.column(align=True)
col.prop(gpd, "use_stroke_edit_mode", text="Enable Editing", icon='EDIT', toggle=True)
if is_3d_view:
col.separator()
col.separator()
col.label(text="Tools:")
col.operator_menu_enum("gpencil.convert", text="Convert to Geometry...", property="type")
col.operator("view3d.ruler")
class GreasePencilStrokeEditPanel:
# subclass must set
# bl_space_type = 'IMAGE_EDITOR'
bl_label = "Edit Strokes"
bl_category = "Grease Pencil"
bl_region_type = 'TOOLS'
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
if context.gpencil_data is None:
return False
gpd = context.gpencil_data
return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode)
@staticmethod
def draw(self, context):
layout = self.layout
is_3d_view = context.space_data.type == 'VIEW_3D'
if not is_3d_view:
layout.label(text="Select:")
col = layout.column(align=True)
col.operator("gpencil.select_all", text="Select All")
col.operator("gpencil.select_border")
col.operator("gpencil.select_circle")
layout.separator()
col = layout.column(align=True)
col.operator("gpencil.select_linked")
col.operator("gpencil.select_more")
col.operator("gpencil.select_less")
col.operator("gpencil.palettecolor_select")
layout.label(text="Edit:")
row = layout.row(align=True)
row.operator("gpencil.copy", text="Copy")
row.operator("gpencil.paste", text="Paste").type = 'COPY'
row.operator("gpencil.paste", text="Paste & Merge").type = 'MERGE'
col = layout.column(align=True)
col.operator("gpencil.delete")
col.operator("gpencil.duplicate_move", text="Duplicate")
if is_3d_view:
col.operator("gpencil.stroke_cyclical_set", text="Toggle Cyclic").type = 'TOGGLE'
layout.separator()
if not is_3d_view:
col = layout.column(align=True)
col.operator("transform.translate") # icon='MAN_TRANS'
col.operator("transform.rotate") # icon='MAN_ROT'
col.operator("transform.resize", text="Scale") # icon='MAN_SCALE'
layout.separator()
col = layout.column(align=True)
col.operator("transform.bend", text="Bend")
col.operator("transform.mirror", text="Mirror")
col.operator("transform.shear", text="Shear")
col.operator("transform.tosphere", text="To Sphere")
layout.separator()
col = layout.column(align=True)
col.operator_menu_enum("gpencil.stroke_arrange", text="Arrange Strokes...", property="direction")
col.operator("gpencil.stroke_change_color", text="Move to Color")
if is_3d_view:
layout.separator()
layout.separator()
col = layout.column(align=True)
col.operator("gpencil.stroke_subdivide", text="Subdivide")
col.operator("gpencil.stroke_join", text="Join").type = 'JOIN'
col.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY'
col.operator("gpencil.stroke_flip", text="Flip Direction")
gpd = context.gpencil_data
if gpd:
col.prop(gpd, "show_stroke_direction", text="Show Directions")
if is_3d_view:
layout.separator()
layout.operator_menu_enum("gpencil.reproject", text="Reproject Strokes...", property="type")
class GreasePencilInterpolatePanel:
bl_space_type = 'VIEW_3D'
bl_label = "Interpolate"
bl_category = "Grease Pencil"
bl_region_type = 'TOOLS'
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
if context.gpencil_data is None:
return False
elif context.space_data.type != 'VIEW_3D':
return False
gpd = context.gpencil_data
return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode)
@staticmethod
def draw(self, context):
layout = self.layout
settings = context.tool_settings.gpencil_interpolate
col = layout.column(align=True)
col.operator("gpencil.interpolate", text="Interpolate")
col.operator("gpencil.interpolate_sequence", text="Sequence")
col.operator("gpencil.interpolate_reverse", text="Remove Breakdowns")
col = layout.column(align=True)
col.label(text="Options:")
col.prop(settings, "interpolate_all_layers")
col.prop(settings, "interpolate_selected_only")
col = layout.column(align=True)
col.label(text="Sequence Options:")
col.prop(settings, "type")
if settings.type == 'CUSTOM':
box = layout.box()
# TODO: Options for loading/saving curve presets?
box.template_curve_mapping(settings, "interpolation_curve", brush=True)
elif settings.type != 'LINEAR':
col.prop(settings, "easing")
if settings.type == 'BACK':
layout.prop(settings, "back")
elif setting.type == 'ELASTIC':
sub = layout.column(align=True)
sub.prop(settings, "amplitude")
sub.prop(settings, "period")
class GreasePencilBrushPanel:
# subclass must set
# bl_space_type = 'IMAGE_EDITOR'
bl_label = "Drawing Brushes"
bl_category = "Grease Pencil"
bl_region_type = 'TOOLS'
@staticmethod
def draw(self, context):
layout = self.layout
row = layout.row()
col = row.column()
ts = context.scene.tool_settings
if len(ts.gpencil_brushes) >= 2:
brows = 3
else:
brows = 2
col.template_list("GPENCIL_UL_brush", "", ts, "gpencil_brushes", ts.gpencil_brushes, "active_index", rows=brows)
col = row.column()
sub = col.column(align=True)
sub.operator("gpencil.brush_add", icon='ZOOMIN', text="")
sub.operator("gpencil.brush_remove", icon='ZOOMOUT', text="")
sub.menu("GPENCIL_MT_brush_specials", icon='DOWNARROW_HLT', text="")
brush = context.active_gpencil_brush
if brush:
if len(ts.gpencil_brushes) > 1:
col.separator()
sub = col.column(align=True)
sub.operator("gpencil.brush_move", icon='TRIA_UP', text="").type = 'UP'
sub.operator("gpencil.brush_move", icon='TRIA_DOWN', text="").type = 'DOWN'
# Brush details
if brush is not None:
row = layout.row()
row.prop(brush, "line_width")
row = layout.row(align=True)
row.prop(brush, "use_random_pressure", text='', icon='RNDCURVE')
row.prop(brush, "pen_sensitivity_factor", slider=True)
row.prop(brush, "use_pressure", text='', icon='STYLUS_PRESSURE')
row = layout.row(align=True)
row.prop(brush, "use_random_strength", text='', icon='RNDCURVE')
row.prop(brush, "strength", slider=True)
row.prop(brush, "use_strength_pressure", text='', icon='STYLUS_PRESSURE')
row = layout.row(align=True)
row.prop(brush, "random_press", slider=True)
row = layout.row(align=True)
row.prop(brush, "jitter", slider=True)
row.prop(brush, "use_jitter_pressure", text='', icon='STYLUS_PRESSURE')
row = layout.row()
row.prop(brush, "angle", slider=True)
row.prop(brush, "angle_factor", text="Factor", slider=True)
box = layout.box()
col = box.column(align=True)
col.label(text="Stroke Quality:")
col.prop(brush, "pen_smooth_factor")
col.prop(brush, "pen_smooth_steps")
col.separator()
row = col.row(align=False)
row.prop(brush, "pen_subdivision_steps")
row.prop(brush, "random_subdiv", text='Randomness', slider=True)
class GreasePencilStrokeSculptPanel:
# subclass must set
# bl_space_type = 'IMAGE_EDITOR'
bl_label = "Sculpt Strokes"
bl_category = "Grease Pencil"
bl_region_type = 'TOOLS'
@classmethod
def poll(cls, context):
if context.gpencil_data is None:
return False
gpd = context.gpencil_data
return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode)
@staticmethod
def draw(self, context):
layout = self.layout
settings = context.tool_settings.gpencil_sculpt
tool = settings.tool
brush = settings.brush
layout.column().prop(settings, "tool")
col = layout.column()
col.prop(brush, "size", slider=True)
row = col.row(align=True)
row.prop(brush, "strength", slider=True)
row.prop(brush, "use_pressure_strength", text="")
col.prop(brush, "use_falloff")
if tool in {'SMOOTH', 'RANDOMIZE'}:
row = layout.row(align=True)
row.prop(settings, "affect_position", text="Position", icon='MESH_DATA', toggle=True)
row.prop(settings, "affect_strength", text="Strength", icon='COLOR', toggle=True)
row.prop(settings, "affect_thickness", text="Thickness", icon='LINE_DATA', toggle=True)
layout.separator()
if tool == 'THICKNESS':
layout.row().prop(brush, "direction", expand=True)
elif tool == 'PINCH':
row = layout.row(align=True)
row.prop_enum(brush, "direction", 'ADD', text="Pinch")
row.prop_enum(brush, "direction", 'SUBTRACT', text="Inflate")
elif settings.tool == 'TWIST':
row = layout.row(align=True)
row.prop_enum(brush, "direction", 'SUBTRACT', text="CW")
row.prop_enum(brush, "direction", 'ADD', text="CCW")
row = layout.row(align=True)
row.prop(settings, "use_select_mask")
row = layout.row(align=True)
row.prop(settings, "selection_alpha", slider=True)
if tool == 'SMOOTH':
layout.prop(brush, "affect_pressure")
class GreasePencilBrushCurvesPanel:
# subclass must set
# bl_space_type = 'IMAGE_EDITOR'
bl_label = "Brush Curves"
bl_category = "Grease Pencil"
bl_region_type = 'TOOLS'
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
if context.active_gpencil_brush is None:
return False
brush = context.active_gpencil_brush
return bool(brush)
@staticmethod
def draw(self, context):
layout = self.layout
brush = context.active_gpencil_brush
# Brush
layout.label("Sensitivity")
box = layout.box()
box.template_curve_mapping(brush, "curve_sensitivity", brush=True)
layout.label("Strength")
box = layout.box()
box.template_curve_mapping(brush, "curve_strength", brush=True)
layout.label("Jitter")
box = layout.box()
box.template_curve_mapping(brush, "curve_jitter", brush=True)
###############################
class GPENCIL_PIE_tool_palette(Menu):
"""A pie menu for quick access to Grease Pencil tools"""
bl_label = "Grease Pencil Tools"
def draw(self, context):
layout = self.layout
pie = layout.menu_pie()
gpd = context.gpencil_data
# W - Drawing Types
col = pie.column()
col.operator("gpencil.draw", text="Draw", icon='GREASEPENCIL').mode = 'DRAW'
col.operator("gpencil.draw", text="Straight Lines", icon='LINE_DATA').mode = 'DRAW_STRAIGHT'
col.operator("gpencil.draw", text="Poly", icon='MESH_DATA').mode = 'DRAW_POLY'
# E - Eraser
# XXX: needs a dedicated icon...
col = pie.column()
col.operator("gpencil.draw", text="Eraser", icon='FORCE_CURVE').mode = 'ERASER'
# E - "Settings" Palette is included here too, since it needs to be in a stable position...
if gpd and gpd.layers.active:
col.separator()
col.operator("wm.call_menu_pie", text="Settings...", icon='SCRIPTWIN').name = "GPENCIL_PIE_settings_palette"
# Editing tools
if gpd:
if gpd.use_stroke_edit_mode and context.editable_gpencil_strokes:
# S - Exit Edit Mode
pie.operator("gpencil.editmode_toggle", text="Exit Edit Mode", icon='EDIT')
# N - Transforms
col = pie.column()
row = col.row(align=True)
row.operator("transform.translate", icon='MAN_TRANS')
row.operator("transform.rotate", icon='MAN_ROT')
row.operator("transform.resize", text="Scale", icon='MAN_SCALE')
row = col.row(align=True)
row.label("Proportional Edit:")
row.prop(context.tool_settings, "proportional_edit", text="", icon_only=True)
row.prop(context.tool_settings, "proportional_edit_falloff", text="", icon_only=True)
# NW - Select (Non-Modal)
col = pie.column()
col.operator("gpencil.select_all", text="Select All", icon='PARTICLE_POINT')
col.operator("gpencil.select_all", text="Select Inverse", icon='BLANK1')
col.operator("gpencil.select_linked", text="Select Linked", icon='LINKED')
col.operator("gpencil.palettecolor_select", text="Select Color", icon='COLOR')
# NE - Select (Modal)
col = pie.column()
col.operator("gpencil.select_border", text="Border Select", icon='BORDER_RECT')
col.operator("gpencil.select_circle", text="Circle Select", icon='META_EMPTY')
col.operator("gpencil.select_lasso", text="Lasso Select", icon='BORDER_LASSO')
# SW - Edit Tools
col = pie.column()
col.operator("gpencil.duplicate_move", icon='PARTICLE_PATH', text="Duplicate")
col.operator("gpencil.delete", icon='X', text="Delete...")
# SE - More Tools
pie.operator("wm.call_menu_pie", text="More...").name = "GPENCIL_PIE_tools_more"
else:
# Toggle Edit Mode
pie.operator("gpencil.editmode_toggle", text="Enable Stroke Editing", icon='EDIT')
class GPENCIL_PIE_settings_palette(Menu):
"""A pie menu for quick access to Grease Pencil settings"""
bl_label = "Grease Pencil Settings"
@classmethod
def poll(cls, context):
return bool(context.gpencil_data and context.active_gpencil_layer)
def draw(self, context):
layout = self.layout
pie = layout.menu_pie()
gpd = context.gpencil_data
gpl = context.active_gpencil_layer
palcolor = context.active_gpencil_palettecolor
brush = context.active_gpencil_brush
is_editmode = bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes)
# W - Stroke draw settings
col = pie.column(align=True)
if palcolor is not None:
col.enabled = not palcolor.lock
col.label(text="Stroke")
col.prop(palcolor, "color", text="")
col.prop(palcolor, "alpha", text="", slider=True)
# E - Fill draw settings
col = pie.column(align=True)
if palcolor is not None:
col.enabled = not palcolor.lock
col.label(text="Fill")
col.prop(palcolor, "fill_color", text="")
col.prop(palcolor, "fill_alpha", text="", slider=True)
# S Brush settings
gpencil_active_brush_settings_simple(context, pie)
# N - Active Layer
col = pie.column()
col.label("Active Layer: ")
row = col.row()
row.operator_context = 'EXEC_REGION_WIN'
row.operator_menu_enum("gpencil.layer_change", "layer", text="", icon='GREASEPENCIL')
row.prop(gpl, "info", text="")
row.operator("gpencil.layer_remove", text="", icon='X')
row = col.row()
row.prop(gpl, "lock")
row.prop(gpl, "hide")
col.prop(gpl, "use_onion_skinning")
# NW/NE/SW/SE - These operators are only available in editmode
# as they require strokes to be selected to work
if is_editmode:
# NW - Move stroke Down
col = pie.column(align=True)
col.label("Arrange Strokes")
col.operator("gpencil.stroke_arrange", text="Send to Back").direction = 'BOTTOM'
col.operator("gpencil.stroke_arrange", text="Send Backward").direction = 'DOWN'
# NE - Move stroke Up
col = pie.column(align=True)
col.label("Arrange Strokes")
col.operator("gpencil.stroke_arrange", text="Bring to Front").direction = 'TOP'
col.operator("gpencil.stroke_arrange", text="Bring Forward").direction = 'UP'
# SW - Move stroke to color
col = pie.column(align=True)
col.operator("gpencil.stroke_change_color", text="Move to Color")
# SE - Join strokes
col = pie.column(align=True)
col.label("Join Strokes")
row = col.row()
row.operator("gpencil.stroke_join", text="Join").type = 'JOIN'
row.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY'
col.operator("gpencil.stroke_flip", text="Flip direction")
col.prop(gpd, "show_stroke_direction", text="Show drawing direction")
class GPENCIL_PIE_tools_more(Menu):
"""A pie menu for accessing more Grease Pencil tools"""
bl_label = "More Grease Pencil Tools"
@classmethod
def poll(cls, context):
gpd = context.gpencil_data
return bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes)
def draw(self, context):
layout = self.layout
pie = layout.menu_pie()
# gpd = context.gpencil_data
col = pie.column(align=True)
col.operator("gpencil.copy", icon='COPYDOWN', text="Copy")
col.operator("gpencil.paste", icon='PASTEDOWN', text="Paste")
col = pie.column(align=True)
col.operator("gpencil.select_more", icon='ZOOMIN')
col.operator("gpencil.select_less", icon='ZOOMOUT')
pie.operator("transform.mirror", icon='MOD_MIRROR')
pie.operator("transform.bend", icon='MOD_SIMPLEDEFORM')
pie.operator("transform.shear", icon='MOD_TRIANGULATE')
pie.operator("transform.tosphere", icon='MOD_MULTIRES')
pie.operator("gpencil.convert", icon='OUTLINER_OB_CURVE', text="Convert...")
pie.operator("wm.call_menu_pie", text="Back to Main Palette...").name = "GPENCIL_PIE_tool_palette"
class GPENCIL_PIE_sculpt(Menu):
"""A pie menu for accessing Grease Pencil stroke sculpting settings"""
bl_label = "Grease Pencil Sculpt"
@classmethod
def poll(cls, context):
gpd = context.gpencil_data
return bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes)
def draw(self, context):
layout = self.layout
pie = layout.menu_pie()
settings = context.tool_settings.gpencil_sculpt
brush = settings.brush
# W - Launch Sculpt Mode
col = pie.column()
# col.label("Tool:")
col.prop(settings, "tool", text="")
col.operator("gpencil.brush_paint", text="Sculpt", icon='SCULPTMODE_HLT')
# E - Common Settings
col = pie.column(align=True)
col.prop(brush, "size", slider=True)
row = col.row(align=True)
row.prop(brush, "strength", slider=True)
# row.prop(brush, "use_pressure_strength", text="", icon_only=True)
col.prop(brush, "use_falloff")
if settings.tool in {'SMOOTH', 'RANDOMIZE'}:
row = col.row(align=True)
row.prop(settings, "affect_position", text="Position", icon='MESH_DATA', toggle=True)
row.prop(settings, "affect_strength", text="Strength", icon='COLOR', toggle=True)
row.prop(settings, "affect_thickness", text="Thickness", icon='LINE_DATA', toggle=True)
# S - Change Brush Type Shortcuts
row = pie.row()
row.prop_enum(settings, "tool", value='GRAB')
row.prop_enum(settings, "tool", value='PUSH')
row.prop_enum(settings, "tool", value='CLONE')
# N - Change Brush Type Shortcuts
row = pie.row()
row.prop_enum(settings, "tool", value='SMOOTH')
row.prop_enum(settings, "tool", value='THICKNESS')
row.prop_enum(settings, "tool", value='STRENGTH')
row.prop_enum(settings, "tool", value='RANDOMIZE')
###############################
class GPENCIL_MT_snap(Menu):
bl_label = "Snap"
def draw(self, context):
layout = self.layout
layout.operator("gpencil.snap_to_grid", text="Selection to Grid")
layout.operator("gpencil.snap_to_cursor", text="Selection to Cursor").use_offset = False
layout.operator("gpencil.snap_to_cursor", text="Selection to Cursor (Offset)").use_offset = True
layout.separator()
layout.operator("gpencil.snap_cursor_to_selected", text="Cursor to Selected")
layout.operator("view3d.snap_cursor_to_center", text="Cursor to Center")
layout.operator("view3d.snap_cursor_to_grid", text="Cursor to Grid")
class GPENCIL_MT_gpencil_edit_specials(Menu):
bl_label = "GPencil Specials"
def draw(self, context):
layout = self.layout
is_3d_view = context.space_data.type == 'VIEW_3D'
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("gpencil.stroke_subdivide", text="Subdivide")
layout.separator()
layout.operator("gpencil.stroke_join", text="Join").type = 'JOIN'
layout.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY'
layout.operator("gpencil.stroke_flip", text="Flip Direction")
if is_3d_view:
layout.separator()
layout.operator("gpencil.reproject")
###############################
class GPENCIL_UL_brush(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
# assert(isinstance(item, bpy.types.GPencilBrush)
brush = item
if self.layout_type in {'DEFAULT', 'COMPACT'}:
row = layout.row(align=True)
row.prop(brush, "name", text="", emboss=False, icon='BRUSH_DATA')
elif self.layout_type == 'GRID':
layout.alignment = 'CENTER'
layout.label(text="", icon_value=icon)
class GPENCIL_UL_palettecolor(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
# assert(isinstance(item, bpy.types.PaletteColor)
palcolor = item
if self.layout_type in {'DEFAULT', 'COMPACT'}:
if palcolor.lock:
layout.active = False
split = layout.split(percentage=0.25)
row = split.row(align=True)
row.enabled = not palcolor.lock
row.prop(palcolor, "color", text="", emboss=palcolor.is_stroke_visible)
row.prop(palcolor, "fill_color", text="", emboss=palcolor.is_fill_visible)
split.prop(palcolor, "name", text="", emboss=False)
row = layout.row(align=True)
row.prop(palcolor, "lock", text="", emboss=False)
row.prop(palcolor, "hide", text="", emboss=False)
if palcolor.ghost is True:
icon = 'GHOST_DISABLED'
else:
icon = 'GHOST_ENABLED'
row.prop(palcolor, "ghost", text="", icon=icon, emboss=False)
elif self.layout_type == 'GRID':
layout.alignment = 'CENTER'
layout.label(text="", icon_value=icon)
class GPENCIL_UL_layer(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
# assert(isinstance(item, bpy.types.GPencilLayer)
gpl = item
if self.layout_type in {'DEFAULT', 'COMPACT'}:
if gpl.lock:
layout.active = False
row = layout.row(align=True)
if gpl.is_parented:
icon = 'BONE_DATA'
else:
icon = 'BLANK1'
row.label(text="", icon=icon)
row.prop(gpl, "info", text="", emboss=False)
row = layout.row(align=True)
row.prop(gpl, "lock", text="", emboss=False)
row.prop(gpl, "hide", text="", emboss=False)
row.prop(gpl, "unlock_color", text="", emboss=False)
elif self.layout_type == 'GRID':
layout.alignment = 'CENTER'
layout.label(text="", icon_value=icon)
class GPENCIL_MT_layer_specials(Menu):
bl_label = "Layer"
def draw(self, context):
layout = self.layout
layout.operator("gpencil.layer_duplicate", icon='COPY_ID') # XXX: needs a dedicated icon
layout.separator()
layout.operator("gpencil.reveal", icon='RESTRICT_VIEW_OFF', text="Show All")
layout.operator("gpencil.hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True
layout.separator()
layout.operator("gpencil.lock_all", icon='LOCKED', text="Lock All")
layout.operator("gpencil.unlock_all", icon='UNLOCKED', text="UnLock All")
layout.separator()
layout.operator("gpencil.layer_merge", icon='NLA', text="Merge Down")
class GPENCIL_MT_brush_specials(Menu):
bl_label = "Layer"
def draw(self, context):
layout = self.layout
layout.operator("gpencil.brush_copy", icon='PASTEDOWN', text="Copy current drawing brush")
layout.operator("gpencil.brush_presets_create", icon='HELP', text="Create a set of predefined brushes")
class GPENCIL_MT_palettecolor_specials(Menu):
bl_label = "Layer"
def draw(self, context):
layout = self.layout
layout.operator("gpencil.palettecolor_reveal", icon='RESTRICT_VIEW_OFF', text="Show All")
layout.operator("gpencil.palettecolor_hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True
layout.separator()
layout.operator("gpencil.palettecolor_lock_all", icon='LOCKED', text="Lock All")
layout.operator("gpencil.palettecolor_unlock_all", icon='UNLOCKED', text="UnLock All")
layout.operator("gpencil.palettecolor_copy", icon='PASTEDOWN', text="Copy Color")
layout.separator()
layout.operator("gpencil.palettecolor_select", icon='COLOR', text="Select Strokes")
layout.operator("gpencil.stroke_change_color", icon='MAN_TRANS', text="Move to Color")
class GreasePencilDataPanel:
# subclass must set
# bl_space_type = 'IMAGE_EDITOR'
bl_label = "Grease Pencil Layers"
bl_region_type = 'UI'
@staticmethod
def draw_header(self, context):
self.layout.prop(context.space_data, "show_grease_pencil", text="")
@staticmethod
def draw(self, context):
layout = self.layout
# owner of Grease Pencil data
gpd_owner = context.gpencil_data_owner
gpd = context.gpencil_data
# Owner Selector
if context.space_data.type == 'VIEW_3D':
layout.prop(context.tool_settings, "grease_pencil_source", expand=True)
elif context.space_data.type == 'CLIP_EDITOR':
layout.prop(context.space_data, "grease_pencil_source", expand=True)
# Grease Pencil data selector
layout.template_ID(gpd_owner, "grease_pencil", new="gpencil.data_add", unlink="gpencil.data_unlink")
# Grease Pencil data...
if (gpd is None) or (not gpd.layers):
layout.operator("gpencil.layer_add", text="New Layer")
else:
self.draw_layers(context, layout, gpd)
def draw_layers(self, context, layout, gpd):
row = layout.row()
col = row.column()
if len(gpd.layers) >= 2:
layer_rows = 5
else:
layer_rows = 2
col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index", rows=layer_rows)
col = row.column()
sub = col.column(align=True)
sub.operator("gpencil.layer_add", icon='ZOOMIN', text="")
sub.operator("gpencil.layer_remove", icon='ZOOMOUT', text="")
gpl = context.active_gpencil_layer
if gpl:
sub.menu("GPENCIL_MT_layer_specials", icon='DOWNARROW_HLT', text="")
if len(gpd.layers) > 1:
col.separator()
sub = col.column(align=True)
sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP'
sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN'
col.separator()
sub = col.column(align=True)
sub.operator("gpencil.layer_isolate", icon='LOCKED', text="").affect_visibility = False
sub.operator("gpencil.layer_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True
if gpl:
self.draw_layer(context, layout, gpl)
def draw_layer(self, context, layout, gpl):
row = layout.row(align=True)
row.prop(gpl, "opacity", text="Opacity", slider=True)
# Layer options
split = layout.split(percentage=0.5)
split.active = not gpl.lock
split.prop(gpl, "show_x_ray")
split.prop(gpl, "show_points")
# Offsets + Parenting (where available)
if context.space_data.type == 'VIEW_3D':
split = layout.split(percentage=0.5)
else:
split = layout.column() # parenting is not available in 2D editors...
split.active = not gpl.lock
# Offsets - Color Tint
col = split.column()
subcol = col.column(align=True)
subcol.label("Tint")
subcol.enabled = not gpl.lock
subcol.prop(gpl, "tint_color", text="")
subcol.prop(gpl, "tint_factor", text="Factor", slider=True)
# Offsets - Thickness
row = col.row(align=True)
row.prop(gpl, "line_change", text="Thickness Change", slider=True)
row.operator("gpencil.stroke_apply_thickness", icon='STYLUS_PRESSURE', text="")
# Parenting
if context.space_data.type == 'VIEW_3D':
col = split.column(align=True)
col.label(text="Parent:")
col.prop(gpl, "parent", text="")
sub = col.column()
sub.prop(gpl, "parent_type", text="")
parent = gpl.parent
if parent and gpl.parent_type == 'BONE' and parent.type == 'ARMATURE':
sub.prop_search(gpl, "parent_bone", parent.data, "bones", text="")
layout.separator()
# Full-Row - Frame Locking (and Delete Frame)
row = layout.row(align=True)
row.active = not gpl.lock
if gpl.active_frame:
lock_status = iface_("Locked") if gpl.lock_frame else iface_("Unlocked")
lock_label = iface_("Frame: %d (%s)") % (gpl.active_frame.frame_number, lock_status)
else:
lock_label = iface_("Lock Frame")
row.prop(gpl, "lock_frame", text=lock_label, icon='UNLOCKED')
row.operator("gpencil.active_frame_delete", text="", icon='X')
layout.separator()
# Onion skinning
col = layout.column(align=True)
col.active = not gpl.lock
row = col.row()
row.prop(gpl, "use_onion_skinning")
sub = row.row(align=True)
icon = 'RESTRICT_RENDER_OFF' if gpl.use_ghosts_always else 'RESTRICT_RENDER_ON'
sub.prop(gpl, "use_ghosts_always", text="", icon=icon)
sub.prop(gpl, "use_ghost_custom_colors", text="", icon='COLOR')
split = col.split(percentage=0.5)
split.active = gpl.use_onion_skinning
# - Before Frames
sub = split.column(align=True)
row = sub.row(align=True)
row.active = gpl.use_ghost_custom_colors
row.prop(gpl, "before_color", text="")
sub.prop(gpl, "ghost_before_range", text="Before")
# - After Frames
sub = split.column(align=True)
row = sub.row(align=True)
row.active = gpl.use_ghost_custom_colors
row.prop(gpl, "after_color", text="")
sub.prop(gpl, "ghost_after_range", text="After")
class GreasePencilPaletteColorPanel:
# subclass must set
bl_label = "Grease Pencil Colors"
bl_region_type = 'UI'
@classmethod
def poll(cls, context):
if context.gpencil_data is None:
return False
gpd = context.gpencil_data
return bool(gpd.layers.active)
@staticmethod
def draw(self, context):
layout = self.layout
palette = context.active_gpencil_palette
if palette:
row = layout.row(align=True)
row.operator_context = 'EXEC_REGION_WIN'
row.operator_menu_enum("gpencil.palette_change", "palette", text="", icon='COLOR')
row.prop(palette, "name", text="")
row.operator("gpencil.palette_add", icon='ZOOMIN', text="")
row.operator("gpencil.palette_remove", icon='X', text="")
# Palette colors
row = layout.row()
col = row.column()
if len(palette.colors) >= 2:
color_rows = 5
else:
color_rows = 2
col.template_list("GPENCIL_UL_palettecolor", "", palette, "colors", palette.colors, "active_index",
rows=color_rows)
col = row.column()
sub = col.column(align=True)
sub.operator("gpencil.palettecolor_add", icon='ZOOMIN', text="")
sub.operator("gpencil.palettecolor_remove", icon='ZOOMOUT', text="")
palcol = context.active_gpencil_palettecolor
if palcol:
sub.menu("GPENCIL_MT_palettecolor_specials", icon='DOWNARROW_HLT', text="")
if len(palette.colors) > 1:
col.separator()
sub = col.column(align=True)
sub.operator("gpencil.palettecolor_move", icon='TRIA_UP', text="").direction = 'UP'
sub.operator("gpencil.palettecolor_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
row = layout.row()
sub = row.row(align=True)
sub.label(text="Isolate:") # based on active color only
sub.operator("gpencil.palettecolor_isolate", icon='LOCKED', text="").affect_visibility = False
sub.operator("gpencil.palettecolor_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True
sub = row.row(align=True)
sub.label(text="Lock:") # based on other stuff...
sub.operator("gpencil.stroke_lock_color", icon='BORDER_RECT', text="")
sub.operator("gpencil.palette_lock_layer", icon='COLOR', text="")
pcolor = palette.colors.active
if pcolor:
self.draw_palettecolors(layout, pcolor)
# Draw palette colors
def draw_palettecolors(self, layout, pcolor):
# color settings
split = layout.split(percentage=0.5)
split.active = not pcolor.lock
# Column 1 - Stroke
col = split.column(align=True)
col.enabled = not pcolor.lock
col.label(text="Stroke:")
col.prop(pcolor, "color", text="")
col.prop(pcolor, "alpha", slider=True)
# Column 2 - Fill
col = split.column(align=True)
col.enabled = not pcolor.lock
col.label(text="Fill:")
col.prop(pcolor, "fill_color", text="")
col.prop(pcolor, "fill_alpha", text="Opacity", slider=True)
# Options
split = layout.split(percentage=0.5)
split.active = not pcolor.lock
col = split.column(align=True)
col.active = not pcolor.lock
col.prop(pcolor, "use_volumetric_strokes")
col = split.column(align=True)
col.active = not pcolor.lock
col.prop(pcolor, "use_hq_fill")
class GreasePencilToolsPanel:
# For use in "2D" Editors without their own toolbar
# subclass must set
# bl_space_type = 'IMAGE_EDITOR'
# bl_options = {'DEFAULT_CLOSED'}
bl_label = "Grease Pencil Settings"
bl_region_type = 'UI'
@classmethod
def poll(cls, context):
return (context.gpencil_data is not None)
@staticmethod
def draw(self, context):
layout = self.layout
# gpd_owner = context.gpencil_data_owner
gpd = context.gpencil_data
layout.prop(gpd, "use_stroke_edit_mode", text="Enable Editing", icon='EDIT', toggle=True)
layout.separator()
layout.label("Proportional Edit:")
row = layout.row()
row.prop(context.tool_settings, "proportional_edit", text="")
row.prop(context.tool_settings, "proportional_edit_falloff", text="")
layout.separator()
layout.separator()
gpencil_active_brush_settings_simple(context, layout)
layout.separator()
gpencil_stroke_placement_settings(context, layout)
classes = (
GPENCIL_PIE_tool_palette,
GPENCIL_PIE_settings_palette,
GPENCIL_PIE_tools_more,
GPENCIL_PIE_sculpt,
GPENCIL_MT_snap,
GPENCIL_MT_gpencil_edit_specials,
GPENCIL_UL_brush,
GPENCIL_UL_palettecolor,
GPENCIL_UL_layer,
GPENCIL_MT_layer_specials,
GPENCIL_MT_brush_specials,
GPENCIL_MT_palettecolor_specials,
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)