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