GPencil: Added entry for the "Select Strokes Using Active COlor" operator to the...
[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_editor"
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         is_3d_view = context.space_data.type == 'VIEW_3D'
67         is_clip_editor = context.space_data.type == 'CLIP_EDITOR'
68
69         col = layout.column(align=True)
70
71         col.label(text="Draw:")
72         row = col.row(align=True)
73         row.operator("gpencil.draw", icon='GREASEPENCIL', text="Draw").mode = 'DRAW'
74         row.operator("gpencil.draw", icon='FORCE_CURVE', text="Erase").mode = 'ERASER'  # XXX: Needs a dedicated icon
75
76         row = col.row(align=True)
77         row.operator("gpencil.draw", icon='LINE_DATA', text="Line").mode = 'DRAW_STRAIGHT'
78         row.operator("gpencil.draw", icon='MESH_DATA', text="Poly").mode = 'DRAW_POLY'
79
80         sub = col.column(align=True)
81         sub.prop(context.tool_settings, "use_gpencil_additive_drawing", text="Additive Drawing")
82         sub.prop(context.tool_settings, "use_gpencil_continuous_drawing", text="Continuous Drawing")
83         sub.prop(context.tool_settings, "use_gpencil_draw_onback", text="Draw on Back")
84
85         col.separator()
86         col.separator()
87
88         if context.space_data.type in {'VIEW_3D', 'CLIP_EDITOR'}:
89             col.separator()
90             col.label("Data Source:")
91             row = col.row(align=True)
92             if is_3d_view:
93                 row.prop(context.tool_settings, "grease_pencil_source", expand=True)
94             elif is_clip_editor:
95                 row.prop(context.space_data, "grease_pencil_source", expand=True)
96
97         col.separator()
98         col.separator()
99
100         gpencil_stroke_placement_settings(context, col)
101
102         gpd = context.gpencil_data
103
104         if gpd and not is_3d_view:
105             layout.separator()
106             layout.separator()
107
108             col = layout.column(align=True)
109             col.prop(gpd, "use_stroke_edit_mode", text="Enable Editing", icon='EDIT', toggle=True)
110
111         if is_3d_view:
112             col.separator()
113             col.separator()
114
115             col.label(text="Tools:")
116             col.operator_menu_enum("gpencil.convert", text="Convert to Geometry...", property="type")
117             col.operator("view3d.ruler")
118
119
120 class GreasePencilStrokeEditPanel:
121     # subclass must set
122     # bl_space_type = 'IMAGE_EDITOR'
123     bl_label = "Edit Strokes"
124     bl_category = "Grease Pencil"
125     bl_region_type = 'TOOLS'
126     bl_options = {'DEFAULT_CLOSED'}
127
128     @classmethod
129     def poll(cls, context):
130         if context.gpencil_data is None:
131             return False
132
133         gpd = context.gpencil_data
134         return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode)
135
136     @staticmethod
137     def draw(self, context):
138         layout = self.layout
139
140         is_3d_view = context.space_data.type == 'VIEW_3D'
141
142         if not is_3d_view:
143             layout.label(text="Select:")
144             col = layout.column(align=True)
145             col.operator("gpencil.select_all", text="Select All")
146             col.operator("gpencil.select_border")
147             col.operator("gpencil.select_circle")
148
149             layout.separator()
150
151             col = layout.column(align=True)
152             col.operator("gpencil.select_linked")
153             col.operator("gpencil.select_more")
154             col.operator("gpencil.select_less")
155             col.operator("gpencil.palettecolor_select")
156
157         layout.label(text="Edit:")
158         row = layout.row(align=True)
159         row.operator("gpencil.copy", text="Copy")
160         row.operator("gpencil.paste", text="Paste").type = 'COPY'
161         row.operator("gpencil.paste", text="Paste & Merge").type = 'MERGE'
162
163         col = layout.column(align=True)
164         col.operator("gpencil.delete")
165         col.operator("gpencil.duplicate_move", text="Duplicate")
166         if is_3d_view:
167             col.operator("gpencil.stroke_cyclical_set", text="Toggle Cyclic").type = 'TOGGLE'
168
169         layout.separator()
170
171         if not is_3d_view:
172             col = layout.column(align=True)
173             col.operator("transform.translate")                # icon='MAN_TRANS'
174             col.operator("transform.rotate")                   # icon='MAN_ROT'
175             col.operator("transform.resize", text="Scale")     # icon='MAN_SCALE'
176
177             layout.separator()
178
179         col = layout.column(align=True)
180         col.operator("transform.bend", text="Bend")
181         col.operator("transform.mirror", text="Mirror")
182         col.operator("transform.shear", text="Shear")
183         col.operator("transform.tosphere", text="To Sphere")
184
185         layout.separator()
186         col = layout.column(align=True)
187         col.operator_menu_enum("gpencil.stroke_arrange", text="Arrange Strokes...", property="direction")
188         col.operator("gpencil.stroke_change_color", text="Move to Color")
189
190         layout.separator()
191         col = layout.column(align=True)
192         col.operator("gpencil.stroke_join", text="Join").type = 'JOIN'
193         col.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY'
194         col.operator("gpencil.stroke_flip", text="Flip Direction")
195
196         gpd = context.gpencil_data
197         if gpd:
198             col.prop(gpd, "show_stroke_direction", text="Show Directions")
199
200
201 class GreasePencilBrushPanel:
202     # subclass must set
203     # bl_space_type = 'IMAGE_EDITOR'
204     bl_label = "Drawing Brushes"
205     bl_category = "Grease Pencil"
206     bl_region_type = 'TOOLS'
207
208     @staticmethod
209     def draw(self, context):
210         layout = self.layout
211
212         row = layout.row()
213         col = row.column()
214         ts = context.scene.tool_settings
215         if len(ts.gpencil_brushes) >= 2:
216             brows = 3
217         else:
218             brows = 2
219         col.template_list("GPENCIL_UL_brush", "", ts, "gpencil_brushes", ts.gpencil_brushes, "active_index", rows=brows)
220
221         col = row.column()
222
223         sub = col.column(align=True)
224         sub.operator("gpencil.brush_add", icon='ZOOMIN', text="")
225         sub.operator("gpencil.brush_remove", icon='ZOOMOUT', text="")
226         sub.menu("GPENCIL_MT_brush_specials", icon='DOWNARROW_HLT', text="")
227         brush = context.active_gpencil_brush
228         if brush:
229             if len(ts.gpencil_brushes) > 1:
230                 col.separator()
231                 sub = col.column(align=True)
232                 sub.operator("gpencil.brush_move", icon='TRIA_UP', text="").type = 'UP'
233                 sub.operator("gpencil.brush_move", icon='TRIA_DOWN', text="").type = 'DOWN'
234
235         # Brush details
236         if brush is not None:
237             row = layout.row()
238             row.prop(brush, "line_width")
239             row = layout.row(align=True)
240             row.prop(brush, "use_random_pressure", text='', icon='RNDCURVE')
241             row.prop(brush, "pen_sensitivity_factor", slider=True)
242             row.prop(brush, "use_pressure", text='', icon='STYLUS_PRESSURE')
243             row = layout.row(align=True)
244             row.prop(brush, "use_random_strength", text='', icon='RNDCURVE')
245             row.prop(brush, "strength", slider=True)
246             row.prop(brush, "use_strength_pressure", text='', icon='STYLUS_PRESSURE')
247             row = layout.row(align=True)
248             row.prop(brush, "random_press", slider=True)
249
250             row = layout.row(align=True)
251             row.prop(brush, "jitter", slider=True)
252             row.prop(brush, "use_jitter_pressure", text='', icon='STYLUS_PRESSURE')
253             row = layout.row()
254             row.prop(brush, "angle", slider=True)
255             row.prop(brush, "angle_factor", text="Factor", slider=True)
256
257             box = layout.box()
258             col = box.column(align=True)
259             col.label(text="Stroke Quality:")
260             col.prop(brush, "pen_smooth_factor")
261             col.prop(brush, "pen_smooth_steps")
262             col.separator()
263             row = col.row(align=False)
264             row.prop(brush, "pen_subdivision_steps")
265             row.prop(brush, "random_subdiv", text='Randomness', slider=True)
266
267
268 class GreasePencilStrokeSculptPanel:
269     # subclass must set
270     # bl_space_type = 'IMAGE_EDITOR'
271     bl_label = "Sculpt Strokes"
272     bl_category = "Grease Pencil"
273     bl_region_type = 'TOOLS'
274
275     @classmethod
276     def poll(cls, context):
277         if context.gpencil_data is None:
278             return False
279
280         gpd = context.gpencil_data
281         return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode)
282
283     @staticmethod
284     def draw(self, context):
285         layout = self.layout
286
287         settings = context.tool_settings.gpencil_sculpt
288         tool = settings.tool
289         brush = settings.brush
290
291         layout.column().prop(settings, "tool")
292
293         col = layout.column()
294         col.prop(brush, "size", slider=True)
295         row = col.row(align=True)
296         row.prop(brush, "strength", slider=True)
297         row.prop(brush, "use_pressure_strength", text="")
298         col.prop(brush, "use_falloff")
299         if tool in {'SMOOTH', 'RANDOMIZE'}:
300             row = layout.row(align=True)
301             row.prop(settings, "affect_position", text="Position", icon='MESH_DATA', toggle=True)
302             row.prop(settings, "affect_strength", text="Strength", icon='COLOR', toggle=True)
303             row.prop(settings, "affect_thickness", text="Thickness", icon='LINE_DATA', toggle=True)
304
305         layout.separator()
306
307         if tool == 'THICKNESS':
308             layout.row().prop(brush, "direction", expand=True)
309         elif tool == 'PINCH':
310             row = layout.row(align=True)
311             row.prop_enum(brush, "direction", 'ADD', text="Pinch")
312             row.prop_enum(brush, "direction", 'SUBTRACT', text="Inflate")
313         elif settings.tool == 'TWIST':
314             row = layout.row(align=True)
315             row.prop_enum(brush, "direction", 'SUBTRACT', text="CW")
316             row.prop_enum(brush, "direction", 'ADD', text="CCW")
317
318         row = layout.row(align=True)
319         row.prop(settings, "use_select_mask")
320         row = layout.row(align=True)
321         row.prop(settings, "selection_alpha", slider=True)
322
323         if tool == 'SMOOTH':
324             layout.prop(brush, "affect_pressure")
325
326
327 class GreasePencilBrushCurvesPanel:
328     # subclass must set
329     # bl_space_type = 'IMAGE_EDITOR'
330     bl_label = "Brush Curves"
331     bl_category = "Grease Pencil"
332     bl_region_type = 'TOOLS'
333     bl_options = {'DEFAULT_CLOSED'}
334
335     @classmethod
336     def poll(cls, context):
337         if context.active_gpencil_brush is None:
338             return False
339
340         brush = context.active_gpencil_brush
341         return bool(brush)
342
343     @staticmethod
344     def draw(self, context):
345         layout = self.layout
346         brush = context.active_gpencil_brush
347         # Brush
348         layout.label("Sensitivity")
349         box = layout.box()
350         box.template_curve_mapping(brush, "curve_sensitivity", brush=True)
351
352         layout.label("Strength")
353         box = layout.box()
354         box.template_curve_mapping(brush, "curve_strength", brush=True)
355
356         layout.label("Jitter")
357         box = layout.box()
358         box.template_curve_mapping(brush, "curve_jitter", brush=True)
359
360
361 ###############################
362
363 class GPENCIL_PIE_tool_palette(Menu):
364     """A pie menu for quick access to Grease Pencil tools"""
365     bl_label = "Grease Pencil Tools"
366
367     def draw(self, context):
368         layout = self.layout
369
370         pie = layout.menu_pie()
371         gpd = context.gpencil_data
372
373         # W - Drawing Types
374         col = pie.column()
375         col.operator("gpencil.draw", text="Draw", icon='GREASEPENCIL').mode = 'DRAW'
376         col.operator("gpencil.draw", text="Straight Lines", icon='LINE_DATA').mode = 'DRAW_STRAIGHT'
377         col.operator("gpencil.draw", text="Poly", icon='MESH_DATA').mode = 'DRAW_POLY'
378
379         # E - Eraser
380         # XXX: needs a dedicated icon...
381         col = pie.column()
382         col.operator("gpencil.draw", text="Eraser", icon='FORCE_CURVE').mode = 'ERASER'
383
384         # E - "Settings" Palette is included here too, since it needs to be in a stable position...
385         if gpd and gpd.layers.active:
386             col.separator()
387             col.operator("wm.call_menu_pie", text="Settings...", icon='SCRIPTWIN').name = "GPENCIL_PIE_settings_palette"
388
389         # Editing tools
390         if gpd:
391             if gpd.use_stroke_edit_mode and context.editable_gpencil_strokes:
392                 # S - Exit Edit Mode
393                 pie.operator("gpencil.editmode_toggle", text="Exit Edit Mode", icon='EDIT')
394
395                 # N - Transforms
396                 col = pie.column()
397                 row = col.row(align=True)
398                 row.operator("transform.translate", icon='MAN_TRANS')
399                 row.operator("transform.rotate", icon='MAN_ROT')
400                 row.operator("transform.resize", text="Scale", icon='MAN_SCALE')
401                 row = col.row(align=True)
402                 row.label("Proportional Edit:")
403                 row.prop(context.tool_settings, "proportional_edit", text="", icon_only=True)
404                 row.prop(context.tool_settings, "proportional_edit_falloff", text="", icon_only=True)
405
406                 # NW - Select (Non-Modal)
407                 col = pie.column()
408                 col.operator("gpencil.select_all", text="Select All", icon='PARTICLE_POINT')
409                 col.operator("gpencil.select_all", text="Select Inverse", icon='BLANK1')
410                 col.operator("gpencil.select_linked", text="Select Linked", icon='LINKED')
411                 col.operator("gpencil.palettecolor_select", text="Select Color", icon='COLOR')
412
413                 # NE - Select (Modal)
414                 col = pie.column()
415                 col.operator("gpencil.select_border", text="Border Select", icon='BORDER_RECT')
416                 col.operator("gpencil.select_circle", text="Circle Select", icon='META_EMPTY')
417                 col.operator("gpencil.select_lasso", text="Lasso Select", icon='BORDER_LASSO')
418
419                 # SW - Edit Tools
420                 col = pie.column()
421                 col.operator("gpencil.duplicate_move", icon='PARTICLE_PATH', text="Duplicate")
422                 col.operator("gpencil.delete", icon='X', text="Delete...")
423
424                 # SE - More Tools
425                 pie.operator("wm.call_menu_pie", text="More...").name = "GPENCIL_PIE_tools_more"
426             else:
427                 # Toggle Edit Mode
428                 pie.operator("gpencil.editmode_toggle", text="Enable Stroke Editing", icon='EDIT')
429
430
431 class GPENCIL_PIE_settings_palette(Menu):
432     """A pie menu for quick access to Grease Pencil settings"""
433     bl_label = "Grease Pencil Settings"
434
435     @classmethod
436     def poll(cls, context):
437         return bool(context.gpencil_data and context.active_gpencil_layer)
438
439     def draw(self, context):
440         layout = self.layout
441
442         pie = layout.menu_pie()
443         # gpd = context.gpencil_data
444         gpl = context.active_gpencil_layer
445         palcolor = context.active_gpencil_palettecolor
446         brush = context.active_gpencil_brush
447
448         # W - Stroke draw settings
449         col = pie.column(align=True)
450         if palcolor is not None:
451             col.label(text="Stroke")
452             col.prop(palcolor, "color", text="")
453             col.prop(palcolor, "alpha", text="", slider=True)
454
455         # E - Fill draw settings
456         col = pie.column(align=True)
457         if palcolor is not None:
458             col.label(text="Fill")
459             col.prop(palcolor, "fill_color", text="")
460             col.prop(palcolor, "fill_alpha", text="", slider=True)
461
462         # S Brush settings
463         col = pie.column()
464         col.label("Active Brush:      ")
465
466         row = col.row()
467         row.operator_context = 'EXEC_REGION_WIN'
468         row.operator_menu_enum("gpencil.brush_change", "brush", text="", icon='BRUSH_DATA')
469         row.prop(brush, "name", text="")
470
471         col.prop(brush, "line_width", slider=True)
472         row = col.row(align=True)
473         row.prop(brush, "use_random_pressure", text='', icon='RNDCURVE')
474         row.prop(brush, "pen_sensitivity_factor", slider=True)
475         row.prop(brush, "use_pressure", text='', icon='STYLUS_PRESSURE')
476         row = col.row(align=True)
477         row.prop(brush, "use_random_strength", text='', icon='RNDCURVE')
478         row.prop(brush, "strength", slider=True)
479         row.prop(brush, "use_strength_pressure", text='', icon='STYLUS_PRESSURE')
480         row = col.row(align=True)
481         row.prop(brush, "jitter", slider=True)
482         row.prop(brush, "use_jitter_pressure", text='', icon='STYLUS_PRESSURE')
483         row = col.row()
484         row.prop(brush, "angle", slider=True)
485         row.prop(brush, "angle_factor", text="Factor", slider=True)
486
487         # N - Active Layer
488         col = pie.column()
489         col.label("Active Layer:      ")
490
491         row = col.row()
492         row.operator_context = 'EXEC_REGION_WIN'
493         row.operator_menu_enum("gpencil.layer_change", "layer", text="", icon='GREASEPENCIL')
494         row.prop(gpl, "info", text="")
495         row.operator("gpencil.layer_remove", text="", icon='X')
496
497         row = col.row()
498         row.prop(gpl, "lock")
499         row.prop(gpl, "hide")
500         col.prop(gpl, "use_onion_skinning")
501
502         # NW - Move stroke Down
503         col = pie.column(align=True)
504         col.label("Arrange Strokes")
505         col.operator("gpencil.stroke_arrange", text="Send to Back").direction = 'BOTTOM'
506         col.operator("gpencil.stroke_arrange", text="Send Backward").direction = 'DOWN'
507
508         # NE - Move stroke Up
509         col = pie.column(align=True)
510         col.label("Arrange Strokes")
511         col.operator("gpencil.stroke_arrange", text="Bring to Front").direction = 'TOP'
512         col.operator("gpencil.stroke_arrange", text="Bring Forward").direction = 'UP'
513
514         # SW - Move stroke to color
515         col = pie.column(align=True)
516         col.operator("gpencil.stroke_change_color", text="Move to Color")
517
518         # SE - Join strokes
519         col = pie.column(align=True)
520         col.label("Join Strokes")
521         row = col.row()
522         row.operator("gpencil.stroke_join", text="Join").type = 'JOIN'
523         row.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY'
524         col.operator("gpencil.stroke_flip", text="Flip direction")
525
526         gpd = context.gpencil_data
527         if gpd:
528             col.prop(gpd, "show_stroke_direction", text="Show drawing direction")
529
530
531 class GPENCIL_PIE_tools_more(Menu):
532     """A pie menu for accessing more Grease Pencil tools"""
533     bl_label = "More Grease Pencil Tools"
534
535     @classmethod
536     def poll(cls, context):
537         gpd = context.gpencil_data
538         return bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes)
539
540     def draw(self, context):
541         layout = self.layout
542
543         pie = layout.menu_pie()
544         # gpd = context.gpencil_data
545
546         col = pie.column(align=True)
547         col.operator("gpencil.copy", icon='COPYDOWN', text="Copy")
548         col.operator("gpencil.paste", icon='PASTEDOWN', text="Paste")
549
550         col = pie.column(align=True)
551         col.operator("gpencil.select_more", icon='ZOOMIN')
552         col.operator("gpencil.select_less", icon='ZOOMOUT')
553
554         pie.operator("transform.mirror", icon='MOD_MIRROR')
555         pie.operator("transform.bend", icon='MOD_SIMPLEDEFORM')
556         pie.operator("transform.shear", icon='MOD_TRIANGULATE')
557         pie.operator("transform.tosphere", icon='MOD_MULTIRES')
558
559         pie.operator("gpencil.convert", icon='OUTLINER_OB_CURVE', text="Convert...")
560         pie.operator("wm.call_menu_pie", text="Back to Main Palette...").name = "GPENCIL_PIE_tool_palette"
561
562
563 class GPENCIL_PIE_sculpt(Menu):
564     """A pie menu for accessing Grease Pencil stroke sculpting settings"""
565     bl_label = "Grease Pencil Sculpt"
566
567     @classmethod
568     def poll(cls, context):
569         gpd = context.gpencil_data
570         return bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes)
571
572     def draw(self, context):
573         layout = self.layout
574
575         pie = layout.menu_pie()
576
577         settings = context.tool_settings.gpencil_sculpt
578         brush = settings.brush
579
580         # W - Launch Sculpt Mode
581         col = pie.column()
582         # col.label("Tool:")
583         col.prop(settings, "tool", text="")
584         col.operator("gpencil.brush_paint", text="Sculpt", icon='SCULPTMODE_HLT')
585
586         # E - Common Settings
587         col = pie.column(align=True)
588         col.prop(brush, "size", slider=True)
589         row = col.row(align=True)
590         row.prop(brush, "strength", slider=True)
591         # row.prop(brush, "use_pressure_strength", text="", icon_only=True)
592         col.prop(brush, "use_falloff")
593         if settings.tool in {'SMOOTH', 'RANDOMIZE'}:
594             row = col.row(align=True)
595             row.prop(settings, "affect_position", text="Position", icon='MESH_DATA', toggle=True)
596             row.prop(settings, "affect_strength", text="Strength", icon='COLOR', toggle=True)
597             row.prop(settings, "affect_thickness", text="Thickness", icon='LINE_DATA', toggle=True)
598
599         # S - Change Brush Type Shortcuts
600         row = pie.row()
601         row.prop_enum(settings, "tool", value='GRAB')
602         row.prop_enum(settings, "tool", value='PUSH')
603         row.prop_enum(settings, "tool", value='CLONE')
604
605         # N - Change Brush Type Shortcuts
606         row = pie.row()
607         row.prop_enum(settings, "tool", value='SMOOTH')
608         row.prop_enum(settings, "tool", value='THICKNESS')
609         row.prop_enum(settings, "tool", value='STRENGTH')
610         row.prop_enum(settings, "tool", value='RANDOMIZE')
611
612
613 ###############################
614
615
616 class GPENCIL_MT_snap(Menu):
617     bl_label = "Snap"
618
619     def draw(self, context):
620         layout = self.layout
621
622         layout.operator("gpencil.snap_to_grid", text="Selection to Grid")
623         layout.operator("gpencil.snap_to_cursor", text="Selection to Cursor").use_offset = False
624         layout.operator("gpencil.snap_to_cursor", text="Selection to Cursor (Offset)").use_offset = True
625
626         layout.separator()
627
628         layout.operator("gpencil.snap_cursor_to_selected", text="Cursor to Selected")
629         layout.operator("view3d.snap_cursor_to_center", text="Cursor to Center")
630         layout.operator("view3d.snap_cursor_to_grid", text="Cursor to Grid")
631
632
633 ###############################
634
635
636 class GPENCIL_UL_brush(UIList):
637     def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
638         # assert(isinstance(item, bpy.types.GPencilBrush)
639         brush = item
640
641         if self.layout_type in {'DEFAULT', 'COMPACT'}:
642             row = layout.row(align=True)
643             row.prop(brush, "name", text="", emboss=False, icon='BRUSH_DATA')
644         elif self.layout_type == 'GRID':
645             layout.alignment = 'CENTER'
646             layout.label(text="", icon_value=icon)
647
648
649 class GPENCIL_UL_palettecolor(UIList):
650     def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
651         # assert(isinstance(item, bpy.types.PaletteColor)
652         palcolor = item
653
654         if self.layout_type in {'DEFAULT', 'COMPACT'}:
655             if palcolor.lock:
656                 layout.active = False
657
658             split = layout.split(percentage=0.25)
659             row = split.row(align=True)
660             row.prop(palcolor, "color", text="", emboss=palcolor.is_stroke_visible)
661             row.prop(palcolor, "fill_color", text="", emboss=palcolor.is_fill_visible)
662             split.prop(palcolor, "name", text="", emboss=False)
663
664             row = layout.row(align=True)
665             row.prop(palcolor, "lock", text="", emboss=False)
666             row.prop(palcolor, "hide", text="", emboss=False)
667             if palcolor.ghost is True:
668                 icon = 'GHOST_DISABLED'
669             else:
670                 icon = 'GHOST_ENABLED'
671             row.prop(palcolor, "ghost", text="", icon=icon, emboss=False)
672
673         elif self.layout_type == 'GRID':
674             layout.alignment = 'CENTER'
675             layout.label(text="", icon_value=icon)
676
677
678 class GPENCIL_UL_layer(UIList):
679     def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
680         # assert(isinstance(item, bpy.types.GPencilLayer)
681         gpl = item
682
683         if self.layout_type in {'DEFAULT', 'COMPACT'}:
684             if gpl.lock:
685                 layout.active = False
686
687             row = layout.row(align=True)
688             if gpl.is_parented:
689                 icon = 'BONE_DATA'
690             else:
691                 icon = 'BLANK1'
692
693             row.label(text="", icon=icon)
694             row.prop(gpl, "info", text="", emboss=False)
695
696             row = layout.row(align=True)
697             row.prop(gpl, "lock", text="", emboss=False)
698             row.prop(gpl, "hide", text="", emboss=False)
699             row.prop(gpl, "unlock_color", text="", emboss=False)
700         elif self.layout_type == 'GRID':
701             layout.alignment = 'CENTER'
702             layout.label(text="", icon_value=icon)
703
704
705 class GPENCIL_MT_layer_specials(Menu):
706     bl_label = "Layer"
707
708     def draw(self, context):
709         layout = self.layout
710
711         layout.operator("gpencil.layer_duplicate", icon='COPY_ID')  # XXX: needs a dedicated icon
712
713         layout.separator()
714
715         layout.operator("gpencil.reveal", icon='RESTRICT_VIEW_OFF', text="Show All")
716         layout.operator("gpencil.hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True
717
718         layout.separator()
719
720         layout.operator("gpencil.lock_all", icon='LOCKED', text="Lock All")
721         layout.operator("gpencil.unlock_all", icon='UNLOCKED', text="UnLock All")
722
723         layout.separator()
724
725         layout.operator("gpencil.layer_merge", icon='NLA', text="Merge Down")
726
727
728 class GPENCIL_MT_brush_specials(Menu):
729     bl_label = "Layer"
730
731     def draw(self, context):
732         layout = self.layout
733         layout.operator("gpencil.brush_copy", icon='PASTEDOWN', text="Copy current drawing brush")
734         layout.operator("gpencil.brush_presets_create", icon='HELP', text="Create a set of predefined brushes")
735
736
737 class GPENCIL_MT_palettecolor_specials(Menu):
738     bl_label = "Layer"
739
740     def draw(self, context):
741         layout = self.layout
742
743         layout.operator("gpencil.palettecolor_reveal", icon='RESTRICT_VIEW_OFF', text="Show All")
744         layout.operator("gpencil.palettecolor_hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True
745
746         layout.separator()
747
748         layout.operator("gpencil.palettecolor_lock_all", icon='LOCKED', text="Lock All")
749         layout.operator("gpencil.palettecolor_unlock_all", icon='UNLOCKED', text="UnLock All")
750         layout.operator("gpencil.palettecolor_copy", icon='PASTEDOWN', text="Copy Color")
751
752         layout.separator()
753
754         layout.operator("gpencil.palettecolor_select", icon='COLOR', text="Select Strokes")
755
756
757 class GreasePencilDataPanel:
758     # subclass must set
759     # bl_space_type = 'IMAGE_EDITOR'
760     bl_label = "Grease Pencil Layers"
761     bl_region_type = 'UI'
762
763     @staticmethod
764     def draw_header(self, context):
765         self.layout.prop(context.space_data, "show_grease_pencil", text="")
766
767     @staticmethod
768     def draw(self, context):
769         layout = self.layout
770
771         # owner of Grease Pencil data
772         gpd_owner = context.gpencil_data_owner
773         gpd = context.gpencil_data
774
775         # Owner Selector
776         if context.space_data.type == 'VIEW_3D':
777             layout.prop(context.tool_settings, "grease_pencil_source", expand=True)
778         elif context.space_data.type == 'CLIP_EDITOR':
779             layout.prop(context.space_data, "grease_pencil_source", expand=True)
780
781         # Grease Pencil data selector
782         layout.template_ID(gpd_owner, "grease_pencil", new="gpencil.data_add", unlink="gpencil.data_unlink")
783
784         # Grease Pencil data...
785         if (gpd is None) or (not gpd.layers):
786             layout.operator("gpencil.layer_add", text="New Layer")
787         else:
788             self.draw_layers(context, layout, gpd)
789
790     def draw_layers(self, context, layout, gpd):
791         row = layout.row()
792
793         col = row.column()
794         if len(gpd.layers) >= 2:
795             layer_rows = 5
796         else:
797             layer_rows = 2
798         col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index", rows=layer_rows)
799
800         col = row.column()
801
802         sub = col.column(align=True)
803         sub.operator("gpencil.layer_add", icon='ZOOMIN', text="")
804         sub.operator("gpencil.layer_remove", icon='ZOOMOUT', text="")
805
806         gpl = context.active_gpencil_layer
807         if gpl:
808             sub.menu("GPENCIL_MT_layer_specials", icon='DOWNARROW_HLT', text="")
809
810             if len(gpd.layers) > 1:
811                 col.separator()
812
813                 sub = col.column(align=True)
814                 sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP'
815                 sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN'
816
817                 col.separator()
818
819                 sub = col.column(align=True)
820                 sub.operator("gpencil.layer_isolate", icon='LOCKED', text="").affect_visibility = False
821                 sub.operator("gpencil.layer_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True
822
823         if gpl:
824             self.draw_layer(context, layout, gpl)
825
826     def draw_layer(self, context, layout, gpl):
827         row = layout.row(align=True)
828         row.prop(gpl, "opacity", text="Opacity", slider=True)
829
830         # Layer options
831         split = layout.split(percentage=0.5)
832         split.active = not gpl.lock
833         split.prop(gpl, "show_x_ray")
834         split.prop(gpl, "show_points")
835
836         # Offsets + Parenting (where available)
837         split = layout.split(percentage=0.5)
838         split.active = not gpl.lock
839
840         # Offsets - Color Tint
841         col = split.column()
842         subcol = col.column(align=True)
843         subcol.label("Tint")
844         subcol.prop(gpl, "tint_color", text="")
845         subcol.prop(gpl, "tint_factor", text="Factor", slider=True)
846
847         # Offsets - Thickness
848         row = col.row(align=True)
849         row.prop(gpl, "line_change", text="Thickness Change", slider=True)
850         row.operator("gpencil.stroke_apply_thickness", icon='STYLUS_PRESSURE', text="")
851
852         # Parenting 
853         if context.space_data.type == 'VIEW_3D':
854             col = split.column(align=True)
855             col.label(text="Parent:")
856             col.prop(gpl, "parent", text="")
857
858             sub = col.column()
859             sub.prop(gpl, "parent_type", text="")
860             parent = gpl.parent
861             if parent and gpl.parent_type == 'BONE' and parent.type == 'ARMATURE':
862                 sub.prop_search(gpl, "parent_bone", parent.data, "bones", text="")
863
864         layout.separator()
865
866         # Full-Row - Frame Locking (and Delete Frame)
867         row = layout.row(align=True)
868         row.active = not gpl.lock
869
870         if gpl.active_frame:
871             lock_status = "Locked" if gpl.lock_frame else "Unlocked"
872             lock_label = "Frame: %d (%s)" % (gpl.active_frame.frame_number, lock_status)
873         else:
874             lock_label = "Lock Frame"
875         row.prop(gpl, "lock_frame", text=lock_label, icon='UNLOCKED')
876         row.operator("gpencil.active_frame_delete", text="", icon='X')
877
878         layout.separator()
879
880         # Onion skinning
881         col = layout.column(align=True)
882         col.active = not gpl.lock
883
884         row = col.row()
885         row.prop(gpl, "use_onion_skinning")
886         row.prop(gpl, "use_ghost_custom_colors", text="", icon='COLOR')
887
888         split = col.split(percentage=0.5)
889         split.active = gpl.use_onion_skinning
890
891         # - Before Frames
892         sub = split.column(align=True)
893         row = sub.row(align=True)
894         row.active = gpl.use_ghost_custom_colors
895         row.prop(gpl, "before_color", text="")
896         sub.prop(gpl, "ghost_before_range", text="Before")
897
898         # - After Frames
899         sub = split.column(align=True)
900         row = sub.row(align=True)
901         row.active = gpl.use_ghost_custom_colors
902         row.prop(gpl, "after_color", text="")
903         sub.prop(gpl, "ghost_after_range", text="After")
904
905
906 class GreasePencilPaletteColorPanel:
907     # subclass must set
908     bl_label = "Grease Pencil Colors"
909     bl_region_type = 'UI'
910
911     @classmethod
912     def poll(cls, context):
913         if context.gpencil_data is None:
914             return False
915
916         gpd = context.gpencil_data
917         return bool(gpd.layers.active)
918
919     @staticmethod
920     def draw(self, context):
921         layout = self.layout
922         palette = context.active_gpencil_palette
923
924         if palette:
925             row = layout.row(align=True)
926             row.operator_context = 'EXEC_REGION_WIN'
927             row.operator_menu_enum("gpencil.palette_change", "palette", text="", icon='COLOR')
928             row.prop(palette, "name", text="")
929             row.operator("gpencil.palette_add", icon='ZOOMIN', text="")
930             row.operator("gpencil.palette_remove", icon='X', text="")
931
932             # Palette colors
933             row = layout.row()
934             col = row.column()
935             if len(palette.colors) >= 2:
936                 color_rows = 5
937             else:
938                 color_rows = 2
939             col.template_list("GPENCIL_UL_palettecolor", "", palette, "colors", palette.colors, "active_index",
940                               rows=color_rows)
941
942             col = row.column()
943
944             sub = col.column(align=True)
945             sub.operator("gpencil.palettecolor_add", icon='ZOOMIN', text="")
946             sub.operator("gpencil.palettecolor_remove", icon='ZOOMOUT', text="")
947
948             palcol = context.active_gpencil_palettecolor
949             if palcol:
950                 sub.menu("GPENCIL_MT_palettecolor_specials", icon='DOWNARROW_HLT', text="")
951
952             if len(palette.colors) > 1:
953                 col.separator()
954
955                 sub = col.column(align=True)
956                 sub.operator("gpencil.palettecolor_move", icon='TRIA_UP', text="").direction = 'UP'
957                 sub.operator("gpencil.palettecolor_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
958
959                 col.separator()
960                 sub = col.column(align=True)
961                 sub.operator("gpencil.palettecolor_isolate", icon='LOCKED', text="").affect_visibility = False
962                 sub.operator("gpencil.palettecolor_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True
963                 sub.operator("gpencil.stroke_lock_color", icon='BORDER_RECT', text="")
964                 sub.operator("gpencil.palette_lock_layer", icon='COLOR', text="")
965
966             pcolor = palette.colors.active
967             if pcolor:
968                 self.draw_palettecolors(layout, pcolor)
969
970     # ----------------------------------------------
971     # Draw palette colors
972     # ----------------------------------------------
973     def draw_palettecolors(self, layout, pcolor):
974         # color settings
975         split = layout.split(percentage=0.5)
976         split.active = not pcolor.lock
977
978         # Column 1 - Stroke
979         col = split.column(align=True)
980         col.active = not pcolor.lock
981         col.label(text="Stroke:")
982         col.prop(pcolor, "color", text="")
983         col.prop(pcolor, "alpha", slider=True)
984
985         # Column 2 - Fill
986         col = split.column(align=True)
987         col.active = not pcolor.lock
988         col.label(text="Fill:")
989         col.prop(pcolor, "fill_color", text="")
990         col.prop(pcolor, "fill_alpha", text="Opacity", slider=True)
991
992         # Options
993         split = layout.split(percentage=0.5)
994         split.active = not pcolor.lock
995
996         col = split.column(align=True)
997         col.active = not pcolor.lock
998         col.prop(pcolor, "use_volumetric_strokes")
999         col = split.column(align=True)
1000         col.active = not pcolor.lock
1001         col.prop(pcolor, "use_hq_fill")
1002
1003
1004 class GreasePencilToolsPanel:
1005     # subclass must set
1006     # bl_space_type = 'IMAGE_EDITOR'
1007     # bl_options = {'DEFAULT_CLOSED'}
1008     bl_label = "Grease Pencil Settings"
1009     bl_region_type = 'UI'
1010
1011     @classmethod
1012     def poll(cls, context):
1013         return (context.gpencil_data is not None)
1014
1015     @staticmethod
1016     def draw(self, context):
1017         layout = self.layout
1018
1019         # gpd_owner = context.gpencil_data_owner
1020         gpd = context.gpencil_data
1021
1022         layout.prop(gpd, "use_stroke_edit_mode", text="Enable Editing", icon='EDIT', toggle=True)
1023
1024         layout.separator()
1025
1026         layout.label("Proportional Edit:")
1027         row = layout.row()
1028         row.prop(context.tool_settings, "proportional_edit", text="")
1029         row.prop(context.tool_settings, "proportional_edit_falloff", text="")
1030
1031         layout.separator()
1032         layout.separator()
1033
1034         gpencil_stroke_placement_settings(context, layout)