Usual i18n fixes, and cleanup: fix 'randomise' -> 'randomize' spelling in code.
[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 from bpy.types import Menu, UIList
23
24
25 def gpencil_stroke_placement_settings(context, layout):
26     if context.space_data.type == 'VIEW_3D':
27         propname = "gpencil_stroke_placement_view3d"
28     elif context.space_data.type == 'SEQUENCE_EDITOR':
29         propname = "gpencil_stroke_placement_sequencer_preview"
30     elif context.space_data.type == 'IMAGE_EDITOR':
31         propname = "gpencil_stroke_placement_image_edit"
32     else:
33         propname = "gpencil_stroke_placement_view2d"
34
35     ts = context.tool_settings
36
37     col = layout.column(align=True)
38
39     col.label(text="Stroke Placement:")
40
41     row = col.row(align=True)
42     row.prop_enum(ts, propname, 'VIEW')
43     row.prop_enum(ts, propname, 'CURSOR')
44
45     if context.space_data.type == 'VIEW_3D':
46         row = col.row(align=True)
47         row.prop_enum(ts, propname, 'SURFACE')
48         row.prop_enum(ts, propname, 'STROKE')
49
50         row = col.row(align=False)
51         row.active = getattr(ts, propname) in {'SURFACE', 'STROKE'}
52         row.prop(ts, "use_gpencil_stroke_endpoints")
53
54
55 class GreasePencilDrawingToolsPanel:
56     # subclass must set
57     # bl_space_type = 'IMAGE_EDITOR'
58     bl_label = "Grease Pencil"
59     bl_category = "Grease Pencil"
60     bl_region_type = 'TOOLS'
61
62     @staticmethod
63     def draw(self, context):
64         layout = self.layout
65
66         col = layout.column(align=True)
67
68         col.label(text="Draw:")
69         row = col.row(align=True)
70         row.operator("gpencil.draw", icon='GREASEPENCIL', text="Draw").mode = 'DRAW'
71         row.operator("gpencil.draw", icon='FORCE_CURVE', text="Erase").mode = 'ERASER'  # XXX: Needs a dedicated icon
72
73         row = col.row(align=True)
74         row.operator("gpencil.draw", icon='LINE_DATA', text="Line").mode = 'DRAW_STRAIGHT'
75         row.operator("gpencil.draw", icon='MESH_DATA', text="Poly").mode = 'DRAW_POLY'
76
77         sub = col.column(align=True)
78         sub.prop(context.tool_settings, "use_gpencil_additive_drawing", text="Additive Drawing")
79         sub.prop(context.tool_settings, "use_gpencil_continuous_drawing", text="Continuous Drawing")
80
81         col.separator()
82         col.separator()
83
84         if context.space_data.type in {'VIEW_3D', 'CLIP_EDITOR'}:
85             col.separator()
86             col.label("Data Source:")
87             row = col.row(align=True)
88             if context.space_data.type == 'VIEW_3D':
89                 row.prop(context.tool_settings, "grease_pencil_source", expand=True)
90             elif context.space_data.type == 'CLIP_EDITOR':
91                 row.prop(context.space_data, "grease_pencil_source", expand=True)
92         
93         col.separator()
94         col.separator()
95
96         gpencil_stroke_placement_settings(context, col)
97
98         gpd = context.gpencil_data
99
100         if gpd:
101             layout.separator()
102             layout.separator()
103
104             col = layout.column(align=True)
105             col.prop(gpd, "use_stroke_edit_mode", text="Enable Editing", icon='EDIT', toggle=True)
106
107         if context.space_data.type == 'VIEW_3D':
108             col.separator()
109             col.separator()
110
111             col.label(text="Tools:")
112             col.operator("gpencil.convert", text="Convert...")
113             col.operator("view3d.ruler")
114
115
116 class GreasePencilStrokeEditPanel:
117     # subclass must set
118     # bl_space_type = 'IMAGE_EDITOR'
119     bl_label = "Edit Strokes"
120     bl_category = "Grease Pencil"
121     bl_region_type = 'TOOLS'
122     bl_options = {'DEFAULT_CLOSED'}
123
124     @classmethod
125     def poll(cls, context):
126         if context.gpencil_data is None:
127             return False
128
129         gpd = context.gpencil_data
130         return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode)
131
132     @staticmethod
133     def draw(self, context):
134         layout = self.layout
135
136         layout.label(text="Select:")
137         col = layout.column(align=True)
138         col.operator("gpencil.select_all", text="Select All")
139         col.operator("gpencil.select_border")
140         col.operator("gpencil.select_circle")
141
142         layout.separator()
143
144         col = layout.column(align=True)
145         col.operator("gpencil.select_linked")
146         col.operator("gpencil.select_more")
147         col.operator("gpencil.select_less")
148
149         layout.separator()
150
151         layout.label(text="Edit:")
152         row = layout.row(align=True)
153         row.operator("gpencil.copy", text="Copy")
154         row.operator("gpencil.paste", text="Paste")
155
156         col = layout.column(align=True)
157         col.operator("gpencil.delete", text="Delete")
158         col.operator("gpencil.duplicate_move", text="Duplicate")
159         col.operator("transform.mirror", text="Mirror")
160
161         layout.separator()
162
163         col = layout.column(align=True)
164         col.operator("transform.translate")                # icon='MAN_TRANS'
165         col.operator("transform.rotate")                   # icon='MAN_ROT'
166         col.operator("transform.resize", text="Scale")     # icon='MAN_SCALE'
167
168         layout.separator()
169
170         col = layout.column(align=True)
171         col.operator("transform.bend", text="Bend")
172         col.operator("transform.shear", text="Shear")
173         col.operator("transform.tosphere", text="To Sphere")
174
175
176 class GreasePencilStrokeSculptPanel:
177     # subclass must set
178     # bl_space_type = 'IMAGE_EDITOR'
179     bl_label = "Sculpt Strokes"
180     bl_category = "Grease Pencil"
181     bl_region_type = 'TOOLS'
182     bl_options = {'DEFAULT_CLOSED'}
183
184     @classmethod
185     def poll(cls, context):
186         if context.gpencil_data is None:
187             return False
188
189         gpd = context.gpencil_data
190         return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode)
191
192     @staticmethod
193     def draw(self, context):
194         layout = self.layout
195         
196         settings = context.tool_settings.gpencil_sculpt
197         tool = settings.tool
198         brush = settings.brush
199         
200         layout.column().prop(settings, "tool", expand=True)
201
202         col = layout.column()
203         col.prop(brush, "size", slider=True)
204         row = col.row(align=True)
205         row.prop(brush, "strength", slider=True)
206         row.prop(brush, "use_pressure_strength", text="")
207         col.prop(brush, "use_falloff")
208
209         layout.separator()
210
211         if settings.tool in {'THICKNESS', 'PINCH', 'TWIST'}:
212             layout.row().prop(brush, "direction", expand=True)
213
214         layout.separator()
215         layout.prop(settings, "use_select_mask")
216
217         if settings.tool == 'SMOOTH':
218             layout.prop(brush, "affect_pressure")
219
220
221 ###############################
222
223 class GPENCIL_PIE_tool_palette(Menu):
224     """A pie menu for quick access to Grease Pencil tools"""
225     bl_label = "Grease Pencil Tools"
226
227     def draw(self, context):
228         layout = self.layout
229
230         pie = layout.menu_pie()
231         gpd = context.gpencil_data
232
233         # W - Drawing Types
234         col = pie.column()
235         col.operator("gpencil.draw", text="Draw", icon='GREASEPENCIL').mode = 'DRAW'
236         col.operator("gpencil.draw", text="Straight Lines", icon='LINE_DATA').mode = 'DRAW_STRAIGHT'
237         col.operator("gpencil.draw", text="Poly", icon='MESH_DATA').mode = 'DRAW_POLY'
238
239         # E - Eraser
240         # XXX: needs a dedicated icon...
241         col = pie.column()
242         col.operator("gpencil.draw", text="Eraser", icon='FORCE_CURVE').mode = 'ERASER'
243
244         # E - "Settings" Palette is included here too, since it needs to be in a stable position...
245         if gpd and gpd.layers.active:
246             col.separator()
247             col.operator("wm.call_menu_pie", text="Settings...", icon='SCRIPTWIN').name = "GPENCIL_PIE_settings_palette"
248
249         # Editing tools
250         if gpd:
251             if gpd.use_stroke_edit_mode and context.editable_gpencil_strokes:
252                 # S - Exit Edit Mode
253                 pie.operator("gpencil.editmode_toggle", text="Exit Edit Mode", icon='EDIT')
254
255                 # N - Transforms
256                 col = pie.column()
257                 row = col.row(align=True)
258                 row.operator("transform.translate", icon='MAN_TRANS')
259                 row.operator("transform.rotate", icon='MAN_ROT')
260                 row.operator("transform.resize", text="Scale", icon='MAN_SCALE')
261                 row = col.row(align=True)
262                 row.label("Proportional Edit:")
263                 row.prop(context.tool_settings, "proportional_edit", text="", icon_only=True)
264                 row.prop(context.tool_settings, "proportional_edit_falloff", text="", icon_only=True)
265
266                 # NW - Select (Non-Modal)
267                 col = pie.column()
268                 col.operator("gpencil.select_all", text="Select All", icon='PARTICLE_POINT')
269                 col.operator("gpencil.select_all", text="Select Inverse", icon='BLANK1')
270                 col.operator("gpencil.select_linked", text="Select Linked", icon='LINKED')
271
272                 # NE - Select (Modal)
273                 col = pie.column()
274                 col.operator("gpencil.select_border", text="Border Select", icon='BORDER_RECT')
275                 col.operator("gpencil.select_circle", text="Circle Select", icon='META_EMPTY')
276                 col.operator("gpencil.select_lasso", text="Lasso Select", icon='BORDER_LASSO')
277
278                 # SW - Edit Tools
279                 col = pie.column()
280                 col.operator("gpencil.duplicate_move", icon='PARTICLE_PATH', text="Duplicate")
281                 col.operator("gpencil.delete", icon='X', text="Delete...")
282
283                 # SE - More Tools
284                 pie.operator("wm.call_menu_pie", text="More...").name = "GPENCIL_PIE_tools_more"
285             else:
286                 # Toggle Edit Mode
287                 pie.operator("gpencil.editmode_toggle", text="Enable Stroke Editing", icon='EDIT')
288
289
290 class GPENCIL_PIE_settings_palette(Menu):
291     """A pie menu for quick access to Grease Pencil settings"""
292     bl_label = "Grease Pencil Settings"
293
294     @classmethod
295     def poll(cls, context):
296         return bool(context.gpencil_data and context.active_gpencil_layer)
297
298     def draw(self, context):
299         layout = self.layout
300
301         pie = layout.menu_pie()
302         # gpd = context.gpencil_data
303         gpl = context.active_gpencil_layer
304
305         # W - Stroke draw settings
306         col = pie.column(align=True)
307         col.label(text="Stroke")
308         col.prop(gpl, "color", text="")
309         col.prop(gpl, "alpha", text="", slider=True)
310
311         # E - Fill draw settings
312         col = pie.column(align=True)
313         col.label(text="Fill")
314         col.prop(gpl, "fill_color", text="")
315         col.prop(gpl, "fill_alpha", text="", slider=True)
316
317         # S - Layer settings
318         col = pie.column()
319         col.prop(gpl, "line_width", slider=True)
320         # col.prop(gpl, "use_volumetric_strokes")
321         col.prop(gpl, "use_onion_skinning")
322
323         # N - Active Layer
324         col = pie.column()
325         col.label("Active Layer:      ")
326
327         row = col.row()
328         row.operator_context = 'EXEC_REGION_WIN'
329         row.operator_menu_enum("gpencil.layer_change", "layer", text="", icon='GREASEPENCIL')
330         row.prop(gpl, "info", text="")
331         row.operator("gpencil.layer_remove", text="", icon='X')
332
333         row = col.row()
334         row.prop(gpl, "lock")
335         row.prop(gpl, "hide")
336
337
338 class GPENCIL_PIE_tools_more(Menu):
339     """A pie menu for accessing more Grease Pencil tools"""
340     bl_label = "More Grease Pencil Tools"
341
342     @classmethod
343     def poll(cls, context):
344         gpd = context.gpencil_data
345         return bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes)
346
347     def draw(self, context):
348         layout = self.layout
349
350         pie = layout.menu_pie()
351         # gpd = context.gpencil_data
352
353         col = pie.column(align=True)
354         col.operator("gpencil.copy", icon='COPYDOWN', text="Copy")
355         col.operator("gpencil.paste", icon='PASTEDOWN', text="Paste")
356
357         col = pie.column(align=True)
358         col.operator("gpencil.select_more", icon='ZOOMIN')
359         col.operator("gpencil.select_less", icon='ZOOMOUT')
360
361         pie.operator("transform.mirror", icon='MOD_MIRROR')
362         pie.operator("transform.bend", icon='MOD_SIMPLEDEFORM')
363         pie.operator("transform.shear", icon='MOD_TRIANGULATE')
364         pie.operator("transform.tosphere", icon='MOD_MULTIRES')
365
366         pie.operator("gpencil.convert", icon='OUTLINER_OB_CURVE', text="Convert...")
367         pie.operator("wm.call_menu_pie", text="Back to Main Palette...").name = "GPENCIL_PIE_tool_palette"
368
369
370 class GPENCIL_PIE_sculpt(Menu):
371     """A pie menu for accessing Grease Pencil stroke sculpting settings"""
372     bl_label = "Grease Pencil Sculpt"
373
374     @classmethod
375     def poll(cls, context):
376         gpd = context.gpencil_data
377         return bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes)
378
379     def draw(self, context):
380         layout = self.layout
381
382         pie = layout.menu_pie()
383
384         settings = context.tool_settings.gpencil_sculpt
385         brush = settings.brush
386
387         # W - Launch Sculpt Mode
388         col = pie.column()
389         #col.label("Tool:")
390         col.prop(settings, "tool", text="")
391         col.operator("gpencil.brush_paint", text="Sculpt", icon='SCULPTMODE_HLT')
392
393         # E - Common Settings
394         col = pie.column(align=True)
395         col.prop(brush, "size", slider=True)
396         row = col.row(align=True)
397         row.prop(brush, "strength", slider=True)
398        # row.prop(brush, "use_pressure_strength", text="", icon_only=True)
399         col.prop(brush, "use_falloff")
400
401         # S - Change Brush Type Shortcuts
402         row = pie.row()
403         row.prop_enum(settings, "tool", value='GRAB')
404         row.prop_enum(settings, "tool", value='PUSH')
405         row.prop_enum(settings, "tool", value='CLONE')
406
407         # N - Change Brush Type Shortcuts
408         row = pie.row()
409         row.prop_enum(settings, "tool", value='SMOOTH')
410         row.prop_enum(settings, "tool", value='THICKNESS')
411         row.prop_enum(settings, "tool", value='RANDOMIZE')
412
413
414 ###############################
415
416
417 class GPENCIL_MT_snap(Menu):
418     bl_label = "Snap"
419
420     def draw(self, context):
421         layout = self.layout
422
423         layout.operator("gpencil.snap_to_grid", text="Selection to Grid")
424         layout.operator("gpencil.snap_to_cursor", text="Selection to Cursor").use_offset = False
425         layout.operator("gpencil.snap_to_cursor", text="Selection to Cursor (Offset)").use_offset = True
426
427         layout.separator()
428
429         layout.operator("gpencil.snap_cursor_to_selected", text="Cursor to Selected")
430         layout.operator("view3d.snap_cursor_to_center", text="Cursor to Center")
431         layout.operator("view3d.snap_cursor_to_grid", text="Cursor to Grid")
432
433
434 ###############################
435
436
437 class GPENCIL_UL_layer(UIList):
438     def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
439         # assert(isinstance(item, bpy.types.GPencilLayer)
440         gpl = item
441
442         if self.layout_type in {'DEFAULT', 'COMPACT'}:
443             if gpl.lock:
444                 layout.active = False
445
446             split = layout.split(percentage=0.25)
447             row = split.row(align=True)
448             row.prop(gpl, "color", text="", emboss=gpl.is_stroke_visible)
449             row.prop(gpl, "fill_color", text="", emboss=gpl.is_fill_visible)
450             split.prop(gpl, "info", text="", emboss=False)
451
452             row = layout.row(align=True)
453             row.prop(gpl, "lock", text="", emboss=False)
454             row.prop(gpl, "hide", text="", emboss=False)
455         elif self.layout_type == 'GRID':
456             layout.alignment = 'CENTER'
457             layout.label(text="", icon_value=icon)
458
459
460 class GPENCIL_MT_layer_specials(Menu):
461     bl_label = "Layer"
462
463     def draw(self, context):
464         layout = self.layout
465
466         layout.operator("gpencil.layer_duplicate", icon='COPY_ID')  # XXX: needs a dedicated icon
467
468         layout.separator()
469
470         layout.operator("gpencil.reveal", icon='RESTRICT_VIEW_OFF', text="Show All")
471         layout.operator("gpencil.hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True
472
473         layout.separator()
474
475         layout.operator("gpencil.lock_all", icon='LOCKED', text="Lock All")
476         layout.operator("gpencil.unlock_all", icon='UNLOCKED', text="UnLock All")
477
478
479 class GreasePencilDataPanel:
480     # subclass must set
481     # bl_space_type = 'IMAGE_EDITOR'
482     bl_label = "Grease Pencil"
483     bl_region_type = 'UI'
484
485     @staticmethod
486     def draw_header(self, context):
487         self.layout.prop(context.space_data, "show_grease_pencil", text="")
488
489     @staticmethod
490     def draw(self, context):
491         layout = self.layout
492
493         # owner of Grease Pencil data
494         gpd_owner = context.gpencil_data_owner
495         gpd = context.gpencil_data
496
497         # Owner Selector
498         if context.space_data.type == 'VIEW_3D':
499             layout.prop(context.tool_settings, "grease_pencil_source", expand=True)
500         elif context.space_data.type == 'CLIP_EDITOR':
501             layout.prop(context.space_data, "grease_pencil_source", expand=True)
502
503         # Grease Pencil data selector
504         layout.template_ID(gpd_owner, "grease_pencil", new="gpencil.data_add", unlink="gpencil.data_unlink")
505
506         # Grease Pencil data...
507         if (gpd is None) or (not gpd.layers):
508             layout.operator("gpencil.layer_add", text="New Layer")
509         else:
510             self.draw_layers(context, layout, gpd)
511
512     def draw_layers(self, context, layout, gpd):
513         row = layout.row()
514
515         col = row.column()
516         if len(gpd.layers) >= 2:
517             layer_rows = 5
518         else:
519             layer_rows = 2
520         col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index", rows=layer_rows)
521
522         col = row.column()
523
524         sub = col.column(align=True)
525         sub.operator("gpencil.layer_add", icon='ZOOMIN', text="")
526         sub.operator("gpencil.layer_remove", icon='ZOOMOUT', text="")
527
528         gpl = context.active_gpencil_layer
529         if gpl:
530             sub.menu("GPENCIL_MT_layer_specials", icon='DOWNARROW_HLT', text="")
531
532             if len(gpd.layers) > 1:
533                 col.separator()
534
535                 sub = col.column(align=True)
536                 sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP'
537                 sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN'
538
539                 col.separator()
540
541                 sub = col.column(align=True)
542                 sub.operator("gpencil.layer_isolate", icon='SOLO_OFF', text="").affect_visibility = False
543                 sub.operator("gpencil.layer_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True
544
545         if gpl:
546             self.draw_layer(layout, gpl)
547
548     def draw_layer(self, layout, gpl):
549         # layer settings
550         split = layout.split(percentage=0.5)
551         split.active = not gpl.lock
552
553         # Column 1 - Stroke
554         col = split.column(align=True)
555         col.label(text="Stroke:")
556         col.prop(gpl, "color", text="")
557         col.prop(gpl, "alpha", slider=True)
558
559         # Column 2 - Fill
560         col = split.column(align=True)
561         col.label(text="Fill:")
562         col.prop(gpl, "fill_color", text="")
563         col.prop(gpl, "fill_alpha", text="Opacity", slider=True)
564
565         # Options
566         split = layout.split(percentage=0.5)
567         split.active = not gpl.lock
568
569         col = split.column(align=True)
570         col.prop(gpl, "line_width", slider=True)
571         col.prop(gpl, "use_volumetric_strokes")
572
573         col = split.column(align=True)
574         col.prop(gpl, "show_x_ray")
575         col.prop(gpl, "show_points", text="Points")
576
577         layout.separator()
578
579         # Full-Row - Frame Locking (and Delete Frame)
580         row = layout.row(align=True)
581         row.active = not gpl.lock
582
583         if gpl.active_frame:
584             lock_status = "Locked" if gpl.lock_frame else "Unlocked"
585             lock_label = "Frame: %d (%s)" % (gpl.active_frame.frame_number, lock_status)
586         else:
587             lock_label = "Lock Frame"
588         row.prop(gpl, "lock_frame", text=lock_label, icon='UNLOCKED')
589         row.operator("gpencil.active_frame_delete", text="", icon='X')
590
591         layout.separator()
592
593         # Onion skinning
594         col = layout.column(align=True)
595         col.active = not gpl.lock
596
597         row = col.row()
598         row.prop(gpl, "use_onion_skinning")
599         row.prop(gpl, "use_ghost_custom_colors", text="", icon='COLOR')
600
601         split = col.split(percentage=0.5)
602         split.active = gpl.use_onion_skinning
603
604         # - Before Frames
605         sub = split.column(align=True)
606         row = sub.row(align=True)
607         row.active = gpl.use_ghost_custom_colors
608         row.prop(gpl, "before_color", text="")
609         sub.prop(gpl, "ghost_before_range", text="Before")
610
611         # - After Frames
612         sub = split.column(align=True)
613         row = sub.row(align=True)
614         row.active = gpl.use_ghost_custom_colors
615         row.prop(gpl, "after_color", text="")
616         sub.prop(gpl, "ghost_after_range", text="After")
617
618
619 class GreasePencilToolsPanel:
620     # subclass must set
621     # bl_space_type = 'IMAGE_EDITOR'
622     # bl_options = {'DEFAULT_CLOSED'}
623     bl_label = "Grease Pencil Settings"
624     bl_region_type = 'UI'
625
626     @classmethod
627     def poll(cls, context):
628         return (context.gpencil_data is not None)
629
630     @staticmethod
631     def draw(self, context):
632         layout = self.layout
633
634         # gpd_owner = context.gpencil_data_owner
635         gpd = context.gpencil_data
636
637         layout.prop(gpd, "use_stroke_edit_mode", text="Enable Editing", icon='EDIT', toggle=True)
638
639         layout.separator()
640
641         layout.label("Proportional Edit:")
642         row = layout.row()
643         row.prop(context.tool_settings, "proportional_edit", text="")
644         row.prop(context.tool_settings, "proportional_edit_falloff", text="")
645
646         layout.separator()
647         layout.separator()
648
649         gpencil_stroke_placement_settings(context, layout)