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