GPencil: Layers and Materials Specials menu updates
[blender.git] / release / scripts / startup / bl_ui / properties_data_gpencil.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 import bpy
21 from bpy.types import Menu, Panel, UIList
22 from rna_prop_ui import PropertyPanel
23
24 ###############################
25 # Base-Classes (for shared stuff - e.g. poll, attributes, etc.)
26
27
28 class DataButtonsPanel:
29     bl_space_type = 'PROPERTIES'
30     bl_region_type = 'WINDOW'
31     bl_context = "data"
32
33     @classmethod
34     def poll(cls, context):
35         return context.gpencil
36
37
38 class ObjectButtonsPanel:
39     bl_space_type = 'PROPERTIES'
40     bl_region_type = 'WINDOW'
41     bl_context = "data"
42
43     @classmethod
44     def poll(cls, context):
45         ob = context.object
46         return ob and ob.type == 'GPENCIL'
47
48
49 class LayerDataButtonsPanel:
50     bl_space_type = 'PROPERTIES'
51     bl_region_type = 'WINDOW'
52     bl_context = "data"
53
54     @classmethod
55     def poll(cls, context):
56         gpencil = context.gpencil
57         return gpencil and gpencil.layers.active
58
59
60 ###############################
61 # GP Object Properties Panels and Helper Classes
62
63 class DATA_PT_context_gpencil(DataButtonsPanel, Panel):
64     bl_label = ""
65     bl_options = {'HIDE_HEADER'}
66
67     def draw(self, context):
68         layout = self.layout
69
70         ob = context.object
71         space = context.space_data
72
73         if ob:
74             layout.template_ID(ob, "data")
75         else:
76             layout.template_ID(space, "pin_id")
77
78
79 class GPENCIL_MT_layer_context_menu(Menu):
80     bl_label = "Layer Specials"
81
82     def draw(self, context):
83         layout = self.layout
84         ob = context.object
85         gpd = ob.data
86
87         layout.operator("gpencil.layer_duplicate", icon='DUPLICATE')
88
89         layout.separator()
90
91         layout.operator("gpencil.reveal", icon='RESTRICT_VIEW_OFF', text="Show All")
92         layout.operator("gpencil.hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True
93
94         layout.separator()
95
96         layout.operator("gpencil.lock_all", icon='LOCKED', text="Lock All")
97         layout.operator("gpencil.unlock_all", icon='UNLOCKED', text="UnLock All")
98         layout.prop(gpd, "use_autolock_layers", text="Autolock Inactive Layers")
99
100         layout.separator()
101
102         layout.operator("gpencil.layer_merge", icon='SORT_ASC', text="Merge Down")
103
104         layout.separator()
105         layout.menu("VIEW3D_MT_gpencil_copy_layer")
106
107
108 class DATA_PT_gpencil_layers(DataButtonsPanel, Panel):
109     bl_label = "Layers"
110
111     def draw(self, context):
112         layout = self.layout
113         #layout.use_property_split = True
114         layout.use_property_decorate = False
115
116         gpd = context.gpencil
117
118         # Grease Pencil data...
119         if (gpd is None) or (not gpd.layers):
120             layout.operator("gpencil.layer_add", text="New Layer")
121         else:
122             self.draw_layers(context, layout, gpd)
123
124     def draw_layers(self, _context, layout, gpd):
125
126         gpl = gpd.layers.active
127
128         row = layout.row()
129         layer_rows = 7
130
131         col = row.column()
132         col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index",
133                           rows=layer_rows, sort_reverse=True, sort_lock=True)
134
135         col = row.column()
136         sub = col.column(align=True)
137         sub.operator("gpencil.layer_add", icon='ADD', text="")
138         sub.operator("gpencil.layer_remove", icon='REMOVE', text="")
139
140         sub.separator()
141
142         if gpl:
143             sub.menu("GPENCIL_MT_layer_context_menu", icon='DOWNARROW_HLT', text="")
144
145             if len(gpd.layers) > 1:
146                 col.separator()
147
148                 sub = col.column(align=True)
149                 sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP'
150                 sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN'
151
152                 col.separator()
153
154                 sub = col.column(align=True)
155                 sub.operator("gpencil.layer_isolate", icon='LOCKED', text="").affect_visibility = False
156                 sub.operator("gpencil.layer_isolate", icon='RESTRICT_VIEW_ON', text="").affect_visibility = True
157
158         # Layer main properties
159         row = layout.row()
160         col = layout.column(align=True)
161
162         if gpl:
163
164             layout = self.layout
165             layout.use_property_split = True
166             layout.use_property_decorate = True
167             col = layout.column(align=True)
168
169             col = layout.row(align=True)
170             col.prop(gpl, "blend_mode", text="Blend")
171
172             col = layout.row(align=True)
173             col.prop(gpl, "opacity", text="Opacity", slider=True)
174
175
176 class DATA_PT_gpencil_layer_adjustments(LayerDataButtonsPanel, Panel):
177     bl_label = "Adjustments"
178     bl_parent_id = 'DATA_PT_gpencil_layers'
179     bl_options = {'DEFAULT_CLOSED'}
180
181     def draw(self, context):
182         layout = self.layout
183         layout.use_property_split = True
184         scene = context.scene
185
186         gpd = context.gpencil
187         gpl = gpd.layers.active
188         layout.active = not gpl.lock
189
190         # Layer options
191         # Offsets - Color Tint
192         layout.enabled = not gpl.lock
193         col = layout.column(align=True)
194         col.prop(gpl, "tint_color")
195         col.prop(gpl, "tint_factor", text="Factor", slider=True)
196
197         # Offsets - Thickness
198         col = layout.row(align=True)
199         col.prop(gpl, "line_change", text="Stroke Thickness")
200
201         col = layout.row(align=True)
202         col.prop(gpl, "pass_index")
203
204         col = layout.row(align=True)
205         col.prop_search(gpl, "viewlayer_render", scene, "view_layers", text="View Layer")
206
207         col = layout.row(align=True)
208         col.prop(gpl, "lock_material")
209
210
211 class DATA_PT_gpencil_layer_relations(LayerDataButtonsPanel, Panel):
212     bl_label = "Relations"
213     bl_parent_id = 'DATA_PT_gpencil_layers'
214     bl_options = {'DEFAULT_CLOSED'}
215
216     def draw(self, context):
217         layout = self.layout
218         layout.use_property_split = True
219         layout.use_property_decorate = False
220
221         gpd = context.gpencil
222         gpl = gpd.layers.active
223
224         col = layout.column()
225         col.active = not gpl.lock
226         col.prop(gpl, "parent")
227         col.prop(gpl, "parent_type", text="Type")
228         parent = gpl.parent
229
230         if parent and gpl.parent_type == 'BONE' and parent.type == 'ARMATURE':
231             col.prop_search(gpl, "parent_bone", parent.data, "bones", text="Bone")
232
233
234 class DATA_PT_gpencil_layer_display(LayerDataButtonsPanel, Panel):
235     bl_label = "Display"
236     bl_parent_id = 'DATA_PT_gpencil_layers'
237     bl_options = {'DEFAULT_CLOSED'}
238
239     def draw(self, context):
240         layout = self.layout
241         layout.use_property_split = True
242         layout.use_property_decorate = False
243
244         gpd = context.gpencil
245         gpl = gpd.layers.active
246
247         col = layout.row(align=True)
248         col.prop(gpl, "channel_color")
249
250         col = layout.row(align=True)
251         col.prop(gpl, "use_solo_mode", text="Show Only On Keyframed")
252
253
254 class DATA_PT_gpencil_onion_skinning(DataButtonsPanel, Panel):
255     bl_label = "Onion Skinning"
256
257     def draw(self, context):
258         gpd = context.gpencil
259
260         layout = self.layout
261         layout.use_property_split = True
262         layout.enabled = gpd.users <= 1
263
264         if gpd.users > 1:
265             layout.label(text="Multiuser datablock not supported", icon='ERROR')
266
267         col = layout.column()
268         col.prop(gpd, "onion_mode")
269         col.prop(gpd, "onion_factor", text="Opacity", slider=True)
270         col.prop(gpd, "onion_keyframe_type")
271
272         if gpd.onion_mode == 'ABSOLUTE':
273             col = layout.column(align=True)
274             col.prop(gpd, "ghost_before_range", text="Frames Before")
275             col.prop(gpd, "ghost_after_range", text="Frames After")
276         elif gpd.onion_mode == 'RELATIVE':
277             col = layout.column(align=True)
278             col.prop(gpd, "ghost_before_range", text="Keyframes Before")
279             col.prop(gpd, "ghost_after_range", text="Keyframes After")
280
281
282 class DATA_PT_gpencil_onion_skinning_custom_colors(DataButtonsPanel, Panel):
283     bl_parent_id = "DATA_PT_gpencil_onion_skinning"
284     bl_label = "Custom Colors"
285     bl_options = {'DEFAULT_CLOSED'}
286
287     def draw_header(self, context):
288         gpd = context.gpencil
289
290         self.layout.prop(gpd, "use_ghost_custom_colors", text="")
291
292     def draw(self, context):
293         gpd = context.gpencil
294
295         layout = self.layout
296         layout.use_property_split = True
297         layout.enabled = gpd.users <= 1 and gpd.use_ghost_custom_colors
298
299         layout.prop(gpd, "before_color", text="Before")
300         layout.prop(gpd, "after_color", text="After")
301
302
303 class DATA_PT_gpencil_onion_skinning_display(DataButtonsPanel, Panel):
304     bl_parent_id = "DATA_PT_gpencil_onion_skinning"
305     bl_label = "Display"
306     bl_options = {'DEFAULT_CLOSED'}
307
308     def draw(self, context):
309         gpd = context.gpencil
310
311         layout = self.layout
312         layout.use_property_split = True
313         layout.enabled = gpd.users <= 1
314
315         layout.prop(gpd, "use_ghosts_always", text="View In Render")
316
317         col = layout.column(align=True)
318         col.prop(gpd, "use_onion_fade", text="Fade")
319         sub = layout.column()
320         sub.active = gpd.onion_mode in {'RELATIVE', 'SELECTED'}
321         sub.prop(gpd, "use_onion_loop", text="Loop")
322
323
324 class GPENCIL_MT_gpencil_vertex_group(Menu):
325     bl_label = "GP Vertex Groups"
326
327     def draw(self, context):
328         layout = self.layout
329
330         layout.operator_context = 'EXEC_AREA'
331         layout.operator("object.vertex_group_add")
332
333         ob = context.active_object
334         if ob.vertex_groups.active:
335             layout.separator()
336
337             layout.operator("gpencil.vertex_group_assign", text="Assign to Active Group")
338             layout.operator("gpencil.vertex_group_remove_from", text="Remove from Active Group")
339
340             layout.separator()
341             layout.operator_menu_enum("object.vertex_group_set_active", "group", text="Set Active Group")
342             layout.operator("object.vertex_group_remove", text="Remove Active Group").all = False
343             layout.operator("object.vertex_group_remove", text="Remove All Groups").all = True
344
345             layout.separator()
346             layout.operator("gpencil.vertex_group_select", text="Select Points")
347             layout.operator("gpencil.vertex_group_deselect", text="Deselect Points")
348
349
350 class GPENCIL_UL_vgroups(UIList):
351     def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
352         vgroup = item
353         if self.layout_type in {'DEFAULT', 'COMPACT'}:
354             layout.prop(vgroup, "name", text="", emboss=False, icon_value=icon)
355             icon = 'LOCKED' if vgroup.lock_weight else 'UNLOCKED'
356             layout.prop(vgroup, "lock_weight", text="", icon=icon, emboss=False)
357         elif self.layout_type == 'GRID':
358             layout.alignment = 'CENTER'
359             layout.label(text="", icon_value=icon)
360
361
362 class DATA_PT_gpencil_vertex_groups(ObjectButtonsPanel, Panel):
363     bl_label = "Vertex Groups"
364     bl_options = {'DEFAULT_CLOSED'}
365
366     def draw(self, context):
367         layout = self.layout
368
369         ob = context.object
370         group = ob.vertex_groups.active
371
372         rows = 2
373         if group:
374             rows = 4
375
376         row = layout.row()
377         row.template_list("GPENCIL_UL_vgroups", "", ob, "vertex_groups", ob.vertex_groups, "active_index", rows=rows)
378
379         col = row.column(align=True)
380         col.operator("object.vertex_group_add", icon='ADD', text="")
381         col.operator("object.vertex_group_remove", icon='REMOVE', text="").all = False
382
383         if ob.vertex_groups:
384             row = layout.row()
385
386             sub = row.row(align=True)
387             sub.operator("gpencil.vertex_group_assign", text="Assign")
388             sub.operator("gpencil.vertex_group_remove_from", text="Remove")
389
390             sub = row.row(align=True)
391             sub.operator("gpencil.vertex_group_select", text="Select")
392             sub.operator("gpencil.vertex_group_deselect", text="Deselect")
393
394             layout.prop(context.tool_settings, "vertex_group_weight", text="Weight")
395
396
397 class DATA_PT_gpencil_strokes(DataButtonsPanel, Panel):
398     bl_label = "Strokes"
399     bl_options = {'DEFAULT_CLOSED'}
400
401     def draw(self, context):
402         layout = self.layout
403         layout.use_property_split = True
404         layout.use_property_decorate = False
405
406         ob = context.object
407         gpd = context.gpencil
408
409         col = layout.column(align=True)
410         col.prop(gpd, "stroke_depth_order")
411
412         if ob:
413             col.enabled = not ob.show_in_front
414
415         col = layout.column(align=True)
416         col.prop(gpd, "stroke_thickness_space")
417         sub = col.column()
418         sub.active = gpd.stroke_thickness_space == 'WORLDSPACE'
419         sub.prop(gpd, "pixel_factor", text="Thickness Scale")
420
421         layout.prop(gpd, "use_force_fill_recalc", text="Force Fill Update")
422         layout.prop(gpd, "use_adaptive_uv", text="Adaptive UVs")
423
424
425 class DATA_PT_gpencil_display(DataButtonsPanel, Panel):
426     bl_label = "Viewport Display"
427     bl_options = {'DEFAULT_CLOSED'}
428
429     def draw(self, context):
430         layout = self.layout
431         layout.use_property_split = True
432         layout.use_property_decorate = False
433
434         gpd = context.gpencil
435         gpl = gpd.layers.active
436
437         layout.prop(gpd, "edit_line_color", text="Edit Line Color")
438         if gpl:
439             layout.prop(gpd, "show_stroke_direction", text="Show Stroke Directions")
440
441
442 class DATA_PT_gpencil_canvas(DataButtonsPanel, Panel):
443     bl_label = "Canvas"
444     bl_parent_id = 'DATA_PT_gpencil_display'
445     bl_options = {'DEFAULT_CLOSED'}
446
447     def draw(self, context):
448         layout = self.layout
449         layout.use_property_split = True
450         layout.use_property_decorate = False
451         gpd = context.gpencil
452         grid = gpd.grid
453
454         row = layout.row(align=True)
455         col = row.column()
456         col.prop(grid, "color", text="Color")
457         col.prop(grid, "scale", text="Scale")
458         col.prop(grid, "offset")
459         row = layout.row(align=True)
460         col = row.column()
461         col.prop(grid, "lines", text="Subdivisions")
462
463
464 class DATA_PT_custom_props_gpencil(DataButtonsPanel, PropertyPanel, Panel):
465     _context_path = "object.data"
466     _property_type = bpy.types.GreasePencil
467
468 ###############################
469
470
471 classes = (
472     DATA_PT_context_gpencil,
473     DATA_PT_gpencil_layers,
474     DATA_PT_gpencil_onion_skinning,
475     DATA_PT_gpencil_onion_skinning_custom_colors,
476     DATA_PT_gpencil_onion_skinning_display,
477     DATA_PT_gpencil_layer_adjustments,
478     DATA_PT_gpencil_layer_relations,
479     DATA_PT_gpencil_layer_display,
480     DATA_PT_gpencil_vertex_groups,
481     DATA_PT_gpencil_strokes,
482     DATA_PT_gpencil_display,
483     DATA_PT_gpencil_canvas,
484     DATA_PT_custom_props_gpencil,
485
486     GPENCIL_UL_vgroups,
487
488     GPENCIL_MT_layer_context_menu,
489     GPENCIL_MT_gpencil_vertex_group,
490 )
491
492 if __name__ == "__main__":  # only for live edit.
493     from bpy.utils import register_class
494     for cls in classes:
495         register_class(cls)