Cleanup: unused variables
[blender.git] / release / scripts / startup / bl_ui / properties_grease_pencil_common.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21
22 from bpy.types import Menu, UIList
23 from bpy.app.translations import pgettext_iface as iface_
24
25
26 def gpencil_stroke_placement_settings(context, layout):
27     if context.space_data.type == 'VIEW_3D':
28         propname = "gpencil_stroke_placement_view3d"
29     elif context.space_data.type == 'SEQUENCE_EDITOR':
30         propname = "gpencil_stroke_placement_sequencer_preview"
31     elif context.space_data.type == 'IMAGE_EDITOR':
32         propname = "gpencil_stroke_placement_image_editor"
33     else:
34         propname = "gpencil_stroke_placement_view2d"
35
36     ts = context.tool_settings
37
38     col = layout.column(align=True)
39
40     col.label(text="Stroke Placement:")
41
42     row = col.row(align=True)
43     row.prop_enum(ts, propname, 'VIEW')
44     row.prop_enum(ts, propname, 'CURSOR')
45
46     if context.space_data.type == 'VIEW_3D':
47         row = col.row(align=True)
48         row.prop_enum(ts, propname, 'SURFACE')
49         row.prop_enum(ts, propname, 'STROKE')
50
51         row = col.row(align=False)
52         row.active = getattr(ts, propname) in {'SURFACE', 'STROKE'}
53         row.prop(ts, "use_gpencil_stroke_endpoints")
54
55         if context.scene.tool_settings.gpencil_stroke_placement_view3d == 'CURSOR':
56             row = col.row(align=True)
57             row.label("Lock axis:")
58             row = col.row(align=True)
59             row.prop(ts.gpencil_sculpt, "lockaxis", expand=True)
60
61
62 def gpencil_active_brush_settings_simple(context, layout):
63     brush = context.active_gpencil_brush
64     if brush is None:
65         layout.label("No Active Brush")
66         return
67
68     col = layout.column()
69     col.label("Active Brush:      ")
70
71     row = col.row(align=True)
72     row.operator_context = 'EXEC_REGION_WIN'
73     row.operator_menu_enum("gpencil.brush_change", "brush", text="", icon='BRUSH_DATA')
74     row.prop(brush, "name", text="")
75
76     col.prop(brush, "line_width", slider=True)
77     row = col.row(align=True)
78     row.prop(brush, "use_random_pressure", text="", icon='RNDCURVE')
79     row.prop(brush, "pen_sensitivity_factor", slider=True)
80     row.prop(brush, "use_pressure", text="", icon='STYLUS_PRESSURE')
81     row = col.row(align=True)
82     row.prop(brush, "use_random_strength", text="", icon='RNDCURVE')
83     row.prop(brush, "strength", slider=True)
84     row.prop(brush, "use_strength_pressure", text="", icon='STYLUS_PRESSURE')
85     row = col.row(align=True)
86     row.prop(brush, "jitter", slider=True)
87     row.prop(brush, "use_jitter_pressure", text="", icon='STYLUS_PRESSURE')
88     row = col.row()
89     row.prop(brush, "angle", slider=True)
90     row.prop(brush, "angle_factor", text="Factor", slider=True)
91
92
93 class GreasePencilDrawingToolsPanel:
94     # subclass must set
95     # bl_space_type = 'IMAGE_EDITOR'
96     bl_label = "Grease Pencil"
97     bl_category = "Grease Pencil"
98     bl_region_type = 'TOOLS'
99
100     @staticmethod
101     def draw(self, context):
102         layout = self.layout
103
104         is_3d_view = context.space_data.type == 'VIEW_3D'
105         is_clip_editor = context.space_data.type == 'CLIP_EDITOR'
106
107         col = layout.column(align=True)
108
109         col.label(text="Draw:")
110         row = col.row(align=True)
111         row.operator("gpencil.draw", icon='GREASEPENCIL', text="Draw").mode = 'DRAW'
112         row.operator("gpencil.draw", icon='FORCE_CURVE', text="Erase").mode = 'ERASER'  # XXX: Needs a dedicated icon
113
114         row = col.row(align=True)
115         row.operator("gpencil.draw", icon='LINE_DATA', text="Line").mode = 'DRAW_STRAIGHT'
116         row.operator("gpencil.draw", icon='MESH_DATA', text="Poly").mode = 'DRAW_POLY'
117
118         col.separator()
119
120         sub = col.column(align=True)
121         sub.operator("gpencil.blank_frame_add", icon='NEW')
122         sub.operator("gpencil.active_frames_delete_all", icon='X', text="Delete Frame(s)")
123
124         sub = col.column(align=True)
125         sub.prop(context.tool_settings, "use_gpencil_additive_drawing", text="Additive Drawing")
126         sub.prop(context.tool_settings, "use_gpencil_continuous_drawing", text="Continuous Drawing")
127         sub.prop(context.tool_settings, "use_gpencil_draw_onback", text="Draw on Back")
128
129         col.separator()
130         col.separator()
131
132         if context.space_data.type in {'VIEW_3D', 'CLIP_EDITOR'}:
133             col.separator()
134             col.label("Data Source:")
135             row = col.row(align=True)
136             if is_3d_view:
137                 row.prop(context.tool_settings, "grease_pencil_source", expand=True)
138             elif is_clip_editor:
139                 row.prop(context.space_data, "grease_pencil_source", expand=True)
140
141         col.separator()
142         col.separator()
143
144         gpencil_stroke_placement_settings(context, col)
145
146         gpd = context.gpencil_data
147
148         if gpd and not is_3d_view:
149             layout.separator()
150             layout.separator()
151
152             col = layout.column(align=True)
153             col.prop(gpd, "use_stroke_edit_mode", text="Enable Editing", icon='EDIT', toggle=True)
154
155         if is_3d_view:
156             col.separator()
157             col.separator()
158
159             col.label(text="Tools:")
160             col.operator_menu_enum("gpencil.convert", text="Convert to Geometry...", property="type")
161             col.operator("view3d.ruler")
162
163
164 class GreasePencilStrokeEditPanel:
165     # subclass must set
166     # bl_space_type = 'IMAGE_EDITOR'
167     bl_label = "Edit Strokes"
168     bl_category = "Grease Pencil"
169     bl_region_type = 'TOOLS'
170     bl_options = {'DEFAULT_CLOSED'}
171
172     @classmethod
173     def poll(cls, context):
174         if context.gpencil_data is None:
175             return False
176
177         gpd = context.gpencil_data
178         return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode)
179
180     @staticmethod
181     def draw(self, context):
182         layout = self.layout
183
184         is_3d_view = context.space_data.type == 'VIEW_3D'
185
186         if not is_3d_view:
187             layout.label(text="Select:")
188             col = layout.column(align=True)
189             col.operator("gpencil.select_all", text="Select All")
190             col.operator("gpencil.select_border")
191             col.operator("gpencil.select_circle")
192
193             layout.separator()
194
195             col = layout.column(align=True)
196             col.operator("gpencil.select_linked")
197             col.operator("gpencil.select_more")
198             col.operator("gpencil.select_less")
199             col.operator("gpencil.palettecolor_select")
200
201         layout.label(text="Edit:")
202         row = layout.row(align=True)
203         row.operator("gpencil.copy", text="Copy")
204         row.operator("gpencil.paste", text="Paste").type = 'COPY'
205         row.operator("gpencil.paste", text="Paste & Merge").type = 'MERGE'
206
207         col = layout.column(align=True)
208         col.operator("gpencil.delete")
209         col.operator("gpencil.duplicate_move", text="Duplicate")
210         if is_3d_view:
211             col.operator("gpencil.stroke_cyclical_set", text="Toggle Cyclic").type = 'TOGGLE'
212
213         layout.separator()
214
215         if not is_3d_view:
216             col = layout.column(align=True)
217             col.operator("transform.translate")                # icon='MAN_TRANS'
218             col.operator("transform.rotate")                   # icon='MAN_ROT'
219             col.operator("transform.resize", text="Scale")     # icon='MAN_SCALE'
220
221             layout.separator()
222
223         col = layout.column(align=True)
224         col.operator("transform.bend", text="Bend")
225         col.operator("transform.mirror", text="Mirror")
226         col.operator("transform.shear", text="Shear")
227         col.operator("transform.tosphere", text="To Sphere")
228
229         layout.separator()
230         col = layout.column(align=True)
231         col.operator_menu_enum("gpencil.stroke_arrange", text="Arrange Strokes...", property="direction")
232         col.operator("gpencil.stroke_change_color", text="Move to Color")
233
234         if is_3d_view:
235             layout.separator()
236
237         layout.separator()
238         col = layout.column(align=True)
239         col.operator("gpencil.stroke_subdivide", text="Subdivide")
240         col.operator("gpencil.stroke_join", text="Join").type = 'JOIN'
241         col.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY'
242         col.operator("gpencil.stroke_flip", text="Flip Direction")
243
244         gpd = context.gpencil_data
245         if gpd:
246             col.prop(gpd, "show_stroke_direction", text="Show Directions")
247
248         if is_3d_view:
249             layout.separator()
250             layout.operator_menu_enum("gpencil.reproject", text="Reproject Strokes...", property="type")
251
252
253 class GreasePencilInterpolatePanel:
254     bl_space_type = 'VIEW_3D'
255     bl_label = "Interpolate"
256     bl_category = "Grease Pencil"
257     bl_region_type = 'TOOLS'
258     bl_options = {'DEFAULT_CLOSED'}
259
260     @classmethod
261     def poll(cls, context):
262         if context.gpencil_data is None:
263             return False
264         elif context.space_data.type != 'VIEW_3D':
265             return False
266
267         gpd = context.gpencil_data
268         return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode)
269
270     @staticmethod
271     def draw(self, context):
272         layout = self.layout
273         settings = context.tool_settings.gpencil_interpolate
274
275         col = layout.column(align=True)
276         col.operator("gpencil.interpolate", text="Interpolate")
277         col.operator("gpencil.interpolate_sequence", text="Sequence")
278         col.operator("gpencil.interpolate_reverse", text="Remove Breakdowns")
279
280         col = layout.column(align=True)
281         col.label(text="Options:")
282         col.prop(settings, "interpolate_all_layers")
283         col.prop(settings, "interpolate_selected_only")
284
285         col = layout.column(align=True)
286         col.label(text="Sequence Options:")
287         col.prop(settings, "type")
288         if settings.type == 'CUSTOM':
289             box = layout.box()
290             # TODO: Options for loading/saving curve presets?
291             box.template_curve_mapping(settings, "interpolation_curve", brush=True)
292         elif settings.type != 'LINEAR':
293             col.prop(settings, "easing")
294
295             if settings.type == 'BACK':
296                 layout.prop(settings, "back")
297             elif setting.type == 'ELASTIC':
298                 sub = layout.column(align=True)
299                 sub.prop(settings, "amplitude")
300                 sub.prop(settings, "period")
301
302
303 class GreasePencilBrushPanel:
304     # subclass must set
305     # bl_space_type = 'IMAGE_EDITOR'
306     bl_label = "Drawing Brushes"
307     bl_category = "Grease Pencil"
308     bl_region_type = 'TOOLS'
309
310     @staticmethod
311     def draw(self, context):
312         layout = self.layout
313
314         row = layout.row()
315         col = row.column()
316         ts = context.scene.tool_settings
317         if len(ts.gpencil_brushes) >= 2:
318             brows = 3
319         else:
320             brows = 2
321         col.template_list("GPENCIL_UL_brush", "", ts, "gpencil_brushes", ts.gpencil_brushes, "active_index", rows=brows)
322
323         col = row.column()
324
325         sub = col.column(align=True)
326         sub.operator("gpencil.brush_add", icon='ZOOMIN', text="")
327         sub.operator("gpencil.brush_remove", icon='ZOOMOUT', text="")
328         sub.menu("GPENCIL_MT_brush_specials", icon='DOWNARROW_HLT', text="")
329         brush = context.active_gpencil_brush
330         if brush:
331             if len(ts.gpencil_brushes) > 1:
332                 col.separator()
333                 sub = col.column(align=True)
334                 sub.operator("gpencil.brush_move", icon='TRIA_UP', text="").type = 'UP'
335                 sub.operator("gpencil.brush_move", icon='TRIA_DOWN', text="").type = 'DOWN'
336
337         # Brush details
338         if brush is not None:
339             row = layout.row()
340             row.prop(brush, "line_width")
341             row = layout.row(align=True)
342             row.prop(brush, "use_random_pressure", text="", icon='RNDCURVE')
343             row.prop(brush, "pen_sensitivity_factor", slider=True)
344             row.prop(brush, "use_pressure", text="", icon='STYLUS_PRESSURE')
345             row = layout.row(align=True)
346             row.prop(brush, "use_random_strength", text="", icon='RNDCURVE')
347             row.prop(brush, "strength", slider=True)
348             row.prop(brush, "use_strength_pressure", text="", icon='STYLUS_PRESSURE')
349             row = layout.row(align=True)
350             row.prop(brush, "random_press", slider=True)
351
352             row = layout.row(align=True)
353             row.prop(brush, "jitter", slider=True)
354             row.prop(brush, "use_jitter_pressure", text="", icon='STYLUS_PRESSURE')
355             row = layout.row()
356             row.prop(brush, "angle", slider=True)
357             row.prop(brush, "angle_factor", text="Factor", slider=True)
358
359             box = layout.box()
360             col = box.column(align=True)
361             col.label(text="Stroke Quality:")
362             col.prop(brush, "pen_smooth_factor")
363             col.prop(brush, "pen_smooth_steps")
364             col.separator()
365             row = col.row(align=False)
366             row.prop(brush, "pen_subdivision_steps")
367             row.prop(brush, "random_subdiv", text="Randomness", slider=True)
368
369
370 class GreasePencilStrokeSculptPanel:
371     # subclass must set
372     # bl_space_type = 'IMAGE_EDITOR'
373     bl_label = "Sculpt Strokes"
374     bl_category = "Grease Pencil"
375     bl_region_type = 'TOOLS'
376
377     @classmethod
378     def poll(cls, context):
379         if context.gpencil_data is None:
380             return False
381
382         gpd = context.gpencil_data
383         return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode)
384
385     @staticmethod
386     def draw(self, context):
387         layout = self.layout
388
389         settings = context.tool_settings.gpencil_sculpt
390         tool = settings.tool
391         brush = settings.brush
392
393         layout.column().prop(settings, "tool")
394
395         col = layout.column()
396         col.prop(brush, "size", slider=True)
397         row = col.row(align=True)
398         row.prop(brush, "strength", slider=True)
399         row.prop(brush, "use_pressure_strength", text="")
400         col.prop(brush, "use_falloff")
401         if tool in {'SMOOTH', 'RANDOMIZE'}:
402             row = layout.row(align=True)
403             row.prop(settings, "affect_position", text="Position", icon='MESH_DATA', toggle=True)
404             row.prop(settings, "affect_strength", text="Strength", icon='COLOR', toggle=True)
405             row.prop(settings, "affect_thickness", text="Thickness", icon='LINE_DATA', toggle=True)
406
407         layout.separator()
408
409         if tool == 'THICKNESS':
410             layout.row().prop(brush, "direction", expand=True)
411         elif tool == 'PINCH':
412             row = layout.row(align=True)
413             row.prop_enum(brush, "direction", 'ADD', text="Pinch")
414             row.prop_enum(brush, "direction", 'SUBTRACT', text="Inflate")
415         elif settings.tool == 'TWIST':
416             row = layout.row(align=True)
417             row.prop_enum(brush, "direction", 'SUBTRACT', text="CW")
418             row.prop_enum(brush, "direction", 'ADD', text="CCW")
419
420         row = layout.row(align=True)
421         row.prop(settings, "use_select_mask")
422         row = layout.row(align=True)
423         row.prop(settings, "selection_alpha", slider=True)
424
425         if tool == 'SMOOTH':
426             layout.prop(brush, "affect_pressure")
427
428
429 class GreasePencilBrushCurvesPanel:
430     # subclass must set
431     # bl_space_type = 'IMAGE_EDITOR'
432     bl_label = "Brush Curves"
433     bl_category = "Grease Pencil"
434     bl_region_type = 'TOOLS'
435     bl_options = {'DEFAULT_CLOSED'}
436
437     @classmethod
438     def poll(cls, context):
439         if context.active_gpencil_brush is None:
440             return False
441
442         brush = context.active_gpencil_brush
443         return bool(brush)
444
445     @staticmethod
446     def draw(self, context):
447         layout = self.layout
448         brush = context.active_gpencil_brush
449         # Brush
450         layout.label("Sensitivity")
451         box = layout.box()
452         box.template_curve_mapping(brush, "curve_sensitivity", brush=True)
453
454         layout.label("Strength")
455         box = layout.box()
456         box.template_curve_mapping(brush, "curve_strength", brush=True)
457
458         layout.label("Jitter")
459         box = layout.box()
460         box.template_curve_mapping(brush, "curve_jitter", brush=True)
461
462
463 ###############################
464
465 class GPENCIL_MT_pie_tool_palette(Menu):
466     """A pie menu for quick access to Grease Pencil tools"""
467     bl_label = "Grease Pencil Tools"
468
469     def draw(self, context):
470         layout = self.layout
471
472         pie = layout.menu_pie()
473         gpd = context.gpencil_data
474
475         # W - Drawing Types
476         col = pie.column()
477         col.operator("gpencil.draw", text="Draw", icon='GREASEPENCIL').mode = 'DRAW'
478         col.operator("gpencil.draw", text="Straight Lines", icon='LINE_DATA').mode = 'DRAW_STRAIGHT'
479         col.operator("gpencil.draw", text="Poly", icon='MESH_DATA').mode = 'DRAW_POLY'
480
481         # E - Eraser
482         # XXX: needs a dedicated icon...
483         col = pie.column()
484         col.operator("gpencil.draw", text="Eraser", icon='FORCE_CURVE').mode = 'ERASER'
485
486         # E - "Settings" Palette is included here too, since it needs to be in a stable position...
487         if gpd and gpd.layers.active:
488             col.separator()
489             col.operator("wm.call_menu_pie", text="Settings...", icon='SCRIPTWIN').name = "GPENCIL_MT_pie_settings_palette"
490
491         # Editing tools
492         if gpd:
493             if gpd.use_stroke_edit_mode and context.editable_gpencil_strokes:
494                 # S - Exit Edit Mode
495                 pie.operator("gpencil.editmode_toggle", text="Exit Edit Mode", icon='EDIT')
496
497                 # N - Transforms
498                 col = pie.column()
499                 row = col.row(align=True)
500                 row.operator("transform.translate", icon='MAN_TRANS')
501                 row.operator("transform.rotate", icon='MAN_ROT')
502                 row.operator("transform.resize", text="Scale", icon='MAN_SCALE')
503                 row = col.row(align=True)
504                 row.label("Proportional Edit:")
505                 row.prop(context.tool_settings, "proportional_edit", text="", icon_only=True)
506                 row.prop(context.tool_settings, "proportional_edit_falloff", text="", icon_only=True)
507
508                 # NW - Select (Non-Modal)
509                 col = pie.column()
510                 col.operator("gpencil.select_all", text="Select All", icon='PARTICLE_POINT')
511                 col.operator("gpencil.select_all", text="Select Inverse", icon='BLANK1')
512                 col.operator("gpencil.select_linked", text="Select Linked", icon='LINKED')
513                 col.operator("gpencil.palettecolor_select", text="Select Color", icon='COLOR')
514
515                 # NE - Select (Modal)
516                 col = pie.column()
517                 col.operator("gpencil.select_border", text="Border Select", icon='BORDER_RECT')
518                 col.operator("gpencil.select_circle", text="Circle Select", icon='META_EMPTY')
519                 col.operator("gpencil.select_lasso", text="Lasso Select", icon='BORDER_LASSO')
520
521                 # SW - Edit Tools
522                 col = pie.column()
523                 col.operator("gpencil.duplicate_move", icon='PARTICLE_PATH', text="Duplicate")
524                 col.operator("gpencil.delete", icon='X', text="Delete...")
525
526                 # SE - More Tools
527                 pie.operator("wm.call_menu_pie", text="More...").name = "GPENCIL_MT_pie_tools_more"
528             else:
529                 # Toggle Edit Mode
530                 pie.operator("gpencil.editmode_toggle", text="Enable Stroke Editing", icon='EDIT')
531
532
533 class GPENCIL_MT_pie_settings_palette(Menu):
534     """A pie menu for quick access to Grease Pencil settings"""
535     bl_label = "Grease Pencil Settings"
536
537     @classmethod
538     def poll(cls, context):
539         return bool(context.gpencil_data and context.active_gpencil_layer)
540
541     def draw(self, context):
542         layout = self.layout
543
544         pie = layout.menu_pie()
545         gpd = context.gpencil_data
546         gpl = context.active_gpencil_layer
547         palcolor = context.active_gpencil_palettecolor
548         # brush = context.active_gpencil_brush
549
550         is_editmode = bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes)
551
552         # W - Stroke draw settings
553         col = pie.column(align=True)
554         if palcolor is not None:
555             col.enabled = not palcolor.lock
556             col.label(text="Stroke")
557             col.prop(palcolor, "color", text="")
558             col.prop(palcolor, "alpha", text="", slider=True)
559
560         # E - Fill draw settings
561         col = pie.column(align=True)
562         if palcolor is not None:
563             col.enabled = not palcolor.lock
564             col.label(text="Fill")
565             col.prop(palcolor, "fill_color", text="")
566             col.prop(palcolor, "fill_alpha", text="", slider=True)
567
568         # S Brush settings
569         gpencil_active_brush_settings_simple(context, pie)
570
571         # N - Active Layer
572         col = pie.column()
573         col.label("Active Layer:      ")
574
575         row = col.row()
576         row.operator_context = 'EXEC_REGION_WIN'
577         row.operator_menu_enum("gpencil.layer_change", "layer", text="", icon='GREASEPENCIL')
578         row.prop(gpl, "info", text="")
579         row.operator("gpencil.layer_remove", text="", icon='X')
580
581         row = col.row()
582         row.prop(gpl, "lock")
583         row.prop(gpl, "hide")
584         col.prop(gpl, "use_onion_skinning")
585
586         # NW/NE/SW/SE - These operators are only available in editmode
587         # as they require strokes to be selected to work
588         if is_editmode:
589             # NW - Move stroke Down
590             col = pie.column(align=True)
591             col.label("Arrange Strokes")
592             col.operator("gpencil.stroke_arrange", text="Send to Back").direction = 'BOTTOM'
593             col.operator("gpencil.stroke_arrange", text="Send Backward").direction = 'DOWN'
594
595             # NE - Move stroke Up
596             col = pie.column(align=True)
597             col.label("Arrange Strokes")
598             col.operator("gpencil.stroke_arrange", text="Bring to Front").direction = 'TOP'
599             col.operator("gpencil.stroke_arrange", text="Bring Forward").direction = 'UP'
600
601             # SW - Move stroke to color
602             col = pie.column(align=True)
603             col.operator("gpencil.stroke_change_color", text="Move to Color")
604
605             # SE - Join strokes
606             col = pie.column(align=True)
607             col.label("Join Strokes")
608             row = col.row()
609             row.operator("gpencil.stroke_join", text="Join").type = 'JOIN'
610             row.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY'
611             col.operator("gpencil.stroke_flip", text="Flip Direction")
612
613             col.prop(gpd, "show_stroke_direction", text="Show Drawing Direction")
614
615
616 class GPENCIL_MT_pie_tools_more(Menu):
617     """A pie menu for accessing more Grease Pencil tools"""
618     bl_label = "More Grease Pencil Tools"
619
620     @classmethod
621     def poll(cls, context):
622         gpd = context.gpencil_data
623         return bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes)
624
625     def draw(self, context):
626         layout = self.layout
627
628         pie = layout.menu_pie()
629         # gpd = context.gpencil_data
630
631         col = pie.column(align=True)
632         col.operator("gpencil.copy", icon='COPYDOWN', text="Copy")
633         col.operator("gpencil.paste", icon='PASTEDOWN', text="Paste")
634
635         col = pie.column(align=True)
636         col.operator("gpencil.select_more", icon='ZOOMIN')
637         col.operator("gpencil.select_less", icon='ZOOMOUT')
638
639         pie.operator("transform.mirror", icon='MOD_MIRROR')
640         pie.operator("transform.bend", icon='MOD_SIMPLEDEFORM')
641         pie.operator("transform.shear", icon='MOD_TRIANGULATE')
642         pie.operator("transform.tosphere", icon='MOD_MULTIRES')
643
644         pie.operator("gpencil.convert", icon='OUTLINER_OB_CURVE', text="Convert...")
645         pie.operator("wm.call_menu_pie", text="Back to Main Palette...").name = "GPENCIL_MT_pie_tool_palette"
646
647
648 class GPENCIL_MT_pie_sculpt(Menu):
649     """A pie menu for accessing Grease Pencil stroke sculpting settings"""
650     bl_label = "Grease Pencil Sculpt"
651
652     @classmethod
653     def poll(cls, context):
654         gpd = context.gpencil_data
655         return bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes)
656
657     def draw(self, context):
658         layout = self.layout
659
660         pie = layout.menu_pie()
661
662         settings = context.tool_settings.gpencil_sculpt
663         brush = settings.brush
664
665         # W - Launch Sculpt Mode
666         col = pie.column()
667         # col.label("Tool:")
668         col.prop(settings, "tool", text="")
669         col.operator("gpencil.brush_paint", text="Sculpt", icon='SCULPTMODE_HLT')
670
671         # E - Common Settings
672         col = pie.column(align=True)
673         col.prop(brush, "size", slider=True)
674         row = col.row(align=True)
675         row.prop(brush, "strength", slider=True)
676         # row.prop(brush, "use_pressure_strength", text="", icon_only=True)
677         col.prop(brush, "use_falloff")
678         if settings.tool in {'SMOOTH', 'RANDOMIZE'}:
679             row = col.row(align=True)
680             row.prop(settings, "affect_position", text="Position", icon='MESH_DATA', toggle=True)
681             row.prop(settings, "affect_strength", text="Strength", icon='COLOR', toggle=True)
682             row.prop(settings, "affect_thickness", text="Thickness", icon='LINE_DATA', toggle=True)
683
684         # S - Change Brush Type Shortcuts
685         row = pie.row()
686         row.prop_enum(settings, "tool", value='GRAB')
687         row.prop_enum(settings, "tool", value='PUSH')
688         row.prop_enum(settings, "tool", value='CLONE')
689
690         # N - Change Brush Type Shortcuts
691         row = pie.row()
692         row.prop_enum(settings, "tool", value='SMOOTH')
693         row.prop_enum(settings, "tool", value='THICKNESS')
694         row.prop_enum(settings, "tool", value='STRENGTH')
695         row.prop_enum(settings, "tool", value='RANDOMIZE')
696
697
698 ###############################
699
700
701 class GPENCIL_MT_snap(Menu):
702     bl_label = "Snap"
703
704     def draw(self, context):
705         layout = self.layout
706
707         layout.operator("gpencil.snap_to_grid", text="Selection to Grid")
708         layout.operator("gpencil.snap_to_cursor", text="Selection to Cursor").use_offset = False
709         layout.operator("gpencil.snap_to_cursor", text="Selection to Cursor (Offset)").use_offset = True
710
711         layout.separator()
712
713         layout.operator("gpencil.snap_cursor_to_selected", text="Cursor to Selected")
714         layout.operator("view3d.snap_cursor_to_center", text="Cursor to Center")
715         layout.operator("view3d.snap_cursor_to_grid", text="Cursor to Grid")
716
717
718 class GPENCIL_MT_gpencil_edit_specials(Menu):
719     bl_label = "GPencil Specials"
720
721     def draw(self, context):
722         layout = self.layout
723         is_3d_view = context.space_data.type == 'VIEW_3D'
724
725         layout.operator_context = 'INVOKE_REGION_WIN'
726
727         layout.operator("gpencil.stroke_subdivide", text="Subdivide")
728
729         layout.separator()
730
731         layout.operator("gpencil.stroke_join", text="Join").type = 'JOIN'
732         layout.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY'
733         layout.operator("gpencil.stroke_flip", text="Flip Direction")
734
735         if is_3d_view:
736             layout.separator()
737             layout.operator("gpencil.reproject")
738
739
740 ###############################
741
742
743 class GPENCIL_UL_brush(UIList):
744     def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
745         # assert(isinstance(item, bpy.types.GPencilBrush)
746         brush = item
747
748         if self.layout_type in {'DEFAULT', 'COMPACT'}:
749             row = layout.row(align=True)
750             row.prop(brush, "name", text="", emboss=False, icon='BRUSH_DATA')
751         elif self.layout_type == 'GRID':
752             layout.alignment = 'CENTER'
753             layout.label(text="", icon_value=icon)
754
755
756 class GPENCIL_UL_palettecolor(UIList):
757     def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
758         # assert(isinstance(item, bpy.types.PaletteColor)
759         palcolor = item
760
761         if self.layout_type in {'DEFAULT', 'COMPACT'}:
762             if palcolor.lock:
763                 layout.active = False
764
765             split = layout.split(percentage=0.25)
766             row = split.row(align=True)
767             row.enabled = not palcolor.lock
768             row.prop(palcolor, "color", text="", emboss=palcolor.is_stroke_visible)
769             row.prop(palcolor, "fill_color", text="", emboss=palcolor.is_fill_visible)
770             split.prop(palcolor, "name", text="", emboss=False)
771
772             row = layout.row(align=True)
773             row.prop(palcolor, "lock", text="", emboss=False)
774             row.prop(palcolor, "hide", text="", emboss=False)
775             if palcolor.ghost is True:
776                 icon = 'GHOST_DISABLED'
777             else:
778                 icon = 'GHOST_ENABLED'
779             row.prop(palcolor, "ghost", text="", icon=icon, emboss=False)
780
781         elif self.layout_type == 'GRID':
782             layout.alignment = 'CENTER'
783             layout.label(text="", icon_value=icon)
784
785
786 class GPENCIL_UL_layer(UIList):
787     def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
788         # assert(isinstance(item, bpy.types.GPencilLayer)
789         gpl = item
790
791         if self.layout_type in {'DEFAULT', 'COMPACT'}:
792             if gpl.lock:
793                 layout.active = False
794
795             row = layout.row(align=True)
796             if gpl.is_parented:
797                 icon = 'BONE_DATA'
798             else:
799                 icon = 'BLANK1'
800
801             row.label(text="", icon=icon)
802             row.prop(gpl, "info", text="", emboss=False)
803
804             row = layout.row(align=True)
805             row.prop(gpl, "lock", text="", emboss=False)
806             row.prop(gpl, "hide", text="", emboss=False)
807             row.prop(gpl, "unlock_color", text="", emboss=False)
808         elif self.layout_type == 'GRID':
809             layout.alignment = 'CENTER'
810             layout.label(text="", icon_value=icon)
811
812
813 class GPENCIL_MT_layer_specials(Menu):
814     bl_label = "Layer"
815
816     def draw(self, context):
817         layout = self.layout
818
819         layout.operator("gpencil.layer_duplicate", icon='COPY_ID')  # XXX: needs a dedicated icon
820
821         layout.separator()
822
823         layout.operator("gpencil.reveal", icon='RESTRICT_VIEW_OFF', text="Show All")
824         layout.operator("gpencil.hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True
825
826         layout.separator()
827
828         layout.operator("gpencil.lock_all", icon='LOCKED', text="Lock All")
829         layout.operator("gpencil.unlock_all", icon='UNLOCKED', text="UnLock All")
830
831         layout.separator()
832
833         layout.operator("gpencil.layer_merge", icon='NLA', text="Merge Down")
834
835
836 class GPENCIL_MT_brush_specials(Menu):
837     bl_label = "Layer"
838
839     def draw(self, context):
840         layout = self.layout
841         layout.operator("gpencil.brush_copy", icon='PASTEDOWN', text="Copy Current Drawing Brush")
842         layout.operator("gpencil.brush_presets_create", icon='HELP', text="Create a Set of Predefined Brushes")
843
844
845 class GPENCIL_MT_palettecolor_specials(Menu):
846     bl_label = "Layer"
847
848     def draw(self, context):
849         layout = self.layout
850
851         layout.operator("gpencil.palettecolor_reveal", icon='RESTRICT_VIEW_OFF', text="Show All")
852         layout.operator("gpencil.palettecolor_hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True
853
854         layout.separator()
855
856         layout.operator("gpencil.palettecolor_lock_all", icon='LOCKED', text="Lock All")
857         layout.operator("gpencil.palettecolor_unlock_all", icon='UNLOCKED', text="UnLock All")
858         layout.operator("gpencil.palettecolor_copy", icon='PASTEDOWN', text="Copy Color")
859
860         layout.separator()
861
862         layout.operator("gpencil.palettecolor_select", icon='COLOR', text="Select Strokes")
863         layout.operator("gpencil.stroke_change_color", icon='MAN_TRANS', text="Move to Color")
864
865
866 class GreasePencilDataPanel:
867     # subclass must set
868     # bl_space_type = 'IMAGE_EDITOR'
869     bl_label = "Grease Pencil Layers"
870     bl_region_type = 'UI'
871
872     @staticmethod
873     def draw_header(self, context):
874         self.layout.prop(context.space_data, "show_grease_pencil", text="")
875
876     @staticmethod
877     def draw(self, context):
878         layout = self.layout
879
880         # owner of Grease Pencil data
881         gpd_owner = context.gpencil_data_owner
882         gpd = context.gpencil_data
883
884         # Owner Selector
885         if context.space_data.type == 'VIEW_3D':
886             layout.row().prop(context.tool_settings, "grease_pencil_source", expand=True)
887         elif context.space_data.type == 'CLIP_EDITOR':
888             layout.row().prop(context.space_data, "grease_pencil_source", expand=True)
889
890         # Grease Pencil data selector
891         layout.template_ID(gpd_owner, "grease_pencil", new="gpencil.data_add", unlink="gpencil.data_unlink")
892
893         # Grease Pencil data...
894         if (gpd is None) or (not gpd.layers):
895             layout.operator("gpencil.layer_add", text="New Layer")
896         else:
897             self.draw_layers(context, layout, gpd)
898
899     def draw_layers(self, context, layout, gpd):
900         row = layout.row()
901
902         col = row.column()
903         if len(gpd.layers) >= 2:
904             layer_rows = 5
905         else:
906             layer_rows = 2
907         col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index", rows=layer_rows)
908
909         col = row.column()
910
911         sub = col.column(align=True)
912         sub.operator("gpencil.layer_add", icon='ZOOMIN', text="")
913         sub.operator("gpencil.layer_remove", icon='ZOOMOUT', text="")
914
915         gpl = context.active_gpencil_layer
916         if gpl:
917             sub.menu("GPENCIL_MT_layer_specials", icon='DOWNARROW_HLT', text="")
918
919             if len(gpd.layers) > 1:
920                 col.separator()
921
922                 sub = col.column(align=True)
923                 sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP'
924                 sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN'
925
926                 col.separator()
927
928                 sub = col.column(align=True)
929                 sub.operator("gpencil.layer_isolate", icon='LOCKED', text="").affect_visibility = False
930                 sub.operator("gpencil.layer_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True
931
932         if gpl:
933             self.draw_layer(context, layout, gpl)
934
935     def draw_layer(self, context, layout, gpl):
936         row = layout.row(align=True)
937         row.prop(gpl, "opacity", text="Opacity", slider=True)
938
939         # Layer options
940         split = layout.split(percentage=0.5)
941         split.active = not gpl.lock
942         split.prop(gpl, "show_x_ray")
943         split.prop(gpl, "show_points")
944
945         # Offsets + Parenting (where available)
946         if context.space_data.type == 'VIEW_3D':
947             split = layout.split(percentage=0.5)
948         else:
949             split = layout.column()  # parenting is not available in 2D editors...
950         split.active = not gpl.lock
951
952         # Offsets - Color Tint
953         col = split.column()
954         subcol = col.column(align=True)
955         subcol.label("Tint")
956         subcol.enabled = not gpl.lock
957         subcol.prop(gpl, "tint_color", text="")
958         subcol.prop(gpl, "tint_factor", text="Factor", slider=True)
959
960         # Offsets - Thickness
961         row = col.row(align=True)
962         row.prop(gpl, "line_change", text="Thickness Change", slider=True)
963         row.operator("gpencil.stroke_apply_thickness", icon='STYLUS_PRESSURE', text="")
964
965         # Parenting
966         if context.space_data.type == 'VIEW_3D':
967             col = split.column(align=True)
968             col.label(text="Parent:")
969             col.prop(gpl, "parent", text="")
970
971             sub = col.column()
972             sub.prop(gpl, "parent_type", text="")
973             parent = gpl.parent
974             if parent and gpl.parent_type == 'BONE' and parent.type == 'ARMATURE':
975                 sub.prop_search(gpl, "parent_bone", parent.data, "bones", text="")
976
977         layout.separator()
978
979         # Full-Row - Frame Locking (and Delete Frame)
980         row = layout.row(align=True)
981         row.active = not gpl.lock
982
983         if gpl.active_frame:
984             lock_status = iface_("Locked") if gpl.lock_frame else iface_("Unlocked")
985             lock_label = iface_("Frame: %d (%s)") % (gpl.active_frame.frame_number, lock_status)
986         else:
987             lock_label = iface_("Lock Frame")
988         row.prop(gpl, "lock_frame", text=lock_label, icon='UNLOCKED')
989         row.operator("gpencil.active_frame_delete", text="", icon='X')
990
991         layout.separator()
992
993         # Onion skinning
994         col = layout.column(align=True)
995         col.active = not gpl.lock
996
997         row = col.row()
998         row.prop(gpl, "use_onion_skinning")
999         sub = row.row(align=True)
1000         icon = 'RESTRICT_RENDER_OFF' if gpl.use_ghosts_always else 'RESTRICT_RENDER_ON'
1001         sub.prop(gpl, "use_ghosts_always", text="", icon=icon)
1002         sub.prop(gpl, "use_ghost_custom_colors", text="", icon='COLOR')
1003
1004         split = col.split(percentage=0.5)
1005         split.active = gpl.use_onion_skinning
1006
1007         # - Before Frames
1008         sub = split.column(align=True)
1009         row = sub.row(align=True)
1010         row.active = gpl.use_ghost_custom_colors
1011         row.prop(gpl, "before_color", text="")
1012         sub.prop(gpl, "ghost_before_range", text="Before")
1013
1014         # - After Frames
1015         sub = split.column(align=True)
1016         row = sub.row(align=True)
1017         row.active = gpl.use_ghost_custom_colors
1018         row.prop(gpl, "after_color", text="")
1019         sub.prop(gpl, "ghost_after_range", text="After")
1020
1021
1022 class GreasePencilPaletteColorPanel:
1023     # subclass must set
1024     bl_label = "Grease Pencil Colors"
1025     bl_region_type = 'UI'
1026
1027     @classmethod
1028     def poll(cls, context):
1029         if context.gpencil_data is None:
1030             return False
1031
1032         gpd = context.gpencil_data
1033         return bool(gpd.layers.active)
1034
1035     @staticmethod
1036     def draw(self, context):
1037         layout = self.layout
1038         palette = context.active_gpencil_palette
1039
1040         if palette:
1041             row = layout.row(align=True)
1042             row.operator_context = 'EXEC_REGION_WIN'
1043             row.operator_menu_enum("gpencil.palette_change", "palette", text="", icon='COLOR')
1044             row.prop(palette, "name", text="")
1045             row.operator("gpencil.palette_add", icon='ZOOMIN', text="")
1046             row.operator("gpencil.palette_remove", icon='X', text="")
1047
1048             # Palette colors
1049             row = layout.row()
1050             col = row.column()
1051             if len(palette.colors) >= 2:
1052                 color_rows = 5
1053             else:
1054                 color_rows = 2
1055             col.template_list("GPENCIL_UL_palettecolor", "", palette, "colors", palette.colors, "active_index",
1056                               rows=color_rows)
1057
1058             col = row.column()
1059
1060             sub = col.column(align=True)
1061             sub.operator("gpencil.palettecolor_add", icon='ZOOMIN', text="")
1062             sub.operator("gpencil.palettecolor_remove", icon='ZOOMOUT', text="")
1063
1064             palcol = context.active_gpencil_palettecolor
1065             if palcol:
1066                 sub.menu("GPENCIL_MT_palettecolor_specials", icon='DOWNARROW_HLT', text="")
1067
1068             if len(palette.colors) > 1:
1069                 col.separator()
1070
1071                 sub = col.column(align=True)
1072                 sub.operator("gpencil.palettecolor_move", icon='TRIA_UP', text="").direction = 'UP'
1073                 sub.operator("gpencil.palettecolor_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
1074
1075                 row = layout.row()
1076                 sub = row.row(align=True)
1077                 sub.label(text="Isolate:")  # based on active color only
1078                 sub.operator("gpencil.palettecolor_isolate", icon='LOCKED', text="").affect_visibility = False
1079                 sub.operator("gpencil.palettecolor_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True
1080                 sub = row.row(align=True)
1081                 sub.label(text="Lock:")  # based on other stuff...
1082                 sub.operator("gpencil.stroke_lock_color", icon='BORDER_RECT', text="")
1083                 sub.operator("gpencil.palette_lock_layer", icon='COLOR', text="")
1084
1085             pcolor = palette.colors.active
1086             if pcolor:
1087                 self.draw_palettecolors(layout, pcolor)
1088
1089     # Draw palette colors
1090     def draw_palettecolors(self, layout, pcolor):
1091         # color settings
1092         split = layout.split(percentage=0.5)
1093         split.active = not pcolor.lock
1094
1095         # Column 1 - Stroke
1096         col = split.column(align=True)
1097         col.enabled = not pcolor.lock
1098         col.label(text="Stroke:")
1099         col.prop(pcolor, "color", text="")
1100         col.prop(pcolor, "alpha", slider=True)
1101
1102         # Column 2 - Fill
1103         col = split.column(align=True)
1104         col.enabled = not pcolor.lock
1105         col.label(text="Fill:")
1106         col.prop(pcolor, "fill_color", text="")
1107         col.prop(pcolor, "fill_alpha", text="Opacity", slider=True)
1108
1109         # Options
1110         split = layout.split(percentage=0.5)
1111         split.active = not pcolor.lock
1112
1113         col = split.column(align=True)
1114         col.active = not pcolor.lock
1115         col.prop(pcolor, "use_volumetric_strokes")
1116         col = split.column(align=True)
1117         col.active = not pcolor.lock
1118         col.prop(pcolor, "use_hq_fill")
1119
1120
1121 class GreasePencilToolsPanel:
1122     # For use in "2D" Editors without their own toolbar
1123     # subclass must set
1124     # bl_space_type = 'IMAGE_EDITOR'
1125     # bl_options = {'DEFAULT_CLOSED'}
1126     bl_label = "Grease Pencil Settings"
1127     bl_region_type = 'UI'
1128
1129     @classmethod
1130     def poll(cls, context):
1131         return (context.gpencil_data is not None)
1132
1133     @staticmethod
1134     def draw(self, context):
1135         layout = self.layout
1136
1137         # gpd_owner = context.gpencil_data_owner
1138         gpd = context.gpencil_data
1139
1140         layout.prop(gpd, "use_stroke_edit_mode", text="Enable Editing", icon='EDIT', toggle=True)
1141
1142         layout.separator()
1143
1144         layout.label("Proportional Edit:")
1145         row = layout.row()
1146         row.prop(context.tool_settings, "proportional_edit", text="")
1147         row.prop(context.tool_settings, "proportional_edit_falloff", text="")
1148
1149         layout.separator()
1150         layout.separator()
1151
1152         gpencil_active_brush_settings_simple(context, layout)
1153
1154         layout.separator()
1155
1156         gpencil_stroke_placement_settings(context, layout)
1157
1158
1159 classes = (
1160     GPENCIL_MT_pie_tool_palette,
1161     GPENCIL_MT_pie_settings_palette,
1162     GPENCIL_MT_pie_tools_more,
1163     GPENCIL_MT_pie_sculpt,
1164     GPENCIL_MT_snap,
1165     GPENCIL_MT_gpencil_edit_specials,
1166     GPENCIL_UL_brush,
1167     GPENCIL_UL_palettecolor,
1168     GPENCIL_UL_layer,
1169     GPENCIL_MT_layer_specials,
1170     GPENCIL_MT_brush_specials,
1171     GPENCIL_MT_palettecolor_specials,
1172 )
1173
1174 if __name__ == "__main__":  # only for live edit.
1175     from bpy.utils import register_class
1176     for cls in classes:
1177         register_class(cls)