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