5b5a7648d837cbc9a9a54f49b55118dfccf1c03f
[blender-staging.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
22 from bpy.types import Menu, UIList
23
24
25 def gpencil_stroke_placement_settings(context, layout, gpd):
26     col = layout.column(align=True)
27
28     col.label(text="Stroke Placement:")
29
30     row = col.row(align=True)
31     row.prop_enum(gpd, "draw_mode", 'VIEW')
32     row.prop_enum(gpd, "draw_mode", 'CURSOR')
33
34     if context.space_data.type == 'VIEW_3D':
35         row = col.row(align=True)
36         row.prop_enum(gpd, "draw_mode", 'SURFACE')
37         row.prop_enum(gpd, "draw_mode", 'STROKE')
38
39         row = col.row(align=False)
40         row.active = gpd.draw_mode in {'SURFACE', 'STROKE'}
41         row.prop(gpd, "use_stroke_endpoints")
42
43
44 class GreasePencilDrawingToolsPanel:
45     # subclass must set
46     # bl_space_type = 'IMAGE_EDITOR'
47     bl_label = "Grease Pencil"
48     bl_category = "Grease Pencil"
49     bl_region_type = 'TOOLS'
50
51     @staticmethod
52     def draw(self, context):
53         layout = self.layout
54
55         col = layout.column(align=True)
56
57         col.label(text="Draw:")
58         row = col.row(align=True)
59         row.operator("gpencil.draw", text="Draw").mode = 'DRAW'
60         row.operator("gpencil.draw", text="Erase").mode = 'ERASER'
61
62         row = col.row(align=True)
63         row.operator("gpencil.draw", text="Line").mode = 'DRAW_STRAIGHT'
64         row.operator("gpencil.draw", text="Poly").mode = 'DRAW_POLY'
65
66         row = col.row(align=True)
67         row.prop(context.tool_settings, "use_grease_pencil_sessions", text="Continuous Drawing")
68
69         if context.space_data.type in {'VIEW_3D', 'CLIP_EDITOR'}:
70             col.separator()
71             col.label("Data Source:")
72             row = col.row(align=True)
73             if context.space_data.type == 'VIEW_3D':
74                 row.prop(context.tool_settings, "grease_pencil_source", expand=True)
75             elif context.space_data.type == 'CLIP_EDITOR':
76                 row.prop(context.space_data, "grease_pencil_source", expand=True)
77
78         gpd = context.gpencil_data
79         if gpd:
80             col.separator()
81             gpencil_stroke_placement_settings(context, col, gpd)
82
83         if context.space_data.type == 'VIEW_3D':
84             col.separator()
85             col.separator()
86
87             col.label(text="Tools:")
88             col.operator("gpencil.convert", text="Convert...")
89             col.operator("view3d.ruler")
90
91
92 class GreasePencilStrokeEditPanel:
93     # subclass must set
94     # bl_space_type = 'IMAGE_EDITOR'
95     bl_label = "Edit Strokes"
96     bl_category = "Grease Pencil"
97     bl_region_type = 'TOOLS'
98
99     @classmethod
100     def poll(cls, context):
101         return (context.gpencil_data is not None)
102
103     @staticmethod
104     def draw(self, context):
105         layout = self.layout
106
107         gpd = context.gpencil_data
108         edit_ok = bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode)
109
110         col = layout.column(align=True)
111         col.prop(gpd, "use_stroke_edit_mode", text="Enable Editing", icon='EDIT', toggle=True)
112
113         col.separator()
114
115         col.label(text="Select:")
116         subcol = col.column(align=True)
117         subcol.active = edit_ok
118         subcol.operator("gpencil.select_all", text="Select All")
119         subcol.operator("gpencil.select_border")
120         subcol.operator("gpencil.select_circle")
121
122         col.separator()
123
124         subcol = col.column(align=True)
125         subcol.active = edit_ok
126         subcol.operator("gpencil.select_linked")
127         subcol.operator("gpencil.select_more")
128         subcol.operator("gpencil.select_less")
129
130         col.separator()
131
132         col.label(text="Edit:")
133         row = col.row(align=True)
134         row.active = edit_ok
135         row.operator("gpencil.copy", text="Copy")
136         row.operator("gpencil.paste", text="Paste")
137
138         subcol = col.column(align=True)
139         subcol.active = edit_ok
140         subcol.operator("gpencil.delete", text="Delete")
141         subcol.operator("gpencil.duplicate_move", text="Duplicate")
142         subcol.operator("transform.mirror", text="Mirror").gpencil_strokes = True
143
144         col.separator()
145
146         subcol = col.column(align=True)
147         subcol.active = edit_ok
148         subcol.operator("transform.translate").gpencil_strokes = True   # icon='MAN_TRANS'
149         subcol.operator("transform.rotate").gpencil_strokes = True      # icon='MAN_ROT'
150         subcol.operator("transform.resize", text="Scale").gpencil_strokes = True      # icon='MAN_SCALE'
151
152         col.separator()
153
154         subcol = col.column(align=True)
155         subcol.active = edit_ok
156         subcol.operator("transform.bend", text="Bend").gpencil_strokes = True
157         subcol.operator("transform.shear", text="Shear").gpencil_strokes = True
158         subcol.operator("transform.tosphere", text="To Sphere").gpencil_strokes = True
159
160
161 ###############################
162
163 class GPENCIL_PIE_tool_palette(Menu):
164     """A pie menu for quick access to Grease Pencil tools"""
165     bl_label = "Grease Pencil Tools"
166
167     def draw(self, context):
168         layout = self.layout
169
170         pie = layout.menu_pie()
171         gpd = context.gpencil_data
172
173         # W - Drawing Types
174         col = pie.column()
175         col.operator("gpencil.draw", text="Draw", icon='GREASEPENCIL').mode = 'DRAW'
176         col.operator("gpencil.draw", text="Straight Lines", icon='LINE_DATA').mode = 'DRAW_STRAIGHT'
177         col.operator("gpencil.draw", text="Poly", icon='MESH_DATA').mode = 'DRAW_POLY'
178
179         # E - Eraser
180         # XXX: needs a dedicated icon...
181         col = pie.column()
182         col.operator("gpencil.draw", text="Eraser", icon='FORCE_CURVE').mode = 'ERASER'
183
184         # E - "Settings" Palette is included here too, since it needs to be in a stable position...
185         if gpd and gpd.layers.active:
186             col.separator()
187             col.operator("wm.call_menu_pie", text="Settings...", icon='SCRIPTWIN').name = "GPENCIL_PIE_settings_palette"
188
189         # Editing tools
190         if gpd:
191             if gpd.use_stroke_edit_mode and context.editable_gpencil_strokes:
192                 # S - Exit Edit Mode
193                 pie.prop(gpd, "use_stroke_edit_mode", text="Exit Edit Mode", icon='EDIT')
194
195                 # N - Transforms
196                 col = pie.column()
197                 row = col.row(align=True)
198                 row.operator("transform.translate", icon='MAN_TRANS').gpencil_strokes = True
199                 row.operator("transform.rotate", icon='MAN_ROT').gpencil_strokes = True
200                 row.operator("transform.resize", text="Scale", icon='MAN_SCALE').gpencil_strokes = True
201                 row = col.row(align=True)
202                 row.label("Proportional Edit:")
203                 row.prop(context.tool_settings, "proportional_edit", text="", icon_only=True)
204                 row.prop(context.tool_settings, "proportional_edit_falloff", text="", icon_only=True)
205
206                 # NW - Select (Non-Modal)
207                 col = pie.column()
208                 col.operator("gpencil.select_all", text="Select All", icon='PARTICLE_POINT')
209                 col.operator("gpencil.select_all", text="Select Inverse", icon='BLANK1')
210                 col.operator("gpencil.select_linked", text="Select Linked", icon='LINKED')
211
212                 # NE - Select (Modal)
213                 col = pie.column()
214                 col.operator("gpencil.select_border", text="Border Select", icon='BORDER_RECT')
215                 col.operator("gpencil.select_circle", text="Circle Select", icon='META_EMPTY')
216                 col.operator("gpencil.select_lasso", text="Lasso Select", icon='BORDER_LASSO')
217
218                 # SW - Edit Tools
219                 col = pie.column()
220                 col.operator("gpencil.duplicate_move", icon='PARTICLE_PATH', text="Duplicate")
221                 col.operator("gpencil.delete", icon='X', text="Delete...")
222
223                 # SE - More Tools
224                 pie.operator("wm.call_menu_pie", text="More...").name = "GPENCIL_PIE_tools_more"
225             else:
226                 # Toggle Edit Mode
227                 pie.prop(gpd, "use_stroke_edit_mode", text="Enable Stroke Editing", icon='EDIT')
228
229
230 class GPENCIL_PIE_settings_palette(Menu):
231     """A pie menu for quick access to Grease Pencil settings"""
232     bl_label = "Grease Pencil Settings"
233
234     @classmethod
235     def poll(cls, context):
236         return bool(context.gpencil_data and context.active_gpencil_layer)
237
238     def draw(self, context):
239         layout = self.layout
240
241         pie = layout.menu_pie()
242         # gpd = context.gpencil_data
243         gpl = context.active_gpencil_layer
244
245         # W - Stroke draw settings
246         col = pie.column(align=True)
247         col.label(text="Stroke")
248         col.prop(gpl, "color", text="")
249         col.prop(gpl, "alpha", text="", slider=True)
250
251         # E - Fill draw settings
252         col = pie.column(align=True)
253         col.label(text="Fill")
254         col.prop(gpl, "fill_color", text="")
255         col.prop(gpl, "fill_alpha", text="", slider=True)
256
257         # S - Layer settings
258         col = pie.column()
259         col.prop(gpl, "line_width", slider=True)
260         # col.prop(gpl, "use_volumetric_strokes")
261         col.prop(gpl, "use_onion_skinning")
262
263         # N - Active Layer
264         # XXX: this should show an operator to change the active layer instead
265         col = pie.column()
266         col.label("Active Layer:      ")
267         col.prop(gpl, "info", text="")
268         # col.prop(gpd, "layers")
269         row = col.row()
270         row.prop(gpl, "lock")
271         row.prop(gpl, "hide")
272
273
274 class GPENCIL_PIE_tools_more(Menu):
275     """A pie menu for accessing more Grease Pencil tools"""
276     bl_label = "More Grease Pencil Tools"
277
278     @classmethod
279     def poll(cls, context):
280         gpd = context.gpencil_data
281         return bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes)
282
283     def draw(self, context):
284         layout = self.layout
285
286         pie = layout.menu_pie()
287         # gpd = context.gpencil_data
288
289         col = pie.column(align=True)
290         col.operator("gpencil.copy", icon='COPYDOWN', text="Copy")
291         col.operator("gpencil.paste", icon='PASTEDOWN', text="Paste")
292
293         col = pie.column(align=True)
294         col.operator("gpencil.select_more", icon='ZOOMIN')
295         col.operator("gpencil.select_less", icon='ZOOMOUT')
296
297         pie.operator("transform.mirror", icon='MOD_MIRROR').gpencil_strokes = True
298         pie.operator("transform.bend", icon='MOD_SIMPLEDEFORM').gpencil_strokes = True
299         pie.operator("transform.shear", icon='MOD_TRIANGULATE').gpencil_strokes = True
300         pie.operator("transform.tosphere", icon='MOD_MULTIRES').gpencil_strokes = True
301
302         pie.operator("gpencil.convert", icon='OUTLINER_OB_CURVE', text="Convert...")
303         pie.operator("wm.call_menu_pie", text="Back to Main Palette...").name = "GPENCIL_PIE_tool_palette"
304
305
306 class GPENCIL_UL_layer(UIList):
307     def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
308         # assert(isinstance(item, bpy.types.GPencilLayer)
309         gpl = item
310
311         if self.layout_type in {'DEFAULT', 'COMPACT'}:
312             if gpl.lock:
313                 layout.active = False
314
315             split = layout.split(percentage=0.25)
316             row = split.row(align=True)
317             row.prop(gpl, "color", text="", emboss=gpl.is_stroke_visible)
318             row.prop(gpl, "fill_color", text="", emboss=gpl.is_fill_visible)
319             split.prop(gpl, "info", text="", emboss=False)
320
321             row = layout.row(align=True)
322             row.prop(gpl, "lock", text="", emboss=False)
323             row.prop(gpl, "hide", text="", emboss=False)
324         elif self.layout_type in {'GRID'}:
325             layout.alignment = 'CENTER'
326             layout.label(text="", icon_value=icon)
327
328
329 class GreasePencilDataPanel:
330     # subclass must set
331     # bl_space_type = 'IMAGE_EDITOR'
332     bl_label = "Grease Pencil"
333     bl_region_type = 'UI'
334
335     @staticmethod
336     def draw_header(self, context):
337         self.layout.prop(context.space_data, "show_grease_pencil", text="")
338
339     @staticmethod
340     def draw(self, context):
341         layout = self.layout
342
343         # owner of Grease Pencil data
344         gpd_owner = context.gpencil_data_owner
345         gpd = context.gpencil_data
346
347         # Owner Selector
348         if context.space_data.type == 'VIEW_3D':
349             layout.prop(context.tool_settings, "grease_pencil_source", expand=True)
350         elif context.space_data.type == 'CLIP_EDITOR':
351             layout.prop(context.space_data, "grease_pencil_source", expand=True)
352
353         # Grease Pencil data selector
354         layout.template_ID(gpd_owner, "grease_pencil", new="gpencil.data_add", unlink="gpencil.data_unlink")
355
356         # Grease Pencil data...
357         if gpd:
358             self.draw_layers(context, layout, gpd)
359
360     def draw_layers(self, context, layout, gpd):
361         row = layout.row()
362
363         col = row.column()
364         if len(gpd.layers) >= 2:
365             layer_rows = 5
366         else:
367             layer_rows = 2
368         col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index", rows=layer_rows)
369
370         col = row.column()
371
372         sub = col.column(align=True)
373         sub.operator("gpencil.layer_add", icon='ZOOMIN', text="")
374         sub.operator("gpencil.layer_remove", icon='ZOOMOUT', text="")
375
376         gpl = context.active_gpencil_layer
377         if gpl:
378             sub.operator("gpencil.layer_duplicate", icon='COPY_ID', text="")  # XXX: needs a dedicated icon
379
380             if len(gpd.layers) > 1:
381                 col.separator()
382
383                 sub = col.column(align=True)
384                 sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP'
385                 sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN'
386
387         if gpl:
388             self.draw_layer(layout, gpl)
389
390     def draw_layer(self, layout, gpl):
391         # layer settings
392         split = layout.split(percentage=0.5)
393         split.active = not gpl.lock
394
395         # Column 1 - Stroke
396         col = split.column(align=True)
397         col.label(text="Stroke:")
398         col.prop(gpl, "color", text="")
399         col.prop(gpl, "alpha", slider=True)
400
401         # Column 2 - Fill
402         col = split.column(align=True)
403         col.label(text="Fill:")
404         col.prop(gpl, "fill_color", text="")
405         col.prop(gpl, "fill_alpha", text="Opacity", slider=True)
406
407         # Options
408         split = layout.split(percentage=0.5)
409         split.active = not gpl.lock
410
411         col = split.column(align=True)
412         col.prop(gpl, "line_width", slider=True)
413         col.prop(gpl, "use_volumetric_strokes")
414
415         col = split.column(align=True)
416         col.prop(gpl, "show_x_ray")
417
418         # if debug:
419         #     layout.prop(gpl, "show_points")
420
421         layout.separator()
422
423         # Full-Row - Frame Locking (and Delete Frame)
424         row = layout.row(align=True)
425         row.active = not gpl.lock
426
427         if gpl.active_frame:
428             lock_status = "Locked" if gpl.lock_frame else "Unlocked"
429             lock_label = "Frame: %d (%s)" % (gpl.active_frame.frame_number, lock_status)
430         else:
431             lock_label = "Lock Frame"
432         row.prop(gpl, "lock_frame", text=lock_label, icon='UNLOCKED')
433         row.operator("gpencil.active_frame_delete", text="", icon='X')
434
435         layout.separator()
436
437         # Onion skinning
438         col = layout.column(align=True)
439         col.active = not gpl.lock
440
441         row = col.row()
442         row.prop(gpl, "use_onion_skinning")
443         row.prop(gpl, "use_ghost_custom_colors", text="", icon='COLOR')
444
445         split = col.split(percentage=0.5)
446         split.active = gpl.use_onion_skinning
447
448         # - Before Frames
449         sub = split.column(align=True)
450         row = sub.row(align=True)
451         row.active = gpl.use_ghost_custom_colors
452         row.prop(gpl, "before_color", text="")
453         sub.prop(gpl, "ghost_before_range", text="Before")
454
455         # - After Frames
456         sub = split.column(align=True)
457         row = sub.row(align=True)
458         row.active = gpl.use_ghost_custom_colors
459         row.prop(gpl, "after_color", text="")
460         sub.prop(gpl, "ghost_after_range", text="After")
461
462
463 class GreasePencilToolsPanel:
464     # subclass must set
465     # bl_space_type = 'IMAGE_EDITOR'
466     # bl_options = {'DEFAULT_CLOSED'}
467     bl_label = "Grease Pencil Settings"
468     bl_region_type = 'UI'
469
470     @classmethod
471     def poll(cls, context):
472         return (context.gpencil_data is not None)
473
474     @staticmethod
475     def draw(self, context):
476         layout = self.layout
477
478         # gpd_owner = context.gpencil_data_owner
479         gpd = context.gpencil_data
480
481         layout.prop(gpd, "use_stroke_edit_mode", text="Enable Editing", icon='EDIT', toggle=True)
482
483         layout.separator()
484
485         layout.label("Proportional Edit:")
486         row = layout.row()
487         row.prop(context.tool_settings, "proportional_edit", text="")
488         row.prop(context.tool_settings, "proportional_edit_falloff", text="")
489
490         layout.separator()
491         layout.separator()
492
493         gpencil_stroke_placement_settings(context, layout, gpd)