GPencil UI: Color swatches for both Stroke and Fill colors are now shown
[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, 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         subcol = col.column(align=True)
134         subcol.active = edit_ok
135         subcol.operator("gpencil.delete", text="Delete")
136         subcol.operator("gpencil.duplicate_move", text="Duplicate")
137         subcol.operator("transform.mirror", text="Mirror").gpencil_strokes = True
138
139         col.separator()
140
141         subcol = col.column(align=True)
142         subcol.active = edit_ok
143         subcol.operator("transform.translate").gpencil_strokes = True   # icon='MAN_TRANS'
144         subcol.operator("transform.rotate").gpencil_strokes = True      # icon='MAN_ROT'
145         subcol.operator("transform.resize", text="Scale").gpencil_strokes = True      # icon='MAN_SCALE'
146
147         col.separator()
148
149         subcol = col.column(align=True)
150         subcol.active = edit_ok
151         subcol.operator("transform.bend", text="Bend").gpencil_strokes = True
152         subcol.operator("transform.shear", text="Shear").gpencil_strokes = True
153         subcol.operator("transform.tosphere", text="To Sphere").gpencil_strokes = True
154
155
156 ###############################
157
158 class GPENCIL_PIE_tool_palette(Menu):
159     """A pie menu for quick access to Grease Pencil tools"""
160     bl_label = "Grease Pencil Tools"
161
162     def draw(self, context):
163         layout = self.layout
164
165         pie = layout.menu_pie()
166         gpd = context.gpencil_data
167
168         # W - Drawing Types
169         col = pie.column()
170         col.operator("gpencil.draw", text="Draw", icon='GREASEPENCIL').mode = 'DRAW'
171         col.operator("gpencil.draw", text="Straight Lines", icon='LINE_DATA').mode = 'DRAW_STRAIGHT'
172         col.operator("gpencil.draw", text="Poly", icon='MESH_DATA').mode = 'DRAW_POLY'
173
174         # E - Eraser
175         # XXX: needs a dedicated icon...
176         col = pie.column()
177         col.operator("gpencil.draw", text="Eraser", icon='FORCE_CURVE').mode = 'ERASER'
178
179         # E - "Settings" Palette is included here too, since it needs to be in a stable position...
180         if gpd and gpd.layers.active:
181             col.separator()
182             col.operator("wm.call_menu_pie", text="Settings...", icon='SCRIPTWIN').name = "GPENCIL_PIE_settings_palette"
183
184         # Editing tools
185         if gpd:
186             if gpd.use_stroke_edit_mode and context.editable_gpencil_strokes:
187                 # S - Exit Edit Mode
188                 pie.prop(gpd, "use_stroke_edit_mode", text="Exit Edit Mode", icon='EDIT')
189
190                 # N - Transforms
191                 col = pie.column()
192                 row = col.row(align=True)
193                 row.operator("transform.translate", icon='MAN_TRANS').gpencil_strokes = True
194                 row.operator("transform.rotate",    icon='MAN_ROT').gpencil_strokes = True
195                 row.operator("transform.resize",    text="Scale", icon='MAN_SCALE').gpencil_strokes = True
196                 row = col.row(align=True)
197                 row.label("Proportional Edit:")
198                 row.prop(context.tool_settings, "proportional_edit", text="", icon_only=True)
199                 row.prop(context.tool_settings, "proportional_edit_falloff", text="", icon_only=True)
200
201                 # NW - Select (Non-Modal)
202                 col = pie.column()
203                 col.operator("gpencil.select_all", text="Select All", icon='PARTICLE_POINT')
204                 col.operator("gpencil.select_all", text="Select Inverse", icon='BLANK1')
205                 col.operator("gpencil.select_linked", text="Select Linked", icon='LINKED')
206
207                 # NE - Select (Modal)
208                 col = pie.column()
209                 col.operator("gpencil.select_border", text="Border Select", icon='BORDER_RECT')
210                 col.operator("gpencil.select_circle", text="Circle Select", icon='META_EMPTY')
211                 col.operator("gpencil.select_lasso", text="Lasso Select", icon='BORDER_LASSO')
212
213                 # SW - Edit Tools
214                 col = pie.column()
215                 col.operator("gpencil.duplicate_move", icon='PARTICLE_PATH', text="Duplicate")
216                 col.operator("gpencil.delete", icon='X', text="Delete...")
217
218                 # SE - More Tools
219                 pie.operator("wm.call_menu_pie", text="More...").name = "GPENCIL_PIE_tools_more"
220             else:
221                 # Toggle Edit Mode
222                 pie.prop(gpd, "use_stroke_edit_mode", text="Enable Stroke Editing", icon='EDIT')
223
224
225 class GPENCIL_PIE_settings_palette(Menu):
226     """A pie menu for quick access to Grease Pencil settings"""
227     bl_label = "Grease Pencil Settings"
228
229     @classmethod
230     def poll(cls, context):
231         return bool(context.gpencil_data and context.active_gpencil_layer)
232
233     def draw(self, context):
234         layout = self.layout
235
236         pie = layout.menu_pie()
237         # gpd = context.gpencil_data
238         gpl = context.active_gpencil_layer
239
240         # W - Stroke draw settings
241         col = pie.column(align=True)
242         col.label(text="Stroke")
243         col.prop(gpl, "color", text="")
244         col.prop(gpl, "alpha", text="", slider=True)
245
246         # E - Fill draw settings
247         col = pie.column(align=True)
248         col.label(text="Fill")
249         col.prop(gpl, "fill_color", text="")
250         col.prop(gpl, "fill_alpha", text="", slider=True)
251
252         # S - Layer settings
253         col = pie.column()
254         col.prop(gpl, "line_width", slider=True)
255         # col.prop(gpl, "use_volumetric_strokes")
256         col.prop(gpl, "use_onion_skinning")
257
258         # N - Active Layer
259         # XXX: this should show an operator to change the active layer instead
260         col = pie.column()
261         col.label("Active Layer:      ")
262         col.prop(gpl, "info", text="")
263         # col.prop(gpd, "layers")
264         row = col.row()
265         row.prop(gpl, "lock")
266         row.prop(gpl, "hide")
267
268
269 class GPENCIL_PIE_tools_more(Menu):
270     """A pie menu for accessing more Grease Pencil tools"""
271     bl_label = "More Grease Pencil Tools"
272
273     @classmethod
274     def poll(cls, context):
275         gpd = context.gpencil_data
276         return bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes)
277
278     def draw(self, context):
279         layout = self.layout
280
281         pie = layout.menu_pie()
282         # gpd = context.gpencil_data
283
284         pie.operator("gpencil.select_more", icon='ZOOMIN')
285         pie.operator("gpencil.select_less", icon='ZOOMOUT')
286
287         pie.operator("transform.mirror", icon='MOD_MIRROR').gpencil_strokes = True
288         pie.operator("transform.bend", icon='MOD_SIMPLEDEFORM').gpencil_strokes = True
289         pie.operator("transform.shear", icon='MOD_TRIANGULATE').gpencil_strokes = True
290         pie.operator("transform.tosphere", icon='MOD_MULTIRES').gpencil_strokes = True
291
292         pie.operator("gpencil.convert", icon='OUTLINER_OB_CURVE')
293         pie.operator("wm.call_menu_pie", text="Back to Main Palette...").name = "GPENCIL_PIE_tool_palette"
294
295
296 class GPENCIL_UL_layer(UIList):
297     def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
298         # assert(isinstance(item, bpy.types.GPencilLayer)
299         gpl = item
300
301         if self.layout_type in {'DEFAULT', 'COMPACT'}:
302             if gpl.lock:
303                 layout.active = False
304
305             split = layout.split(percentage=0.25)
306             row = split.row(align=True)
307             row.prop(gpl, "color",      text="", emboss = gpl.is_stroke_visible)
308             row.prop(gpl, "fill_color", text="", emboss = gpl.is_fill_visible)
309             split.prop(gpl, "info",  text="", emboss=False)
310
311             row = layout.row(align=True)
312             row.prop(gpl, "lock", text="", emboss=False)
313             row.prop(gpl, "hide", text="", emboss=False)
314         elif self.layout_type in {'GRID'}:
315             layout.alignment = 'CENTER'
316             layout.label(text="", icon_value=icon)
317
318
319 class GreasePencilDataPanel():
320     # subclass must set
321     # bl_space_type = 'IMAGE_EDITOR'
322     bl_label = "Grease Pencil"
323     bl_region_type = 'UI'
324
325     @staticmethod
326     def draw_header(self, context):
327         self.layout.prop(context.space_data, "show_grease_pencil", text="")
328
329     @staticmethod
330     def draw(self, context):
331         layout = self.layout
332
333         # owner of Grease Pencil data
334         gpd_owner = context.gpencil_data_owner
335         gpd = context.gpencil_data
336
337         # Owner Selector
338         if context.space_data.type == 'VIEW_3D':
339             layout.prop(context.tool_settings, "grease_pencil_source", expand=True)
340         elif context.space_data.type == 'CLIP_EDITOR':
341             layout.prop(context.space_data, "grease_pencil_source", expand=True)
342
343         # Grease Pencil data selector
344         layout.template_ID(gpd_owner, "grease_pencil", new="gpencil.data_add", unlink="gpencil.data_unlink")
345
346         # Grease Pencil data...
347         if gpd:
348             self.draw_layers(context, layout, gpd)
349
350     def draw_layers(self, context, layout, gpd):
351         row = layout.row()
352
353         col = row.column()
354         col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index", rows=5)
355
356         col = row.column()
357
358         sub = col.column(align=True)
359         sub.operator("gpencil.layer_add", icon='ZOOMIN', text="")
360         sub.operator("gpencil.layer_remove", icon='ZOOMOUT', text="")
361
362         gpl = context.active_gpencil_layer
363         if gpl:
364             col.separator()
365
366             sub = col.column(align=True)
367             sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP'
368             sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN'
369
370         if gpl:
371             self.draw_layer(layout, gpl)
372
373     def draw_layer(self, layout, gpl):
374         # layer settings
375         split = layout.split(percentage=0.5)
376         split.active = not gpl.lock
377
378         # Column 1 - Stroke
379         col = split.column(align=True)
380         col.label(text="Stroke:")
381         col.prop(gpl, "color", text="")
382         col.prop(gpl, "alpha", slider=True)
383
384         # Column 2 - Fill
385         col = split.column(align=True)
386         col.label(text="Fill:")
387         col.prop(gpl, "fill_color", text="")
388         col.prop(gpl, "fill_alpha", text="Opacity", slider=True)
389
390         # Options
391         split = layout.split(percentage=0.5)
392         split.active = not gpl.lock
393
394         col = split.column(align=True)
395         col.prop(gpl, "line_width", slider=True)
396         col.prop(gpl, "use_volumetric_strokes")
397
398         col = split.column(align=True)
399         col.prop(gpl, "show_x_ray")
400
401         # if debug:
402         #     layout.prop(gpl, "show_points")
403
404         layout.separator()
405
406         # Full-Row - Frame Locking (and Delete Frame)
407         row = layout.row(align=True)
408         row.active = not gpl.lock
409
410         if gpl.active_frame:
411             lock_status = "Locked" if gpl.lock_frame else "Unlocked"
412             lock_label = "Frame: %d (%s)" % (gpl.active_frame.frame_number, lock_status)
413         else:
414             lock_label = "Lock Frame"
415         row.prop(gpl, "lock_frame", text=lock_label, icon='UNLOCKED')
416         row.operator("gpencil.active_frame_delete", text="", icon='X')
417
418         layout.separator()
419
420         # Onion skinning
421         col = layout.column(align=True)
422         col.active = not gpl.lock
423
424         row = col.row()
425         row.prop(gpl, "use_onion_skinning")
426         row.prop(gpl, "use_ghost_custom_colors", text="", icon='COLOR')
427
428         split = col.split(percentage=0.5)
429         split.active = gpl.use_onion_skinning
430
431         # - Before Frames
432         sub = split.column(align=True)
433         row = sub.row(align=True)
434         row.active = gpl.use_ghost_custom_colors
435         row.prop(gpl, "before_color", text="")
436         sub.prop(gpl, "ghost_before_range", text="Before")
437
438         # - After Frames
439         sub = split.column(align=True)
440         row = sub.row(align=True)
441         row.active = gpl.use_ghost_custom_colors
442         row.prop(gpl, "after_color", text="")
443         sub.prop(gpl, "ghost_after_range", text="After")
444
445
446 class GreasePencilToolsPanel():
447     # subclass must set
448     # bl_space_type = 'IMAGE_EDITOR'
449     # bl_options = {'DEFAULT_CLOSED'}
450     bl_label = "Grease Pencil Settings"
451     bl_region_type = 'UI'
452
453     @classmethod
454     def poll(cls, context):
455         return (context.gpencil_data is not None)
456
457     @staticmethod
458     def draw(self, context):
459         layout = self.layout
460
461         # gpd_owner = context.gpencil_data_owner
462         gpd = context.gpencil_data
463
464         layout.prop(gpd, "use_stroke_edit_mode", text="Enable Editing", icon='EDIT', toggle=True)
465
466         layout.separator()
467
468         layout.label("Proportional Edit:")
469         row = layout.row()
470         row.prop(context.tool_settings, "proportional_edit", text="")
471         row.prop(context.tool_settings, "proportional_edit_falloff", text="")
472
473         layout.separator()
474         layout.separator()
475
476         gpencil_stroke_placement_settings(context, layout, gpd)