GPencil: Add lock icon to Vertex Groups list
[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_layer_display(LayerDataButtonsPanel, Panel):
228     bl_label = "Display"
229     bl_parent_id = 'DATA_PT_gpencil_layers'
230     bl_options = {'DEFAULT_CLOSED'}
231
232     def draw(self, context):
233         layout = self.layout
234         layout.use_property_split = True
235         layout.use_property_decorate = False
236
237         gpd = context.gpencil
238         gpl = gpd.layers.active
239
240         col = layout.row(align=True)
241         col.prop(gpl, "channel_color")
242
243
244 class DATA_PT_gpencil_onion_skinning(DataButtonsPanel, Panel):
245     bl_label = "Onion Skinning"
246     bl_options = {'DEFAULT_CLOSED'}
247
248     def draw(self, context):
249         gpd = context.gpencil
250
251         layout = self.layout
252         layout.use_property_split = True
253         layout.enabled = gpd.users <= 1
254
255         if gpd.users > 1:
256             layout.label(text="Multiuser datablock not supported", icon='ERROR')
257
258         col = layout.column()
259         col.prop(gpd, "onion_mode")
260         col.prop(gpd, "onion_factor", text="Opacity", slider=True)
261
262         if gpd.onion_mode == 'ABSOLUTE':
263             col = layout.column(align=True)
264             col.prop(gpd, "ghost_before_range", text="Frames Before")
265             col.prop(gpd, "ghost_after_range", text="Frames After")
266         elif gpd.onion_mode == 'RELATIVE':
267             col = layout.column(align=True)
268             col.prop(gpd, "ghost_before_range", text="Keyframes Before")
269             col.prop(gpd, "ghost_after_range", text="Keyframes After")
270
271
272 class DATA_PT_gpencil_onion_skinning_custom_colors(DataButtonsPanel, Panel):
273     bl_parent_id = "DATA_PT_gpencil_onion_skinning"
274     bl_label = "Custom Colors"
275     bl_options = {'DEFAULT_CLOSED'}
276
277     def draw_header(self, context):
278         gpd = context.gpencil
279
280         self.layout.prop(gpd, "use_ghost_custom_colors", text="")
281
282     def draw(self, context):
283         gpd = context.gpencil
284
285         layout = self.layout
286         layout.use_property_split = True
287         layout.enabled = gpd.users <= 1 and gpd.use_ghost_custom_colors
288
289         layout.prop(gpd, "before_color", text="Before")
290         layout.prop(gpd, "after_color", text="After")
291
292
293 class DATA_PT_gpencil_onion_skinning_display(DataButtonsPanel, Panel):
294     bl_parent_id = "DATA_PT_gpencil_onion_skinning"
295     bl_label = "Display"
296     bl_options = {'DEFAULT_CLOSED'}
297
298     def draw(self, context):
299         gpd = context.gpencil
300
301         layout = self.layout
302         layout.use_property_split = True
303         layout.enabled = gpd.users <= 1
304
305         layout.prop(gpd, "use_ghosts_always", text="View In Render")
306
307         col = layout.column(align=True)
308         col.prop(gpd, "use_onion_fade", text="Fade")
309         sub = layout.column()
310         sub.active = gpd.onion_mode in {'RELATIVE', 'SELECTED'}
311         sub.prop(gpd, "use_onion_loop", text="Loop")
312
313
314 class GPENCIL_MT_gpencil_vertex_group(Menu):
315     bl_label = "GP Vertex Groups"
316
317     def draw(self, context):
318         layout = self.layout
319
320         layout.operator_context = 'EXEC_AREA'
321         layout.operator("object.vertex_group_add")
322
323         ob = context.active_object
324         if ob.vertex_groups.active:
325             layout.separator()
326
327             layout.operator("gpencil.vertex_group_assign", text="Assign to Active Group")
328             layout.operator("gpencil.vertex_group_remove_from", text="Remove from Active Group")
329
330             layout.separator()
331             layout.operator_menu_enum("object.vertex_group_set_active", "group", text="Set Active Group")
332             layout.operator("object.vertex_group_remove", text="Remove Active Group").all = False
333             layout.operator("object.vertex_group_remove", text="Remove All Groups").all = True
334
335             layout.separator()
336             layout.operator("gpencil.vertex_group_select", text="Select Points")
337             layout.operator("gpencil.vertex_group_deselect", text="Deselect Points")
338
339
340 class GPENCIL_UL_vgroups(UIList):
341     def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
342         vgroup = item
343         if self.layout_type in {'DEFAULT', 'COMPACT'}:
344             layout.prop(vgroup, "name", text="", emboss=False, icon_value=icon)
345             icon = 'LOCKED' if vgroup.lock_weight else 'UNLOCKED'
346             layout.prop(vgroup, "lock_weight", text="", icon=icon, emboss=False)
347         elif self.layout_type == 'GRID':
348             layout.alignment = 'CENTER'
349             layout.label(text="", icon_value=icon)
350
351
352 class DATA_PT_gpencil_vertex_groups(ObjectButtonsPanel, Panel):
353     bl_label = "Vertex Groups"
354     bl_options = {'DEFAULT_CLOSED'}
355
356     def draw(self, context):
357         layout = self.layout
358
359         ob = context.object
360         group = ob.vertex_groups.active
361
362         rows = 2
363         if group:
364             rows = 4
365
366         row = layout.row()
367         row.template_list("GPENCIL_UL_vgroups", "", ob, "vertex_groups", ob.vertex_groups, "active_index", rows=rows)
368
369         col = row.column(align=True)
370         col.operator("object.vertex_group_add", icon='ADD', text="")
371         col.operator("object.vertex_group_remove", icon='REMOVE', text="").all = False
372
373         if ob.vertex_groups:
374             row = layout.row()
375
376             sub = row.row(align=True)
377             sub.operator("gpencil.vertex_group_assign", text="Assign")
378             sub.operator("gpencil.vertex_group_remove_from", text="Remove")
379
380             sub = row.row(align=True)
381             sub.operator("gpencil.vertex_group_select", text="Select")
382             sub.operator("gpencil.vertex_group_deselect", text="Deselect")
383
384             layout.prop(context.tool_settings, "vertex_group_weight", text="Weight")
385
386
387 class DATA_PT_gpencil_strokes(DataButtonsPanel, Panel):
388     bl_label = "Strokes"
389     bl_options = {'DEFAULT_CLOSED'}
390
391     def draw(self, context):
392         layout = self.layout
393         layout.use_property_split = True
394         layout.use_property_decorate = False
395
396         ob = context.object
397         gpd = context.gpencil
398
399         col = layout.column(align=True)
400         col.prop(gpd, "stroke_depth_order")
401
402         if ob:
403             col.enabled = not ob.show_in_front
404
405         col = layout.column(align=True)
406         col.prop(gpd, "stroke_thickness_space")
407         sub = col.column()
408         sub.active = gpd.stroke_thickness_space == 'WORLDSPACE'
409         sub.prop(gpd, "pixel_factor", text="Thickness Scale")
410
411         layout.prop(gpd, "use_force_fill_recalc", text="Force Fill Update")
412         layout.prop(gpd, "use_adaptive_uv", text="Adaptive UVs")
413
414
415 class DATA_PT_gpencil_display(DataButtonsPanel, Panel):
416     bl_label = "Viewport Display"
417     bl_options = {'DEFAULT_CLOSED'}
418
419     def draw(self, context):
420         layout = self.layout
421         layout.use_property_split = True
422         layout.use_property_decorate = False
423
424         gpd = context.gpencil
425         gpl = gpd.layers.active
426
427         layout.prop(gpd, "edit_line_color", text="Edit Line Color")
428         if gpl:
429             layout.prop(gpd, "show_stroke_direction", text="Show Stroke Directions")
430
431
432 class DATA_PT_gpencil_canvas(DataButtonsPanel, Panel):
433     bl_label = "Canvas"
434     bl_parent_id = 'DATA_PT_gpencil_display'
435     bl_options = {'DEFAULT_CLOSED'}
436
437     def draw(self, context):
438         layout = self.layout
439         layout.use_property_split = True
440         layout.use_property_decorate = False
441         gpd = context.gpencil
442         grid = gpd.grid
443
444         row = layout.row(align=True)
445         col = row.column()
446         col.prop(grid, "color", text="Color")
447         col.prop(grid, "scale", text="Scale")
448         col.prop(grid, "offset")
449         row = layout.row(align=True)
450         col = row.column()
451         col.prop(grid, "lines", text="Subdivisions")
452
453
454 class DATA_PT_custom_props_gpencil(DataButtonsPanel, PropertyPanel, Panel):
455     _context_path = "object.data"
456     _property_type = bpy.types.GreasePencil
457
458 ###############################
459
460
461 classes = (
462     DATA_PT_context_gpencil,
463     DATA_PT_gpencil_layers,
464     DATA_PT_gpencil_onion_skinning,
465     DATA_PT_gpencil_onion_skinning_custom_colors,
466     DATA_PT_gpencil_onion_skinning_display,
467     DATA_PT_gpencil_layer_adjustments,
468     DATA_PT_gpencil_layer_relations,
469     DATA_PT_gpencil_layer_display,
470     DATA_PT_gpencil_vertex_groups,
471     DATA_PT_gpencil_strokes,
472     DATA_PT_gpencil_display,
473     DATA_PT_gpencil_canvas,
474     DATA_PT_custom_props_gpencil,
475
476     GPENCIL_UL_vgroups,
477
478     GPENCIL_MT_layer_context_menu,
479     GPENCIL_MT_gpencil_vertex_group,
480 )
481
482 if __name__ == "__main__":  # only for live edit.
483     from bpy.utils import register_class
484     for cls in classes:
485         register_class(cls)