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