UI: add painting blend mode to top-bar
[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 import bpy
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 = "annotation_stroke_placement_view3d"
29     elif context.space_data.type == 'SEQUENCE_EDITOR':
30         propname = "annotation_stroke_placement_sequencer_preview"
31     elif context.space_data.type == 'IMAGE_EDITOR':
32         propname = "annotation_stroke_placement_image_editor"
33     else:
34         propname = "annotation_stroke_placement_view2d"
35
36     tool_settings = context.tool_settings
37
38     col = layout.column(align=True)
39
40     if context.space_data.type != 'VIEW_3D':
41         col.label(text="Stroke Placement:")
42         row = col.row(align=True)
43         row.prop_enum(tool_settings, propname, 'VIEW')
44         row.prop_enum(tool_settings, propname, 'CURSOR', text="Cursor")
45
46
47 def gpencil_active_brush_settings_simple(context, layout):
48     tool_settings = context.tool_settings
49     brush = tool_settings.gpencil_paint.brush
50     if brush is None:
51         layout.label(text="No Active Brush")
52         return
53
54     col = layout.column()
55     col.label(text="Active Brush:      ")
56
57     row = col.row(align=True)
58     row.operator_context = 'EXEC_REGION_WIN'
59     row.operator_menu_enum("gpencil.brush_change", "brush", text="", icon='BRUSH_DATA')
60     row.prop(brush, "name", text="")
61
62     col.prop(brush, "size", slider=True)
63     row = col.row(align=True)
64     row.prop(brush, "use_random_pressure", text="", icon='RNDCURVE')
65     row.prop(brush, "pen_sensitivity_factor", slider=True)
66     row.prop(brush, "use_pressure", text="", icon='STYLUS_PRESSURE')
67     row = col.row(align=True)
68     row.prop(brush, "use_random_strength", text="", icon='RNDCURVE')
69     row.prop(brush, "strength", slider=True)
70     row.prop(brush, "use_strength_pressure", text="", icon='STYLUS_PRESSURE')
71     row = col.row(align=True)
72     row.prop(brush, "jitter", slider=True)
73     row.prop(brush, "use_jitter_pressure", text="", icon='STYLUS_PRESSURE')
74     row = col.row()
75     row.prop(brush, "angle", slider=True)
76     row.prop(brush, "angle_factor", text="Factor", slider=True)
77
78
79 # XXX: To be replaced with active tools
80 class AnnotationDrawingToolsPanel:
81     # subclass must set
82     # bl_space_type = 'IMAGE_EDITOR'
83     bl_label = "Annotation"
84     bl_category = "Annotation"
85     bl_region_type = 'TOOLS'
86
87     @classmethod
88     def poll(cls, context):
89         return True
90
91     @staticmethod
92     def draw(self, context):
93         layout = self.layout
94
95         is_3d_view = context.space_data.type == 'VIEW_3D'
96         is_clip_editor = context.space_data.type == 'CLIP_EDITOR'
97
98         col = layout.column(align=True)
99
100         col.label(text="Draw:")
101         row = col.row(align=True)
102         row.operator("gpencil.annotate", icon='GREASEPENCIL', text="Draw").mode = 'DRAW'
103         row.operator("gpencil.annotate", icon='FORCE_CURVE', text="Erase").mode = 'ERASER'  # XXX: Needs a dedicated icon
104
105         row = col.row(align=True)
106         row.operator("gpencil.annotate", icon='LINE_DATA', text="Line").mode = 'DRAW_STRAIGHT'
107         row.operator("gpencil.annotate", icon='MESH_DATA', text="Poly").mode = 'DRAW_POLY'
108
109         col.separator()
110
111         sub = col.column(align=True)
112         sub.operator("gpencil.blank_frame_add", icon='FILE_NEW')
113         sub.operator("gpencil.active_frames_delete_all", icon='X', text="Delete Frame(s)")
114
115         #sub = col.column(align=True)
116         #sub.prop(context.tool_settings, "use_gpencil_draw_additive", text="Additive Drawing")
117         #sub.prop(context.tool_settings, "use_gpencil_continuous_drawing", text="Continuous Drawing")
118         #sub.prop(context.tool_settings, "use_gpencil_draw_onback", text="Draw on Back")
119
120         col.separator()
121         col.separator()
122
123         if context.space_data.type in {'CLIP_EDITOR'}:
124             col.separator()
125             col.label(text="Data Source:")
126             row = col.row(align=True)
127             if is_3d_view:
128                 row.prop(context.tool_settings, "grease_pencil_source", expand=True)
129             elif is_clip_editor:
130                 row.prop(context.space_data, "grease_pencil_source", expand=True)
131
132         # col.separator()
133         # col.separator()
134
135         gpencil_stroke_placement_settings(context, col)
136
137         gpd = context.gpencil_data
138
139         if gpd and not is_3d_view:
140             layout.separator()
141             layout.separator()
142
143             col = layout.column(align=True)
144             col.prop(gpd, "use_stroke_edit_mode", text="Enable Editing", toggle=True)  # was: icon='EDIT'
145
146
147 class GreasePencilStrokeEditPanel:
148     # subclass must set
149     # bl_space_type = 'IMAGE_EDITOR'
150     bl_label = "Edit Strokes"
151     bl_category = "Tools"
152     bl_region_type = 'TOOLS'
153
154     @classmethod
155     def poll(cls, context):
156         if context.gpencil_data is None:
157             return False
158
159         gpd = context.gpencil_data
160         return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode)
161
162     @staticmethod
163     def draw(self, context):
164         layout = self.layout
165
166         is_3d_view = context.space_data.type == 'VIEW_3D'
167
168         if not is_3d_view:
169             layout.label(text="Select:")
170             col = layout.column(align=True)
171             col.operator("gpencil.select_all", text="Select All")
172             col.operator("gpencil.select_box")
173             col.operator("gpencil.select_circle")
174
175             layout.separator()
176
177             col = layout.column(align=True)
178             col.operator("gpencil.select_linked")
179             col.operator("gpencil.select_more")
180             col.operator("gpencil.select_less")
181             col.operator("gpencil.select_alternate")
182
183         layout.label(text="Edit:")
184         row = layout.row(align=True)
185         row.operator("gpencil.copy", text="Copy")
186         row.operator("gpencil.paste", text="Paste").type = 'COPY'
187         row.operator("gpencil.paste", text="Paste & Merge").type = 'MERGE'
188
189         col = layout.column(align=True)
190         col.operator("gpencil.delete")
191         col.operator("gpencil.duplicate_move", text="Duplicate")
192         if is_3d_view:
193             col.operator("gpencil.stroke_cyclical_set", text="Toggle Cyclic").type = 'TOGGLE'
194
195         layout.separator()
196
197         if not is_3d_view:
198             col = layout.column(align=True)
199             col.operator("transform.translate")                # icon='MAN_TRANS'
200             col.operator("transform.rotate")                   # icon='MAN_ROT'
201             col.operator("transform.resize", text="Scale")     # icon='MAN_SCALE'
202
203             layout.separator()
204
205         layout.separator()
206         col = layout.column(align=True)
207         col.operator_menu_enum("gpencil.stroke_arrange", text="Arrange Strokes...", property="direction")
208         col.operator("gpencil.stroke_change_color", text="Assign Material")
209
210         layout.separator()
211         col = layout.column(align=True)
212         col.operator("gpencil.stroke_subdivide", text="Subdivide")
213         row = col.row(align=True)
214         row.operator("gpencil.stroke_simplify_fixed", text="Simplify")
215         row.operator("gpencil.stroke_simplify", text="Adaptive")
216
217         col.separator()
218
219         row = col.row(align=True)
220         row.operator("gpencil.stroke_merge", text="Merge")
221         row.operator("gpencil.stroke_join", text="Join").type = 'JOIN'
222         row.operator("gpencil.stroke_join", text="& Copy").type = 'JOINCOPY'
223
224         col.operator("gpencil.stroke_flip", text="Flip Direction")
225
226         if is_3d_view:
227             layout.separator()
228
229             col = layout.column(align=True)
230             col.operator_menu_enum("gpencil.stroke_separate", text="Separate...", property="mode")
231             col.operator("gpencil.stroke_split", text="Split")
232
233             col = layout.column(align=True)
234             col.label(text="Cleanup:")
235             col.operator_menu_enum("gpencil.reproject", text="Reproject Strokes...", property="type")
236             col.operator_menu_enum("gpencil.frame_clean_fill", text="Clean Boundary Strokes...", property="mode")
237
238
239 class GreasePencilStrokeSculptPanel:
240     # subclass must set
241     # bl_space_type = 'IMAGE_EDITOR'
242     bl_label = "Sculpt Strokes"
243     bl_category = "Tools"
244
245     @staticmethod
246     def draw(self, context):
247         layout = self.layout
248         layout.use_property_split = True
249         layout.use_property_decorate = False
250
251         settings = context.tool_settings.gpencil_sculpt
252         tool = settings.sculpt_tool
253         brush = settings.brush
254
255         layout.template_icon_view(settings, "sculpt_tool", show_labels=True)
256
257         row = layout.row(align=True)
258         row.prop(brush, "size", slider=True)
259         sub = row.row(align=True)
260         sub.enabled = tool not in {'GRAB', 'CLONE'}
261         sub.prop(brush, "use_pressure_radius", text="")
262
263         row = layout.row(align=True)
264         row.prop(brush, "strength", slider=True)
265         row.prop(brush, "use_pressure_strength", text="")
266
267         layout.prop(brush, "use_falloff")
268
269         layout.use_property_split = False
270         if tool in {'THICKNESS', 'STRENGTH'}:
271             layout.row().prop(brush, "direction", expand=True)
272         elif tool == 'PINCH':
273             row = layout.row(align=True)
274             row.prop_enum(brush, "direction", value='ADD', text="Pinch")
275             row.prop_enum(brush, "direction", value='SUBTRACT', text="Inflate")
276         elif tool == 'TWIST':
277             row = layout.row(align=True)
278             row.prop_enum(brush, "direction", value='ADD', text="CCW")
279             row.prop_enum(brush, "direction", value='SUBTRACT', text="CW")
280
281
282 class GreasePencilSculptOptionsPanel:
283     bl_label = "Sculpt Strokes"
284
285     @classmethod
286     def poll(cls, context):
287         settings = context.tool_settings.gpencil_sculpt
288         tool = settings.sculpt_tool
289
290         return bool(tool in {'SMOOTH', 'RANDOMIZE', 'SMOOTH'})
291
292     @staticmethod
293     def draw(self, context):
294         layout = self.layout
295         layout.use_property_split = True
296         layout.use_property_decorate = False
297
298         settings = context.tool_settings.gpencil_sculpt
299         tool = settings.sculpt_tool
300         brush = settings.brush
301
302         if tool in {'SMOOTH', 'RANDOMIZE'}:
303             layout.prop(settings, "use_edit_position", text="Affect Position")
304             layout.prop(settings, "use_edit_strength", text="Affect Strength")
305             layout.prop(settings, "use_edit_thickness", text="Affect Thickness")
306
307             if tool == 'SMOOTH':
308                 layout.prop(brush, "use_edit_pressure")
309
310             layout.prop(settings, "use_edit_uv", text="Affect UV")
311
312
313 # GP Object Tool Settings
314 class GreasePencilAppearancePanel:
315     bl_label = "Brush Appearance"
316     bl_options = {'DEFAULT_CLOSED'}
317
318     @classmethod
319     def poll(cls, context):
320         ob = context.active_object
321         return ob and ob.type == 'GPENCIL'
322
323     @staticmethod
324     def draw(self, context):
325         layout = self.layout
326         layout.use_property_split = True
327         layout.use_property_decorate = False
328
329         tool_settings = context.tool_settings
330         ob = context.active_object
331
332         if ob.mode == 'PAINT_GPENCIL':
333             brush = tool_settings.gpencil_paint.brush
334             gp_settings = brush.gpencil_settings
335
336             sub = layout.column(align=True)
337             sub.enabled = not brush.use_custom_icon
338             sub.prop(gp_settings, "gp_icon", text="Icon")
339
340             layout.prop(brush, "use_custom_icon")
341             sub = layout.column()
342             sub.active = brush.use_custom_icon
343             sub.prop(brush, "icon_filepath", text="")
344
345             layout.prop(gp_settings, "use_cursor", text="Show Brush")
346
347             if brush.gpencil_tool == 'DRAW':
348                 layout.prop(gp_settings, "show_lasso", text="Show fill color while drawing")
349
350             if brush.gpencil_tool == 'FILL':
351                 layout.prop(brush, "cursor_color_add", text="Color")
352
353         elif ob.mode in {'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}:
354             settings = tool_settings.gpencil_sculpt
355             brush = settings.brush
356             tool = settings.sculpt_tool
357
358             col = layout.column(align=True)
359             col.prop(brush, "use_cursor", text="Show Brush")
360
361             if tool in {'THICKNESS', 'STRENGTH'}:
362                 col.prop(brush, "cursor_color_add", text="Add")
363                 col.prop(brush, "cursor_color_sub", text="Subtract")
364             elif tool == 'PINCH':
365                 col.prop(brush, "cursor_color_add", text="Pinch")
366                 col.prop(brush, "cursor_color_sub", text="Inflate")
367             elif tool == 'TWIST':
368                 col.prop(brush, "cursor_color_add", text="CCW")
369                 col.prop(brush, "cursor_color_sub", text="CW")
370             else:
371                 col.prop(brush, "cursor_color_add", text="")
372
373
374 class GPENCIL_MT_pie_tool_palette(Menu):
375     """A pie menu for quick access to Grease Pencil tools"""
376     bl_label = "Grease Pencil Tools"
377
378     def draw(self, context):
379         layout = self.layout
380
381         pie = layout.menu_pie()
382         gpd = context.gpencil_data
383
384         # W - Drawing Types
385         col = pie.column()
386         col.operator("gpencil.draw", text="Draw", icon='GREASEPENCIL').mode = 'DRAW'
387         col.operator("gpencil.draw", text="Straight Lines", icon='LINE_DATA').mode = 'DRAW_STRAIGHT'
388         col.operator("gpencil.draw", text="Poly", icon='MESH_DATA').mode = 'DRAW_POLY'
389
390         # E - Eraser
391         # XXX: needs a dedicated icon...
392         col = pie.column()
393         col.operator("gpencil.draw", text="Eraser", icon='FORCE_CURVE').mode = 'ERASER'
394
395         # E - "Settings" Palette is included here too, since it needs to be in a stable position...
396         if gpd and gpd.layers.active:
397             col.separator()
398             col.operator("wm.call_menu_pie", text="Settings...", icon='SCRIPTWIN').name = "GPENCIL_MT_pie_settings_palette"
399
400         # Editing tools
401         if gpd:
402             if gpd.use_stroke_edit_mode and context.editable_gpencil_strokes:
403                 # S - Exit Edit Mode
404                 pie.operator("gpencil.editmode_toggle", text="Exit Edit Mode", icon='EDIT')
405
406                 # N - Transforms
407                 col = pie.column()
408                 row = col.row(align=True)
409                 row.operator("transform.translate", icon='MAN_TRANS')
410                 row.operator("transform.rotate", icon='MAN_ROT')
411                 row.operator("transform.resize", text="Scale", icon='MAN_SCALE')
412                 row = col.row(align=True)
413                 row.label(text="Proportional Edit:")
414                 row.prop(context.tool_settings, "proportional_edit", text="", icon_only=True)
415                 row.prop(context.tool_settings, "proportional_edit_falloff", text="", icon_only=True)
416
417                 # NW - Select (Non-Modal)
418                 col = pie.column()
419                 col.operator("gpencil.select_all", text="Select All", icon='PARTICLE_POINT')
420                 col.operator("gpencil.select_all", text="Select Inverse", icon='BLANK1')
421                 col.operator("gpencil.select_linked", text="Select Linked", icon='LINKED')
422                 col.operator("gpencil.palettecolor_select", text="Select Color", icon='COLOR')
423
424                 # NE - Select (Modal)
425                 col = pie.column()
426                 col.operator("gpencil.select_box", text="Box Select", icon='BORDER_RECT')
427                 col.operator("gpencil.select_circle", text="Circle Select", icon='META_EMPTY')
428                 col.operator("gpencil.select_lasso", text="Lasso Select", icon='BORDER_LASSO')
429                 col.operator("gpencil.select_alternate", text="Alternate Select", icon='BORDER_LASSO')
430
431                 # SW - Edit Tools
432                 col = pie.column()
433                 col.operator("gpencil.duplicate_move", icon='PARTICLE_PATH', text="Duplicate")
434                 col.operator("gpencil.delete", icon='X', text="Delete...")
435
436                 # SE - More Tools
437                 pie.operator("wm.call_menu_pie", text="More...").name = "GPENCIL_MT_pie_tools_more"
438             else:
439                 # Toggle Edit Mode
440                 pie.operator("gpencil.editmode_toggle", text="Enable Stroke Editing", icon='EDIT')
441
442
443 class GPENCIL_MT_pie_settings_palette(Menu):
444     """A pie menu for quick access to Grease Pencil settings"""
445     bl_label = "Grease Pencil Settings"
446
447     @classmethod
448     def poll(cls, context):
449         return bool(context.gpencil_data and context.active_gpencil_layer)
450
451     def draw(self, context):
452         layout = self.layout
453
454         pie = layout.menu_pie()
455         gpd = context.gpencil_data
456         gpl = context.active_gpencil_layer
457         palcolor = None  # context.active_gpencil_palettecolor
458
459         is_editmode = bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes)
460
461         # W - Stroke draw settings
462         col = pie.column(align=True)
463         if palcolor is not None:
464             col.enabled = not palcolor.lock
465             col.label(text="Stroke")
466             col.prop(palcolor, "color", text="")
467             col.prop(palcolor, "alpha", text="", slider=True)
468
469         # E - Fill draw settings
470         col = pie.column(align=True)
471         if palcolor is not None:
472             col.enabled = not palcolor.lock
473             col.label(text="Fill")
474             col.prop(palcolor, "fill_color", text="")
475             col.prop(palcolor, "fill_alpha", text="", slider=True)
476
477         # S Brush settings
478         gpencil_active_brush_settings_simple(context, pie)
479
480         # N - Active Layer
481         col = pie.column()
482         col.label(text="Active Layer:      ")
483
484         row = col.row()
485         row.operator_context = 'EXEC_REGION_WIN'
486         row.operator_menu_enum("gpencil.layer_change", "layer", text="", icon='GREASEPENCIL')
487         row.prop(gpl, "info", text="")
488         row.operator("gpencil.layer_remove", text="", icon='X')
489
490         row = col.row()
491         row.prop(gpl, "lock")
492         row.prop(gpl, "hide")
493         col.prop(gpl, "use_onion_skinning")
494
495         # NW/NE/SW/SE - These operators are only available in editmode
496         # as they require strokes to be selected to work
497         if is_editmode:
498             # NW - Move stroke Down
499             col = pie.column(align=True)
500             col.label(text="Arrange Strokes")
501             col.operator("gpencil.stroke_arrange", text="Send to Back").direction = 'BOTTOM'
502             col.operator("gpencil.stroke_arrange", text="Send Backward").direction = 'DOWN'
503
504             # NE - Move stroke Up
505             col = pie.column(align=True)
506             col.label(text="Arrange Strokes")
507             col.operator("gpencil.stroke_arrange", text="Bring to Front").direction = 'TOP'
508             col.operator("gpencil.stroke_arrange", text="Bring Forward").direction = 'UP'
509
510             # SW - Move stroke to color
511             col = pie.column(align=True)
512             col.operator("gpencil.stroke_change_color", text="Move to Color")
513
514             # SE - Join strokes
515             col = pie.column(align=True)
516             col.label(text="Join Strokes")
517             row = col.row()
518             row.operator("gpencil.stroke_join", text="Join").type = 'JOIN'
519             row.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY'
520             col.operator("gpencil.stroke_flip", text="Flip Direction")
521
522             col.prop(gpd, "show_stroke_direction", text="Show Drawing Direction")
523
524
525 class GPENCIL_MT_pie_tools_more(Menu):
526     """A pie menu for accessing more Grease Pencil tools"""
527     bl_label = "More Grease Pencil Tools"
528
529     @classmethod
530     def poll(cls, context):
531         gpd = context.gpencil_data
532         return bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes)
533
534     def draw(self, context):
535         layout = self.layout
536
537         pie = layout.menu_pie()
538         # gpd = context.gpencil_data
539
540         col = pie.column(align=True)
541         col.operator("gpencil.copy", icon='COPYDOWN', text="Copy")
542         col.operator("gpencil.paste", icon='PASTEDOWN', text="Paste")
543
544         col = pie.column(align=True)
545         col.operator("gpencil.select_more", icon='ADD')
546         col.operator("gpencil.select_less", icon='REMOVE')
547
548         pie.operator("transform.mirror", icon='MOD_MIRROR')
549         pie.operator("transform.bend", icon='MOD_SIMPLEDEFORM')
550         pie.operator("transform.shear", icon='MOD_TRIANGULATE')
551         pie.operator("transform.tosphere", icon='MOD_MULTIRES')
552
553         pie.operator("gpencil.convert", icon='OUTLINER_OB_CURVE', text="Convert...")
554         pie.operator("wm.call_menu_pie", text="Back to Main Palette...").name = "GPENCIL_MT_pie_tool_palette"
555
556
557 class GPENCIL_MT_pie_sculpt(Menu):
558     """A pie menu for accessing Grease Pencil stroke sculpt settings"""
559     bl_label = "Grease Pencil Sculpt"
560
561     @classmethod
562     def poll(cls, context):
563         gpd = context.gpencil_data
564         return bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes)
565
566     def draw(self, context):
567         layout = self.layout
568
569         pie = layout.menu_pie()
570
571         settings = context.tool_settings.gpencil_sculpt
572         brush = settings.brush
573
574         # W - Launch Sculpt Mode
575         col = pie.column()
576         # col.label(text="Tool:")
577         col.prop(settings, "sculpt_tool", text="")
578         col.operator("gpencil.sculpt_paint", text="Sculpt", icon='SCULPTMODE_HLT')
579
580         # E - Common Settings
581         col = pie.column(align=True)
582         col.prop(brush, "size", slider=True)
583         row = col.row(align=True)
584         row.prop(brush, "strength", slider=True)
585         # row.prop(brush, "use_pressure_strength", text="", icon_only=True)
586         col.prop(brush, "use_falloff")
587         if settings.sculpt_tool in {'SMOOTH', 'RANDOMIZE'}:
588             row = col.row(align=True)
589             row.prop(settings, "use_edit_position", text="Position", icon='MESH_DATA', toggle=True)
590             row.prop(settings, "use_edit_strength", text="Strength", icon='COLOR', toggle=True)
591             row.prop(settings, "use_edit_thickness", text="Thickness", icon='LINE_DATA', toggle=True)
592
593         # S - Change Brush Type Shortcuts
594         row = pie.row()
595         row.prop_enum(settings, "tool", value='GRAB')
596         row.prop_enum(settings, "tool", value='PUSH')
597         row.prop_enum(settings, "tool", value='CLONE')
598
599         # N - Change Brush Type Shortcuts
600         row = pie.row()
601         row.prop_enum(settings, "tool", value='SMOOTH')
602         row.prop_enum(settings, "tool", value='THICKNESS')
603         row.prop_enum(settings, "tool", value='STRENGTH')
604         row.prop_enum(settings, "tool", value='RANDOMIZE')
605
606
607 class GPENCIL_MT_snap(Menu):
608     bl_label = "Snap"
609
610     def draw(self, context):
611         layout = self.layout
612
613         layout.operator("gpencil.snap_to_grid", text="Selection to Grid")
614         layout.operator("gpencil.snap_to_cursor", text="Selection to Cursor").use_offset = False
615         layout.operator("gpencil.snap_to_cursor", text="Selection to Cursor (Keep Offset)").use_offset = True
616
617         layout.separator()
618
619         layout.operator("gpencil.snap_cursor_to_selected", text="Cursor to Selected")
620         layout.operator("view3d.snap_cursor_to_center", text="Cursor to World Origin")
621         layout.operator("view3d.snap_cursor_to_grid", text="Cursor to Grid")
622
623
624 class GPENCIL_MT_separate(Menu):
625     bl_label = "Separate"
626
627     def draw(self, context):
628         layout = self.layout
629         layout.operator("gpencil.stroke_separate", text="Selected Points").mode = 'POINT'
630         layout.operator("gpencil.stroke_separate", text="Selected Strokes").mode = 'STROKE'
631         layout.operator("gpencil.stroke_separate", text="Active Layer").mode = 'LAYER'
632
633
634 class GPENCIL_MT_gpencil_draw_specials(Menu):
635     bl_label = "GPencil Draw Specials"
636
637     def draw(self, context):
638         layout = self.layout
639
640         layout.operator_context = 'INVOKE_REGION_WIN'
641
642         layout.operator("gpencil.frame_duplicate", text="Duplicate Active Frame")
643         layout.operator("gpencil.frame_duplicate", text="Duplicate Active Frame All Layers").mode = 'ALL'
644
645         layout.separator()
646         layout.operator("gpencil.primitive", text="Line", icon='IPO_CONSTANT').type = 'LINE'
647         layout.operator("gpencil.primitive", text="Rectangle", icon='UV_FACESEL').type = 'BOX'
648         layout.operator("gpencil.primitive", text="Circle", icon='ANTIALIASED').type = 'CIRCLE'
649         layout.operator("gpencil.primitive", text="Arc", icon='SPHERECURVE').type = 'ARC'
650         layout.operator("gpencil.primitive", text="Curve", icon='CURVE_BEZCURVE').type = 'CURVE'
651
652
653 class GPENCIL_MT_gpencil_draw_delete(Menu):
654     bl_label = "GPencil Draw Delete"
655
656     def draw(self, context):
657         layout = self.layout
658
659         layout.operator_context = 'INVOKE_REGION_WIN'
660
661         layout.operator("gpencil.active_frames_delete_all", text="Delete Frame")
662
663
664 class GPENCIL_MT_cleanup(Menu):
665     bl_label = "Clean Up"
666
667     def draw(self, context):
668         layout = self.layout
669         layout.operator("gpencil.frame_clean_loose", text="Loose Points")
670         layout.separator()
671
672         layout.operator("gpencil.frame_clean_fill", text="Boundary Strokes").mode = 'ACTIVE'
673         layout.operator("gpencil.frame_clean_fill", text="Boundary Strokes all Frames").mode = 'ALL'
674         layout.separator()
675
676         layout.operator("gpencil.reproject")
677
678
679 class GPENCIL_UL_annotation_layer(UIList):
680     def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
681         # assert(isinstance(item, bpy.types.GPencilLayer)
682         gpl = item
683
684         if self.layout_type in {'DEFAULT', 'COMPACT'}:
685             if gpl.lock:
686                 layout.active = False
687
688             split = layout.split(factor=0.2)
689             split.prop(gpl, "color", text="", emboss=True)
690             split.prop(gpl, "info", text="", emboss=False)
691
692             row = layout.row(align=True)
693             # row.prop(gpl, "lock", text="", emboss=False)
694             row.prop(gpl, "hide", text="", emboss=False)
695         elif self.layout_type == 'GRID':
696             layout.alignment = 'CENTER'
697             layout.label(text="", icon_value=icon)
698
699
700 class AnnotationDataPanel:
701     bl_label = "Annotations"
702     bl_region_type = 'UI'
703     bl_options = {'DEFAULT_CLOSED'}
704
705     @classmethod
706     def poll(cls, context):
707         # Show this panel as long as someone that might own this exists
708         # AND the owner isn't an object (e.g. GP Object)
709         if context.gpencil_data_owner is None:
710             return False
711         elif type(context.gpencil_data_owner) is bpy.types.Object:
712             return False
713         else:
714             return True
715
716     @staticmethod
717     def draw_header(self, context):
718         if context.space_data.type not in {'VIEW_3D', 'TOPBAR'}:
719             self.layout.prop(context.space_data, "show_annotation", text="")
720
721     @staticmethod
722     def draw(self, context):
723         layout = self.layout
724         layout.use_property_decorate = False
725
726         # Grease Pencil owner.
727         gpd_owner = context.gpencil_data_owner
728         gpd = context.gpencil_data
729
730         # Owner selector.
731         if context.space_data.type == 'CLIP_EDITOR':
732             layout.row().prop(context.space_data, "grease_pencil_source", expand=True)
733
734         layout.template_ID(gpd_owner, "grease_pencil", new="gpencil.data_add", unlink="gpencil.data_unlink")
735
736         # List of layers/notes.
737         if gpd and gpd.layers:
738             self.draw_layers(context, layout, gpd)
739
740     def draw_layers(self, context, layout, gpd):
741         row = layout.row()
742
743         col = row.column()
744         if len(gpd.layers) >= 2:
745             layer_rows = 5
746         else:
747             layer_rows = 3
748         col.template_list("GPENCIL_UL_annotation_layer", "", gpd, "layers", gpd.layers, "active_index",
749                           rows=layer_rows, reverse=True)
750
751         col = row.column()
752
753         sub = col.column(align=True)
754         sub.operator("gpencil.layer_add", icon='ADD', text="")
755         sub.operator("gpencil.layer_remove", icon='REMOVE', text="")
756
757         gpl = context.active_gpencil_layer
758         if gpl:
759             if len(gpd.layers) > 1:
760                 col.separator()
761
762                 sub = col.column(align=True)
763                 sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP'
764                 sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN'
765
766         tool_settings = context.tool_settings
767         if gpd and gpl:
768             layout.prop(gpl, "thickness")
769         else:
770             layout.prop(tool_settings, "annotation_thickness", text="Thickness")
771
772         if gpl:
773             # Full-Row - Frame Locking (and Delete Frame)
774             row = layout.row(align=True)
775             row.active = not gpl.lock
776
777             if gpl.active_frame:
778                 lock_status = iface_("Locked") if gpl.lock_frame else iface_("Unlocked")
779                 lock_label = iface_("Frame: %d (%s)") % (gpl.active_frame.frame_number, lock_status)
780             else:
781                 lock_label = iface_("Lock Frame")
782             row.prop(gpl, "lock_frame", text=lock_label, icon='UNLOCKED')
783             row.operator("gpencil.active_frame_delete", text="", icon='X')
784
785
786 class AnnotationOnionSkin:
787     bl_label = "Onion Skin"
788     bl_region_type = 'UI'
789     bl_options = {'DEFAULT_CLOSED'}
790
791     @classmethod
792     def poll(cls, context):
793         # Show this panel as long as someone that might own this exists
794         # AND the owner isn't an object (e.g. GP Object)
795         if context.gpencil_data_owner is None:
796             return False
797         elif type(context.gpencil_data_owner) is bpy.types.Object:
798             return False
799         else:
800             gpl = context.active_gpencil_layer
801             if gpl is None:
802                 return False
803
804             return True
805
806     @staticmethod
807     def draw_header(self, context):
808         gpl = context.active_gpencil_layer
809         self.layout.prop(gpl, "use_annotation_onion_skinning", text="")
810
811     @staticmethod
812     def draw(self, context):
813         layout = self.layout
814         layout.use_property_decorate = False
815
816         gpl = context.active_gpencil_layer
817         col = layout.column()
818         split = col.split(factor=0.5)
819         split.active = gpl.use_annotation_onion_skinning
820
821         # - Before Frames
822         sub = split.column(align=True)
823         row = sub.row(align=True)
824         row.prop(gpl, "annotation_onion_before_color", text="")
825         sub.prop(gpl, "annotation_onion_before_range", text="Before")
826
827         # - After Frames
828         sub = split.column(align=True)
829         row = sub.row(align=True)
830         row.prop(gpl, "annotation_onion_after_color", text="")
831         sub.prop(gpl, "annotation_onion_after_range", text="After")
832
833
834 class GreasePencilOnionPanel:
835     @staticmethod
836     def draw_settings(layout, gp):
837         col = layout.column()
838         col.prop(gp, "onion_mode")
839         col.prop(gp, "onion_factor", text="Opacity", slider=True)
840
841         if gp.onion_mode == 'ABSOLUTE':
842             col = layout.column(align=True)
843             col.prop(gp, "ghost_before_range", text="Frames Before")
844             col.prop(gp, "ghost_after_range", text="Frames After")
845         if gp.onion_mode == 'RELATIVE':
846             col = layout.column(align=True)
847             col.prop(gp, "ghost_before_range", text="Keyframes Before")
848             col.prop(gp, "ghost_after_range", text="Keyframes After")
849
850         layout.prop(gp, "use_ghost_custom_colors", text="Use Custom Colors")
851
852         if gp.use_ghost_custom_colors:
853             col = layout.column(align=True)
854             col.active = gp.use_ghost_custom_colors
855             col.prop(gp, "before_color", text="Color Before")
856             col.prop(gp, "after_color", text="After")
857
858         layout.prop(gp, "use_ghosts_always", text="View In Render")
859
860         col = layout.column(align=True)
861         col.prop(gp, "use_onion_fade", text="Fade")
862         if hasattr(gp, "use_onion_loop"):  # XXX
863             sub = layout.column()
864             sub.active = gp.onion_mode in ('RELATIVE', 'SELECTED')
865             sub.prop(gp, "use_onion_loop", text="Loop")
866
867
868 class GreasePencilToolsPanel:
869     # For use in "2D" Editors without their own toolbar
870     # subclass must set
871     # bl_space_type = 'IMAGE_EDITOR'
872     bl_label = "Grease Pencil Settings"
873     bl_region_type = 'UI'
874     bl_options = {'DEFAULT_CLOSED'}
875
876     @classmethod
877     def poll(cls, context):
878         # XXX - disabled in 2.8 branch.
879         return False
880
881         return (context.gpencil_data is not None)
882
883     @staticmethod
884     def draw(self, context):
885         layout = self.layout
886
887         gpd = context.gpencil_data
888
889         layout.prop(gpd, "use_stroke_edit_mode", text="Enable Editing", icon='EDIT', toggle=True)
890
891         layout.separator()
892
893         layout.label(text="Proportional Edit:")
894         row = layout.row()
895         row.prop(context.tool_settings, "proportional_edit", text="")
896         row.prop(context.tool_settings, "proportional_edit_falloff", text="")
897
898         layout.separator()
899         layout.separator()
900
901         gpencil_active_brush_settings_simple(context, layout)
902
903         layout.separator()
904
905         gpencil_stroke_placement_settings(context, layout)
906
907
908 class GreasePencilMaterialsPanel:
909     # Mix-in, use for properties editor and top-bar.
910
911     @classmethod
912     def poll(cls, context):
913         ob = context.object
914         return ob and ob.type == 'GPENCIL'
915
916     @staticmethod
917     def draw(self, context):
918         layout = self.layout
919         show_full_ui = (self.bl_space_type == 'PROPERTIES')
920
921         gpd = context.gpencil_data
922
923         ob = context.object
924
925         is_sortable = len(ob.material_slots) > 1
926         rows = 7
927
928         row = layout.row()
929
930         row.template_list("GPENCIL_UL_matslots", "", ob, "material_slots", ob, "active_material_index", rows=rows)
931
932         col = row.column(align=True)
933         if show_full_ui:
934             col.operator("object.material_slot_add", icon='ADD', text="")
935             col.operator("object.material_slot_remove", icon='REMOVE', text="")
936
937         col.menu("GPENCIL_MT_color_specials", icon='DOWNARROW_HLT', text="")
938
939         if is_sortable:
940             col.separator()
941
942             col.operator("object.material_slot_move", icon='TRIA_UP', text="").direction = 'UP'
943             col.operator("object.material_slot_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
944
945             col.separator()
946
947             sub = col.column(align=True)
948             sub.operator("gpencil.color_isolate", icon='LOCKED', text="").affect_visibility = False
949             sub.operator("gpencil.color_isolate", icon='RESTRICT_VIEW_ON', text="").affect_visibility = True
950
951         if show_full_ui:
952             row = layout.row()
953
954             row.template_ID(ob, "active_material", new="material.new", live_icon=True)
955
956             slot = context.material_slot
957             if slot:
958                 icon_link = 'MESH_DATA' if slot.link == 'DATA' else 'OBJECT_DATA'
959                 row.prop(slot, "link", icon=icon_link, icon_only=True)
960
961             if gpd.use_stroke_edit_mode:
962                 row = layout.row(align=True)
963                 row.operator("gpencil.stroke_change_color", text="Assign")
964                 row.operator("gpencil.color_select", text="Select").deselect = False
965                 row.operator("gpencil.color_select", text="Deselect").deselect = True
966
967
968 class GPENCIL_UL_layer(UIList):
969     def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
970         # assert(isinstance(item, bpy.types.GPencilLayer)
971         gpl = item
972         gpd = context.gpencil_data
973
974         if self.layout_type in {'DEFAULT', 'COMPACT'}:
975             if gpl.lock:
976                 layout.active = False
977
978             row = layout.row(align=True)
979             row.label(
980                 text="",
981                 icon='BONE_DATA' if gpl.is_parented else 'BLANK1',
982             )
983             row.prop(gpl, "info", text="", emboss=False)
984
985             row = layout.row(align=True)
986             row.prop(gpl, "clamp_layer", text="",
987                      icon='MOD_MASK' if gpl.clamp_layer else 'LAYER_ACTIVE',
988                      emboss=False)
989
990             row.prop(gpl, "lock", text="", emboss=False)
991             row.prop(gpl, "hide", text="", emboss=False)
992             subrow = row.row(align=True)
993             subrow.prop(
994                 gpl,
995                 "use_onion_skinning",
996                 text="",
997                 icon='ONIONSKIN_ON' if gpl.use_onion_skinning else 'ONIONSKIN_OFF',
998                 emboss=False,
999             )
1000         elif self.layout_type == 'GRID':
1001             layout.alignment = 'CENTER'
1002             layout.label(
1003                 text="",
1004                 icon_value=icon,
1005             )
1006
1007
1008 classes = (
1009     GPENCIL_MT_pie_tool_palette,
1010     GPENCIL_MT_pie_settings_palette,
1011     GPENCIL_MT_pie_tools_more,
1012     GPENCIL_MT_pie_sculpt,
1013
1014     GPENCIL_MT_snap,
1015     GPENCIL_MT_separate,
1016     GPENCIL_MT_cleanup,
1017
1018     GPENCIL_MT_gpencil_draw_specials,
1019     GPENCIL_MT_gpencil_draw_delete,
1020
1021     GPENCIL_UL_annotation_layer,
1022     GPENCIL_UL_layer,
1023 )
1024
1025 if __name__ == "__main__":  # only for live edit.
1026     from bpy.utils import register_class
1027     for cls in classes:
1028         register_class(cls)