Fix UV path redo in vert/edge mode
[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, Operator
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 # XXX: To be replaced with active tools
48 class AnnotationDrawingToolsPanel:
49     # subclass must set
50     # bl_space_type = 'IMAGE_EDITOR'
51     bl_label = "Annotation"
52     bl_category = "Annotation"
53     bl_region_type = 'TOOLS'
54
55     def draw(self, context):
56         layout = self.layout
57
58         is_3d_view = context.space_data.type == 'VIEW_3D'
59         is_clip_editor = context.space_data.type == 'CLIP_EDITOR'
60
61         col = layout.column(align=True)
62
63         col.label(text="Draw:")
64         row = col.row(align=True)
65         row.operator("gpencil.annotate", icon='GREASEPENCIL', text="Draw").mode = 'DRAW'
66         # XXX: Needs a dedicated icon
67         row.operator("gpencil.annotate", icon='FORCE_CURVE', text="Erase").mode = 'ERASER'
68
69         row = col.row(align=True)
70         row.operator("gpencil.annotate", icon='LINE_DATA', text="Line").mode = 'DRAW_STRAIGHT'
71         row.operator("gpencil.annotate", icon='MESH_DATA', text="Poly").mode = 'DRAW_POLY'
72
73         col.separator()
74
75         sub = col.column(align=True)
76         sub.operator("gpencil.blank_frame_add", icon='FILE_NEW')
77         sub.operator("gpencil.active_frames_delete_all", icon='X', text="Delete Frame(s)")
78
79         col.separator()
80         col.separator()
81
82         if context.space_data.type == 'CLIP_EDITOR':
83             col.separator()
84             col.label(text="Data Source:")
85             row = col.row(align=True)
86             if is_3d_view:
87                 row.prop(context.tool_settings, "annotation_source", expand=True)
88             elif is_clip_editor:
89                 row.prop(context.space_data, "annotation_source", expand=True)
90
91         gpencil_stroke_placement_settings(context, col)
92
93
94 class GreasePencilSculptOptionsPanel:
95     bl_label = "Sculpt Strokes"
96
97     @classmethod
98     def poll(cls, context):
99         tool_settings = context.scene.tool_settings
100         settings = tool_settings.gpencil_sculpt_paint
101         brush = settings.brush
102         tool = brush.gpencil_sculpt_tool
103
104         return bool(tool in {'SMOOTH', 'RANDOMIZE'})
105
106     def draw(self, context):
107         layout = self.layout
108         layout.use_property_split = True
109         layout.use_property_decorate = False
110
111         tool_settings = context.scene.tool_settings
112         settings = tool_settings.gpencil_sculpt_paint
113         brush = settings.brush
114         gp_settings = brush.gpencil_settings
115         tool = brush.gpencil_sculpt_tool
116
117         if tool in {'SMOOTH', 'RANDOMIZE'}:
118             layout.prop(gp_settings, "use_edit_position", text="Affect Position")
119             layout.prop(gp_settings, "use_edit_strength", text="Affect Strength")
120             layout.prop(gp_settings, "use_edit_thickness", text="Affect Thickness")
121
122             if tool == 'SMOOTH':
123                 layout.prop(gp_settings, "use_edit_pressure")
124
125             layout.prop(gp_settings, "use_edit_uv", text="Affect UV")
126
127
128 # GP Object Tool Settings
129 class GreasePencilDisplayPanel:
130     bl_label = "Brush Tip"
131     bl_options = {'DEFAULT_CLOSED'}
132
133     @classmethod
134     def poll(cls, context):
135         ob = context.active_object
136         brush = context.tool_settings.gpencil_paint.brush
137         if ob and ob.type == 'GPENCIL' and brush:
138             if context.mode == 'PAINT_GPENCIL':
139                 return brush.gpencil_tool != 'ERASE'
140             else:
141                 # GP Sculpt, Vertex and Weight Paint always have Brush Tip panel.
142                 return True
143         return False
144
145     def draw_header(self, context):
146         if self.is_popover:
147             return
148
149         tool_settings = context.tool_settings
150         if context.mode == 'PAINT_GPENCIL':
151             settings = tool_settings.gpencil_paint
152         elif context.mode == 'SCULPT_GPENCIL':
153             settings = tool_settings.gpencil_sculpt_paint
154         elif context.mode == 'WEIGHT_GPENCIL':
155             settings = tool_settings.gpencil_weight_paint
156         elif context.mode == 'VERTEX_GPENCIL':
157             settings = tool_settings.gpencil_vertex_paint
158         brush = settings.brush
159         if brush:
160             self.layout.prop(settings, "show_brush", text="")
161
162     def draw(self, context):
163         layout = self.layout
164         layout.use_property_split = True
165         layout.use_property_decorate = False
166
167         tool_settings = context.tool_settings
168         if context.mode == 'PAINT_GPENCIL':
169             settings = tool_settings.gpencil_paint
170         elif context.mode == 'SCULPT_GPENCIL':
171             settings = tool_settings.gpencil_sculpt_paint
172         elif context.mode == 'WEIGHT_GPENCIL':
173             settings = tool_settings.gpencil_weight_paint
174         elif context.mode == 'VERTEX_GPENCIL':
175             settings = tool_settings.gpencil_vertex_paint
176         brush = settings.brush
177         gp_settings = brush.gpencil_settings
178
179         ob = context.active_object
180         if ob.mode == 'PAINT_GPENCIL':
181
182             if self.is_popover:
183                 row = layout.row(align=True)
184                 row.prop(settings, "show_brush", text="")
185                 row.label(text="Display Cursor")
186
187             col = layout.column(align=True)
188             col.active = settings.show_brush
189
190             if brush.gpencil_tool == 'DRAW':
191                 col.prop(gp_settings, "show_lasso", text="Show Fill Color While Drawing")
192
193         elif ob.mode == 'SCULPT_GPENCIL':
194             col = layout.column(align=True)
195             col.active = settings.show_brush
196
197             col.prop(brush, "cursor_color_add", text="Cursor Color")
198             if brush.gpencil_sculpt_tool in {'THICKNESS', 'STRENGTH', 'PINCH', 'TWIST'}:
199                 col.prop(brush, "cursor_color_subtract", text="Inverse Cursor Color")
200
201         elif ob.mode == 'WEIGHT_GPENCIL':
202             col = layout.column(align=True)
203             col.active = settings.show_brush
204
205             col.prop(brush, "cursor_color_add", text="Cursor Color")
206
207         elif ob.mode == 'VERTEX_GPENCIL':
208             row = layout.row(align=True)
209             row.prop(settings, "show_brush", text="")
210             row.label(text="Display Cursor")
211
212
213 class GreasePencilBrushFalloff:
214     bl_label = "Falloff"
215     bl_options = {'DEFAULT_CLOSED'}
216
217     @classmethod
218     def poll(cls, context):
219         ts = context.tool_settings
220         settings = None
221         if context.mode == 'PAINT_GPENCIL':
222             settings = ts.gpencil_paint
223         if context.mode == 'SCULPT_GPENCIL':
224             settings = ts.gpencil_sculpt_paint
225         elif context.mode == 'WEIGHT_GPENCIL':
226             settings = ts.gpencil_weight_paint
227         elif context.mode == 'VERTEX_GPENCIL':
228             settings = ts.gpencil_vertex_paint
229
230         return (settings and settings.brush and settings.brush.curve)
231
232     def draw(self, context):
233         layout = self.layout
234         ts = context.tool_settings
235         settings = None
236         if context.mode == 'PAINT_GPENCIL':
237             settings = ts.gpencil_paint
238         if context.mode == 'SCULPT_GPENCIL':
239             settings = ts.gpencil_sculpt_paint
240         elif context.mode == 'WEIGHT_GPENCIL':
241             settings = ts.gpencil_weight_paint
242         elif context.mode == 'VERTEX_GPENCIL':
243             settings = ts.gpencil_vertex_paint
244
245         if settings:
246             brush = settings.brush
247
248             col = layout.column(align=True)
249             row = col.row(align=True)
250             row.prop(brush, "curve_preset", text="")
251
252             if brush.curve_preset == 'CUSTOM':
253                 layout.template_curve_mapping(brush, "curve", brush=True)
254
255                 col = layout.column(align=True)
256                 row = col.row(align=True)
257                 row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH'
258                 row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND'
259                 row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT'
260                 row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP'
261                 row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE'
262                 row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX'
263
264
265 class GPENCIL_MT_snap(Menu):
266     bl_label = "Snap"
267
268     def draw(self, _context):
269         layout = self.layout
270
271         layout.operator("gpencil.snap_to_grid", text="Selection to Grid")
272         layout.operator("gpencil.snap_to_cursor", text="Selection to Cursor").use_offset = False
273         layout.operator("gpencil.snap_to_cursor", text="Selection to Cursor (Keep Offset)").use_offset = True
274
275         layout.separator()
276
277         layout.operator("gpencil.snap_cursor_to_selected", text="Cursor to Selected")
278         layout.operator("view3d.snap_cursor_to_center", text="Cursor to World Origin")
279         layout.operator("view3d.snap_cursor_to_grid", text="Cursor to Grid")
280
281
282 class GPENCIL_MT_move_to_layer(Menu):
283     bl_label = "Move to Layer"
284
285     def draw(self, context):
286         layout = self.layout
287         gpd = context.gpencil_data
288         if gpd:
289             gpl_active = context.active_gpencil_layer
290             tot_layers = len(gpd.layers)
291             i = tot_layers - 1
292             while i >= 0:
293                 gpl = gpd.layers[i]
294                 if gpl.info == gpl_active.info:
295                     icon = 'GREASEPENCIL'
296                 else:
297                     icon = 'NONE'
298                 layout.operator("gpencil.move_to_layer", text=gpl.info, icon=icon).layer = i
299                 i -= 1
300
301             layout.separator()
302
303         layout.operator("gpencil.move_to_layer", text="New Layer", icon='ADD').layer = -1
304
305
306 class GPENCIL_MT_layer_active(Menu):
307     bl_label = "Change Active Layer"
308
309     def draw(self, context):
310         layout = self.layout
311         layout.operator_context = 'INVOKE_REGION_WIN'
312
313         gpd = context.gpencil_data
314         if gpd:
315             gpl_active = context.active_gpencil_layer
316             tot_layers = len(gpd.layers)
317             i = tot_layers - 1
318             while i >= 0:
319                 gpl = gpd.layers[i]
320                 if gpl.info == gpl_active.info:
321                     icon = 'GREASEPENCIL'
322                 else:
323                     icon = 'NONE'
324                 layout.operator("gpencil.layer_active", text=gpl.info, icon=icon).layer = i
325                 i -= 1
326
327             layout.separator()
328
329         layout.operator("gpencil.layer_add", text="New Layer", icon='ADD')
330
331
332 class GPENCIL_MT_material_active(Menu):
333     bl_label = "Change Active Material"
334
335     @classmethod
336     def poll(cls, context):
337         ob = context.active_object
338         tool_settings = context.scene.tool_settings
339         mode = tool_settings.gpencil_paint.color_mode
340         if mode != 'MATERIAL':
341             return False
342
343         if ob is None or len(ob.material_slots) == 0:
344             return False
345
346         return True
347
348     def draw(self, context):
349         layout = self.layout
350         layout.operator_context = 'INVOKE_REGION_WIN'
351         ob = context.active_object
352         mat_active = ob.active_material
353
354         for slot in ob.material_slots:
355             mat = slot.material
356             if mat:
357                 icon = mat.id_data.preview.icon_id
358                 layout.operator("gpencil.material_set", text=mat.name, icon_value=icon).slot = mat.name
359
360
361 class GPENCIL_MT_gpencil_draw_delete(Menu):
362     bl_label = "Delete"
363
364     def draw(self, _context):
365         layout = self.layout
366
367         layout.operator_context = 'INVOKE_REGION_WIN'
368
369         layout.operator("gpencil.delete", text="Delete Active Keyframe (Active Layer)").type = 'FRAME'
370         layout.operator("gpencil.active_frames_delete_all", text="Delete Active Keyframes (All Layers)")
371
372
373 class GPENCIL_MT_cleanup(Menu):
374     bl_label = "Clean Up"
375
376     def draw(self, context):
377
378         ob = context.active_object
379
380         layout = self.layout
381
382         layout.operator("gpencil.frame_clean_loose", text="Delete Loose Points")
383
384         if ob.mode != 'PAINT_GPENCIL':
385             layout.operator("gpencil.stroke_merge_by_distance", text="Merge by Distance")
386
387         layout.separator()
388
389         layout.operator("gpencil.frame_clean_fill", text="Boundary Strokes").mode = 'ACTIVE'
390         layout.operator("gpencil.frame_clean_fill", text="Boundary Strokes all Frames").mode = 'ALL'
391
392         if ob.mode != 'PAINT_GPENCIL':
393             layout.separator()
394
395             layout.operator("gpencil.reproject")
396
397
398 class GPENCIL_UL_annotation_layer(UIList):
399     def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
400         # assert(isinstance(item, bpy.types.GPencilLayer)
401         gpl = item
402
403         if self.layout_type in {'DEFAULT', 'COMPACT'}:
404             if gpl.lock:
405                 layout.active = False
406
407             split = layout.split(factor=0.2)
408             split.prop(gpl, "color", text="", emboss=True)
409             split.prop(gpl, "info", text="", emboss=False)
410
411             row = layout.row(align=True)
412             row.prop(gpl, "annotation_hide", text="", emboss=False)
413         elif self.layout_type == 'GRID':
414             layout.alignment = 'CENTER'
415             layout.label(text="", icon_value=icon)
416
417
418 class AnnotationDataPanel:
419     bl_label = "Annotations"
420     bl_region_type = 'UI'
421     bl_options = {'DEFAULT_CLOSED'}
422
423     @classmethod
424     def poll(cls, context):
425         # Show this panel as long as someone that might own this exists
426         # AND the owner isn't an object (e.g. GP Object)
427         if context.annotation_data_owner is None:
428             return False
429         elif type(context.annotation_data_owner) is bpy.types.Object:
430             return False
431         else:
432             return True
433
434     def draw_header(self, context):
435         if context.space_data.type not in {'VIEW_3D', 'TOPBAR'}:
436             self.layout.prop(context.space_data, "show_annotation", text="")
437
438     def draw(self, context):
439         layout = self.layout
440         layout.use_property_decorate = False
441
442         # Grease Pencil owner.
443         gpd_owner = context.annotation_data_owner
444         gpd = context.annotation_data
445
446         # Owner selector.
447         if context.space_data.type == 'CLIP_EDITOR':
448             layout.row().prop(context.space_data, "annotation_source", expand=True)
449
450         layout.template_ID(gpd_owner, "grease_pencil", new="gpencil.annotation_add", unlink="gpencil.data_unlink")
451
452         # List of layers/notes.
453         if gpd and gpd.layers:
454             self.draw_layers(context, layout, gpd)
455
456     def draw_layers(self, context, layout, gpd):
457         row = layout.row()
458
459         col = row.column()
460         if len(gpd.layers) >= 2:
461             layer_rows = 5
462         else:
463             layer_rows = 3
464         col.template_list("GPENCIL_UL_annotation_layer", "", gpd, "layers", gpd.layers, "active_index",
465                           rows=layer_rows, sort_reverse=True, sort_lock=True)
466
467         col = row.column()
468
469         sub = col.column(align=True)
470         sub.operator("gpencil.layer_annotation_add", icon='ADD', text="")
471         sub.operator("gpencil.layer_annotation_remove", icon='REMOVE', text="")
472
473         gpl = context.active_annotation_layer
474         if gpl:
475             if len(gpd.layers) > 1:
476                 col.separator()
477
478                 sub = col.column(align=True)
479                 sub.operator("gpencil.layer_annotation_move", icon='TRIA_UP', text="").type = 'UP'
480                 sub.operator("gpencil.layer_annotation_move", icon='TRIA_DOWN', text="").type = 'DOWN'
481
482         tool_settings = context.tool_settings
483         if gpd and gpl:
484             layout.prop(gpl, "thickness")
485         else:
486             layout.prop(tool_settings, "annotation_thickness", text="Thickness")
487
488         if gpl:
489             # Full-Row - Frame Locking (and Delete Frame)
490             row = layout.row(align=True)
491             row.active = not gpl.lock
492
493             if gpl.active_frame:
494                 lock_status = iface_("Locked") if gpl.lock_frame else iface_("Unlocked")
495                 lock_label = iface_("Frame: %d (%s)") % (gpl.active_frame.frame_number, lock_status)
496             else:
497                 lock_label = iface_("Lock Frame")
498             row.prop(gpl, "lock_frame", text=lock_label, icon='UNLOCKED')
499             row.operator("gpencil.annotation_active_frame_delete", text="", icon='X')
500
501
502 class AnnotationOnionSkin:
503     bl_label = "Onion Skin"
504     bl_region_type = 'UI'
505     bl_options = {'DEFAULT_CLOSED'}
506
507     @classmethod
508     def poll(cls, context):
509         # Show this panel as long as someone that might own this exists
510         # AND the owner isn't an object (e.g. GP Object)
511         if context.annotation_data_owner is None:
512             return False
513         elif type(context.annotation_data_owner) is bpy.types.Object:
514             return False
515         else:
516             gpl = context.active_annotation_layer
517             if gpl is None:
518                 return False
519
520         return True
521
522     def draw_header(self, context):
523         gpl = context.active_annotation_layer
524         self.layout.prop(gpl, "use_annotation_onion_skinning", text="")
525
526     def draw(self, context):
527         layout = self.layout
528         layout.use_property_decorate = False
529
530         gpl = context.active_annotation_layer
531         col = layout.column()
532         split = col.split(factor=0.5)
533         split.active = gpl.use_annotation_onion_skinning
534
535         # - Before Frames
536         sub = split.column(align=True)
537         row = sub.row(align=True)
538         row.prop(gpl, "annotation_onion_before_color", text="")
539         sub.prop(gpl, "annotation_onion_before_range", text="Before")
540
541         # - After Frames
542         sub = split.column(align=True)
543         row = sub.row(align=True)
544         row.prop(gpl, "annotation_onion_after_color", text="")
545         sub.prop(gpl, "annotation_onion_after_range", text="After")
546
547
548 class GreasePencilMaterialsPanel:
549     # Mix-in, use for properties editor and top-bar.
550     def draw(self, context):
551         layout = self.layout
552         show_full_ui = (self.bl_space_type == 'PROPERTIES')
553
554         is_view3d = (self.bl_space_type == 'VIEW_3D')
555         tool_settings = context.scene.tool_settings
556         gpencil_paint = tool_settings.gpencil_paint
557         brush = gpencil_paint.brush
558
559         ob = context.object
560         row = layout.row()
561
562         if ob:
563             is_sortable = len(ob.material_slots) > 1
564             rows = 7
565
566             row.template_list("GPENCIL_UL_matslots", "", ob, "material_slots", ob, "active_material_index", rows=rows)
567
568             # if topbar popover and brush pinned, disable
569             if is_view3d and brush is not None:
570                 gp_settings = brush.gpencil_settings
571                 if gp_settings.use_material_pin:
572                     row.enabled = False
573
574             col = row.column(align=True)
575             if show_full_ui:
576                 col.operator("object.material_slot_add", icon='ADD', text="")
577                 col.operator("object.material_slot_remove", icon='REMOVE', text="")
578
579             col.separator()
580
581             col.menu("GPENCIL_MT_material_context_menu", icon='DOWNARROW_HLT', text="")
582
583             if is_sortable:
584                 col.separator()
585
586                 col.operator("object.material_slot_move", icon='TRIA_UP', text="").direction = 'UP'
587                 col.operator("object.material_slot_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
588
589                 col.separator()
590
591                 sub = col.column(align=True)
592                 sub.operator("gpencil.material_isolate", icon='RESTRICT_VIEW_ON', text="").affect_visibility = True
593                 sub.operator("gpencil.material_isolate", icon='LOCKED', text="").affect_visibility = False
594
595             if show_full_ui:
596                 row = layout.row()
597
598                 row.template_ID(ob, "active_material", new="material.new", live_icon=True)
599
600                 slot = context.material_slot
601                 if slot:
602                     icon_link = 'MESH_DATA' if slot.link == 'DATA' else 'OBJECT_DATA'
603                     row.prop(slot, "link", icon=icon_link, icon_only=True)
604
605                 if ob.data.use_stroke_edit_mode:
606                     row = layout.row(align=True)
607                     row.operator("gpencil.stroke_change_color", text="Assign")
608                     row.operator("gpencil.material_select", text="Select").deselect = False
609                     row.operator("gpencil.material_select", text="Deselect").deselect = True
610         # stroke color
611             ma = None
612             if is_view3d and brush is not None:
613                 gp_settings = brush.gpencil_settings
614                 if gp_settings.use_material_pin is False:
615                     if len(ob.material_slots) > 0 and ob.active_material_index >= 0:
616                         ma = ob.material_slots[ob.active_material_index].material
617                 else:
618                     ma = gp_settings.material
619
620             if ma is not None and ma.grease_pencil is not None:
621                 gpcolor = ma.grease_pencil
622                 if gpcolor.stroke_style == 'SOLID':
623                     row = layout.row()
624                     row.prop(gpcolor, "color", text="Stroke Color")
625
626         else:
627             space = context.space_data
628             row.template_ID(space, "pin_id")
629
630
631 class GreasePencilVertexcolorPanel:
632
633     def draw(self, context):
634         layout = self.layout
635         layout.use_property_split = True
636         layout.use_property_decorate = False
637
638         ts = context.scene.tool_settings
639         is_vertex = context.mode == 'VERTEX_GPENCIL'
640         gpencil_paint = ts.gpencil_vertex_paint if is_vertex else ts.gpencil_paint
641         brush = gpencil_paint.brush
642         gp_settings = brush.gpencil_settings
643         tool = brush.gpencil_vertex_tool if is_vertex else brush.gpencil_tool
644
645         ob = context.object
646
647         if ob:
648             col = layout.column()
649             col.template_color_picker(brush, "color", value_slider=True)
650
651             sub_row = layout.row(align=True)
652             sub_row.prop(brush, "color", text="")
653             sub_row.prop(brush, "secondary_color", text="")
654
655             sub_row.operator("gpencil.tint_flip", icon='FILE_REFRESH', text="")
656
657             row = layout.row(align=True)
658             row.template_ID(gpencil_paint, "palette", new="palette.new")
659             if gpencil_paint.palette:
660                 layout.template_palette(gpencil_paint, "palette", color=True)
661
662             if tool in {'DRAW', 'FILL'} and is_vertex is False:
663                 row = layout.row(align=True)
664                 row.prop(gp_settings, "vertex_mode", text="Mode")
665                 row = layout.row(align=True)
666                 row.prop(gp_settings, "vertex_color_factor", slider=True, text="Mix Factor")
667
668
669 class GPENCIL_UL_layer(UIList):
670     def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
671         # assert(isinstance(item, bpy.types.GPencilLayer)
672         gpl = item
673
674         if self.layout_type in {'DEFAULT', 'COMPACT'}:
675             if gpl.lock:
676                 layout.active = False
677
678             row = layout.row(align=True)
679             row.label(
680                 text="",
681                 icon='BONE_DATA' if gpl.is_parented else 'BLANK1',
682             )
683             row.prop(gpl, "info", text="", emboss=False)
684
685             row = layout.row(align=True)
686
687             icon_mask = 'MOD_MASK' if gpl.use_mask_layer else 'LAYER_ACTIVE'
688
689             row.prop(gpl, "use_mask_layer", text="", icon=icon_mask, emboss=False)
690
691             subrow = row.row(align=True)
692             subrow.prop(
693                 gpl,
694                 "use_onion_skinning",
695                 text="",
696                 icon='ONIONSKIN_ON' if gpl.use_onion_skinning else 'ONIONSKIN_OFF',
697                 emboss=False,
698             )
699             row.prop(gpl, "hide", text="", emboss=False)
700             row.prop(gpl, "lock", text="", emboss=False)
701         elif self.layout_type == 'GRID':
702             layout.alignment = 'CENTER'
703             layout.label(
704                 text="",
705                 icon_value=icon,
706             )
707
708
709 class GreasePencilSimplifyPanel:
710
711     def draw_header(self, context):
712         rd = context.scene.render
713         self.layout.prop(rd, "simplify_gpencil", text="")
714
715     def draw(self, context):
716         layout = self.layout
717         layout.use_property_split = True
718         layout.use_property_decorate = False
719
720         rd = context.scene.render
721
722         layout.active = rd.simplify_gpencil
723
724         col = layout.column()
725         col.prop(rd, "simplify_gpencil_onplay")
726         col.prop(rd, "simplify_gpencil_view_fill")
727         col.prop(rd, "simplify_gpencil_modifier")
728         col.prop(rd, "simplify_gpencil_shader_fx")
729         col.prop(rd, "simplify_gpencil_tint")
730         col.prop(rd, "simplify_gpencil_antialiasing")
731
732
733 class GreasePencilLayerAdjustmentsPanel:
734
735     def draw(self, context):
736         layout = self.layout
737         layout.use_property_split = True
738         scene = context.scene
739
740         ob = context.object
741         gpd = ob.data
742         gpl = gpd.layers.active
743         layout.active = not gpl.lock
744
745         # Layer options
746         # Offsets - Color Tint
747         layout.enabled = not gpl.lock
748         col = layout.column(align=True)
749         col.prop(gpl, "tint_color")
750         col.prop(gpl, "tint_factor", text="Factor", slider=True)
751
752         # Offsets - Thickness
753         col = layout.row(align=True)
754         col.prop(gpl, "line_change", text="Stroke Thickness")
755
756         col = layout.row(align=True)
757         col.prop(gpl, "pass_index")
758
759         col = layout.row(align=True)
760         col.prop_search(gpl, "viewlayer_render", scene, "view_layers", text="View Layer")
761
762         col = layout.row(align=True)
763         col.prop(gpl, "lock_material")
764
765
766 class GPENCIL_UL_masks(UIList):
767     def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
768         mask = item
769         if self.layout_type in {'DEFAULT', 'COMPACT'}:
770             row = layout.row(align=True)
771             row.prop(mask, "name", text="", emboss=False, icon_value=icon)
772             row.prop(mask, "invert", text="", emboss=False)
773             row.prop(mask, "hide", text="", emboss=False)
774         elif self.layout_type == 'GRID':
775             layout.alignment = 'CENTER'
776             layout.prop(mask, "name", text="", emboss=False, icon_value=icon)
777
778
779 class GPENCIL_MT_layer_mask_menu(Menu):
780     bl_label = "Layer Specials"
781
782     def draw(self, context):
783         layout = self.layout
784         ob = context.object
785         gpd = ob.data
786         gpl_active = gpd.layers.active
787         done = False
788         for gpl in gpd.layers:
789             if gpl != gpl_active and gpl.info not in gpl_active.mask_layers:
790                 done = True
791                 layout.operator("gpencil.layer_mask_add", text=gpl.info).name=gpl.info
792
793         if done is False:
794             layout.label(text="No layers to add")
795
796
797 class GreasePencilLayerMasksPanel:
798     def draw_header(self, context):
799         ob = context.active_object
800         gpd = ob.data
801         gpl = gpd.layers.active
802
803         self.layout.prop(gpl, "use_mask_layer", text="")
804
805     def draw(self, context):
806         ob = context.active_object
807         gpd = ob.data
808         gpl = gpd.layers.active
809
810         layout = self.layout
811         layout.enabled = gpl.use_mask_layer
812
813         if gpl:
814             rows = 4
815             row = layout.row()
816             col = row.column()
817             col.template_list("GPENCIL_UL_masks", "", gpl, "mask_layers", gpl.mask_layers,
818                             "active_mask_index", rows=rows, sort_lock=True)
819
820             col2 = row.column(align=True)
821             col2.menu("GPENCIL_MT_layer_mask_menu", icon='ADD', text="")
822             col2.operator("gpencil.layer_mask_remove", icon='REMOVE', text="")
823
824
825 class GreasePencilLayerRelationsPanel:
826
827     def draw(self, context):
828         layout = self.layout
829         layout.use_property_split = True
830         layout.use_property_decorate = False
831
832         ob = context.object
833         gpd = ob.data
834         gpl = gpd.layers.active
835
836         col = layout.column()
837         col.active = not gpl.lock
838         col.prop(gpl, "parent")
839         col.prop(gpl, "parent_type", text="Type")
840         parent = gpl.parent
841
842         if parent and gpl.parent_type == 'BONE' and parent.type == 'ARMATURE':
843             col.prop_search(gpl, "parent_bone", parent.data, "bones", text="Bone")
844
845
846 class GreasePencilLayerDisplayPanel:
847
848     def draw(self, context):
849         layout = self.layout
850         layout.use_property_split = True
851         layout.use_property_decorate = False
852
853         ob = context.object
854         gpd = ob.data
855         gpl = gpd.layers.active
856
857         col = layout.row(align=True)
858         col.prop(gpl, "channel_color")
859
860         col = layout.row(align=True)
861         col.prop(gpl, "use_solo_mode", text="Show Only On Keyframed")
862
863
864 class GreasePencilFlipTintColors(Operator):
865     bl_label = "Flip Colors"
866     bl_idname = "gpencil.tint_flip"
867     bl_description = "Switch Tint colors"
868
869     def execute(self, context):
870         try:
871             ts = context.tool_settings
872             settings = None
873             if context.mode == 'PAINT_GPENCIL':
874                 settings = ts.gpencil_paint
875             if context.mode == 'SCULPT_GPENCIL':
876                 settings = ts.gpencil_sculpt_paint
877             elif context.mode == 'WEIGHT_GPENCIL':
878                 settings = ts.gpencil_weight_paint
879             elif context.mode == 'VERTEX_GPENCIL':
880                 settings = ts.gpencil_vertex_paint
881
882             brush = settings.brush
883             if brush is not None:
884                 color = brush.color
885                 secondary_color = brush.secondary_color
886
887                 orig_prim = color.hsv
888                 orig_sec = secondary_color.hsv
889
890                 color.hsv = orig_sec
891                 secondary_color.hsv = orig_prim
892
893             return {'FINISHED'}
894
895         except Exception as e:
896             utils_core.error_handlers(self, "gpencil.tint_flip", e,
897                                       "Flip Colors could not be completed")
898
899             return {'CANCELLED'}
900
901
902 classes = (
903     GPENCIL_MT_snap,
904     GPENCIL_MT_cleanup,
905     GPENCIL_MT_move_to_layer,
906     GPENCIL_MT_layer_active,
907     GPENCIL_MT_material_active,
908
909     GPENCIL_MT_gpencil_draw_delete,
910     GPENCIL_MT_layer_mask_menu,
911
912     GPENCIL_UL_annotation_layer,
913     GPENCIL_UL_layer,
914     GPENCIL_UL_masks,
915
916     GreasePencilFlipTintColors,
917 )
918
919 if __name__ == "__main__":  # only for live edit.
920     from bpy.utils import register_class
921     for cls in classes:
922         register_class(cls)