Cleanup: remove redundant 'gpencil_' prefix
[blender.git] / release / scripts / startup / bl_ui / space_toolsystem_toolbar.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 # For now group all tools together
22 # we may want to move these into per space-type files.
23 #
24 # For now keep this in a single file since it's an area that may change,
25 # so avoid making changes all over the place.
26
27 import bpy
28 from bpy.types import Panel
29
30 from .space_toolsystem_common import (
31     ToolSelectPanelHelper,
32     ToolDef,
33 )
34 from .properties_material_gpencil import (
35     GPENCIL_UL_matslots,
36 )
37 from .properties_grease_pencil_common import (
38     AnnotationDataPanel,
39 )
40
41 def generate_from_brushes_ex(
42         context, *,
43         icon_prefix,
44         brush_test_attr,
45         brush_category_attr,
46         brush_category_layout,
47 ):
48     # Categories
49     brush_categories = {}
50     if context.mode != 'GPENCIL_PAINT':
51         for brush in context.blend_data.brushes:
52             if getattr(brush, brush_test_attr) and brush.gpencil_settings is None:
53                 category = getattr(brush, brush_category_attr)
54                 name = brush.name
55                 brush_categories.setdefault(category, []).append(
56                     ToolDef.from_dict(
57                         dict(
58                             text=name,
59                             icon=icon_prefix + category.lower(),
60                             data_block=name,
61                         )
62                     )
63                 )
64     else:
65         def draw_settings(context, layout, tool):
66             _defs_gpencil_paint.draw_settings_common(context, layout, tool)
67
68         for brush_type in brush_category_layout:
69             for brush in context.blend_data.brushes:
70                 if brush.gpencil_settings and getattr(brush, brush_test_attr) and brush.gpencil_settings.gp_icon == brush_type[0]:
71                     category = brush_type[0]
72                     name = brush.name
73                     text = name
74
75                     # Define icon.
76                     icon_name = {
77                         'PENCIL': 'draw_pencil',
78                         'PEN': 'draw_pen',
79                         'INK': 'draw_ink',
80                         'INKNOISE': 'draw_noise',
81                         'BLOCK': 'draw_block',
82                         'MARKER': 'draw_marker',
83                         'FILL': 'draw_fill',
84                         'SOFT': 'draw.eraser_soft',
85                         'HARD': 'draw.eraser_hard',
86                         'STROKE': 'draw.eraser_stroke',
87                     }[category]
88                     brush_categories.setdefault(category, []).append(
89                         ToolDef.from_dict(
90                             dict(
91                                 text=text,
92                                 icon=icon_prefix + icon_name,
93                                 data_block=name,
94                                 widget=None,
95                                 operator="gpencil.draw",
96                                 draw_settings=draw_settings,
97                             )
98                         )
99                     )
100
101     def tools_from_brush_group(groups):
102         assert(type(groups) is tuple)
103         if len(groups) == 1:
104             tool_defs = tuple(brush_categories.pop(groups[0], ()))
105         else:
106             tool_defs = tuple(item for g in groups for item in brush_categories.pop(g, ()))
107
108         if len(tool_defs) > 1:
109             return (tool_defs,)
110         else:
111             return tool_defs
112
113     # Each item below is a single toolbar entry:
114     # Grouped for multiple or none if no brushes are found.
115     tool_defs = tuple(
116         tool_def
117         for category in brush_category_layout
118         for tool_def in tools_from_brush_group(category)
119     )
120     # Ensure we use all types.
121     if brush_categories:
122         print(brush_categories)
123     assert(len(brush_categories) == 0)
124     return tool_defs
125
126
127 def generate_from_enum_ex(
128         context, *,
129         icon_prefix,
130         data,
131         attr,
132 ):
133     tool_defs = []
134     for enum in data.rna_type.properties[attr].enum_items_static:
135         name = enum.name
136         identifier = enum.identifier
137         tool_defs.append(
138             ToolDef.from_dict(
139                 dict(
140                     text=name,
141                     icon=icon_prefix + identifier.lower(),
142                     data_block=identifier,
143                 )
144             )
145         )
146     return tuple(tool_defs)
147
148
149 class _defs_view3d_generic:
150     @ToolDef.from_fn
151     def cursor():
152         def draw_settings(context, layout, tool):
153             props = tool.operator_properties("view3d.cursor3d")
154             layout.prop(props, "use_depth")
155             layout.prop(props, "orientation")
156         return dict(
157             text="Cursor",
158             description=(
159                 "Set the cursor location, drag to transform"
160             ),
161             icon="ops.generic.cursor",
162             keymap=(
163                 ("view3d.cursor3d", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
164                 ("transform.translate",
165                  dict(release_confirm=True, cursor_transform=True),
166                  dict(type='EVT_TWEAK_A', value='ANY'),
167                  ),
168             ),
169             draw_settings=draw_settings,
170         )
171
172     @ToolDef.from_fn
173     def cursor_click():
174         return dict(
175             text="None",
176             icon="ops.generic.cursor",
177             keymap=(
178                 # This is a dummy keymap entry, until particle system is properly working with toolsystem.
179                 ("view3d.cursor3d", dict(), dict(type='ACTIONMOUSE', value='CLICK', ctrl=True, alt=True, shift=True)),
180             ),
181         )
182
183     @ToolDef.from_fn
184     def ruler():
185         return dict(
186             text="Measure",
187             description=(
188                 "Measure distance and angles.\n"
189                 "\u2022 Drag anywhere for new measurement.\n"
190                 "\u2022 Drag ruler segment to measure an angle.\n"
191                 "\u2022 Drag ruler outside the view to remove.\n"
192                 "\u2022 Ctrl to snap.\n"
193                 "\u2022 Shift to measure surface thickness"
194             ),
195             icon="ops.view3d.ruler",
196             widget="VIEW3D_GGT_ruler",
197             keymap=(
198                 ("view3d.ruler_add", dict(), dict(type='EVT_TWEAK_A', value='ANY')),
199             ),
200         )
201
202
203 def _defs_annotate_factory():
204
205     class _defs_annotate:
206         @staticmethod
207         def draw_settings_common(context, layout, tool):
208             if type(context.gpencil_data_owner) is bpy.types.Object:
209                 gpd = context.scene.grease_pencil
210             else:
211                 gpd = context.gpencil_data
212
213             if gpd is not None:
214                 if gpd.layers.active_note is not None:
215                     text = gpd.layers.active_note
216                     maxw = 25
217                     if len(text) > maxw:
218                         text = text[:maxw - 5] + '..' + text[-3:]
219                 else:
220                     text = ""
221
222                 layout.label(text="Annotation:")
223                 gpl = context.active_gpencil_layer
224                 if gpl is not None:
225                     sub = layout.row(align=True)
226                     sub.ui_units_x = 8
227
228                     sub.prop(gpl, "color", text="")
229                     sub.popover(
230                         panel="TOPBAR_PT_annotation_layers",
231                         text=text,
232                     )
233
234             tool_settings = context.tool_settings
235             space_type = tool.space_type
236             if space_type == 'VIEW_3D':
237                 layout.separator()
238
239                 row = layout.row(align=True)
240                 row.prop(tool_settings, "annotation_stroke_placement_view3d", text="Placement")
241                 if tool_settings.gpencil_stroke_placement_view3d == 'CURSOR':
242                     row.prop(tool_settings.gpencil_sculpt, "lockaxis")
243                 elif tool_settings.gpencil_stroke_placement_view3d in {'SURFACE', 'STROKE'}:
244                     row.prop(tool_settings, "use_gpencil_stroke_endpoints")
245
246         @ToolDef.from_fn
247         def scribble():
248             def draw_settings(context, layout, tool):
249                 _defs_annotate.draw_settings_common(context, layout, tool)
250             return dict(
251                 text="Annotate",
252                 icon="ops.gpencil.draw",
253                 cursor='PAINT_BRUSH',
254                 keymap=(
255                     ("gpencil.annotate",
256                      dict(mode='DRAW', wait_for_input=False),
257                      dict(type='ACTIONMOUSE', value='PRESS')),
258                 ),
259                 draw_settings=draw_settings,
260             )
261
262         @ToolDef.from_fn
263         def line():
264             def draw_settings(context, layout, tool):
265                 _defs_annotate.draw_settings_common(context, layout, tool)
266             return dict(
267                 text="Annotate Line",
268                 icon="ops.gpencil.draw.line",
269                 cursor='CROSSHAIR',
270                 keymap=(
271                     ("gpencil.annotate",
272                      dict(mode='DRAW_STRAIGHT', wait_for_input=False),
273                      dict(type='EVT_TWEAK_A', value='ANY')),
274                 ),
275                 draw_settings=draw_settings,
276             )
277
278         @ToolDef.from_fn
279         def poly():
280             def draw_settings(context, layout, tool):
281                 _defs_annotate.draw_settings_common(context, layout, tool)
282             return dict(
283                 text="Annotate Polygon",
284                 icon="ops.gpencil.draw.poly",
285                 cursor='CROSSHAIR',
286                 keymap=(
287                     ("gpencil.annotate",
288                      dict(mode='DRAW_POLY', wait_for_input=False),
289                      dict(type='ACTIONMOUSE', value='PRESS')),
290                 ),
291                 draw_settings=draw_settings,
292             )
293
294         @ToolDef.from_fn
295         def eraser():
296             def draw_settings(context, layout, tool):
297                 # TODO: Move this setting to tool_settings
298                 user_prefs = context.user_preferences
299                 layout.prop(user_prefs.edit, "grease_pencil_eraser_radius", text="Radius")
300             return dict(
301                 text="Annotate Eraser",
302                 icon="ops.gpencil.draw.eraser",
303                 cursor='CROSSHAIR',  # XXX: Always show brush circle when enabled
304                 keymap=(
305                     ("gpencil.annotate",
306                      dict(mode='ERASER', wait_for_input=False),
307                      dict(type='ACTIONMOUSE', value='PRESS')),
308                 ),
309                 draw_settings=draw_settings,
310             )
311     return _defs_annotate
312
313
314 # Needed so annotation gets a keymap per space type.
315 _defs_annotate_image = _defs_annotate_factory()
316 _defs_annotate_view3d = _defs_annotate_factory()
317
318
319 class _defs_transform:
320
321     @ToolDef.from_fn
322     def translate():
323         return dict(
324             text="Move",
325             # cursor='SCROLL_XY',
326             icon="ops.transform.translate",
327             widget="TRANSFORM_GGT_gizmo",
328             operator="transform.translate",
329             keymap=(
330                 ("transform.translate", dict(release_confirm=True), dict(type='EVT_TWEAK_A', value='ANY')),
331             ),
332         )
333
334     @ToolDef.from_fn
335     def rotate():
336         return dict(
337             text="Rotate",
338             # cursor='SCROLL_XY',
339             icon="ops.transform.rotate",
340             widget="TRANSFORM_GGT_gizmo",
341             operator="transform.rotate",
342             keymap=(
343                 ("transform.rotate", dict(release_confirm=True), dict(type='EVT_TWEAK_A', value='ANY')),
344             ),
345         )
346
347     @ToolDef.from_fn
348     def scale():
349         return dict(
350             text="Scale",
351             # cursor='SCROLL_XY',
352             icon="ops.transform.resize",
353             widget="TRANSFORM_GGT_gizmo",
354             operator="transform.resize",
355             keymap=(
356                 ("transform.resize", dict(release_confirm=True), dict(type='EVT_TWEAK_A', value='ANY')),
357             ),
358         )
359
360     @ToolDef.from_fn
361     def scale_cage():
362         return dict(
363             text="Scale Cage",
364             icon="ops.transform.resize.cage",
365             widget="VIEW3D_GGT_xform_cage",
366             operator="transform.resize",
367         )
368
369     @ToolDef.from_fn
370     def transform():
371         def draw_settings(context, layout, tool):
372             layout.label(text="Gizmos:")
373             tool_settings = context.tool_settings
374             layout.prop(tool_settings, "use_gizmo_mode")
375
376             props = tool.gizmo_group_properties("TRANSFORM_GGT_gizmo")
377             layout.prop(props, "drag_action")
378
379         return dict(
380             text="Transform",
381             description=(
382                 "Supports any combination of grab, rotate & scale at once"
383             ),
384             icon="ops.transform.transform",
385             widget="TRANSFORM_GGT_gizmo",
386             keymap=(
387                 ("transform.from_gizmo", dict(), dict(type='EVT_TWEAK_A', value='ANY')),
388             ),
389             draw_settings=draw_settings,
390         )
391
392
393 class _defs_view3d_select:
394
395     @ToolDef.from_fn
396     def border():
397         def draw_settings(context, layout, tool):
398             props = tool.operator_properties("view3d.select_box")
399             layout.prop(props, "mode", expand=True)
400         return dict(
401             text="Select Box",
402             icon="ops.generic.select_box",
403             widget=None,
404             keymap=(
405                 ("view3d.select_box",
406                  dict(mode='ADD'),
407                  dict(type='EVT_TWEAK_A', value='ANY')),
408                 ("view3d.select_box",
409                  dict(mode='SUB'),
410                  dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)),
411             ),
412             draw_settings=draw_settings,
413         )
414
415     @ToolDef.from_fn
416     def circle():
417         def draw_settings(context, layout, tool):
418             props = tool.operator_properties("view3d.select_circle")
419             layout.prop(props, "radius")
420
421         def draw_cursor(context, tool, xy):
422             from gpu_extras.presets import draw_circle_2d
423             props = tool.operator_properties("view3d.select_circle")
424             radius = props.radius
425             draw_circle_2d(xy, (1.0,) * 4, radius, 32)
426
427         return dict(
428             text="Select Circle",
429             icon="ops.generic.select_circle",
430             widget=None,
431             keymap=(
432                 ("view3d.select_circle",
433                  dict(deselect=False),
434                  dict(type='ACTIONMOUSE', value='PRESS')),
435                 ("view3d.select_circle",
436                  dict(deselect=True),
437                  dict(type='ACTIONMOUSE', value='PRESS', ctrl=True)),
438             ),
439             draw_settings=draw_settings,
440             draw_cursor=draw_cursor,
441         )
442
443     @ToolDef.from_fn
444     def lasso():
445         def draw_settings(context, layout, tool):
446             props = tool.operator_properties("view3d.select_lasso")
447             layout.prop(props, "mode", expand=True)
448         return dict(
449             text="Select Lasso",
450             icon="ops.generic.select_lasso",
451             widget=None,
452             keymap=(
453                 ("view3d.select_lasso",
454                  dict(mode='ADD'),
455                  dict(type='EVT_TWEAK_A', value='ANY')),
456                 ("view3d.select_lasso",
457                  dict(mode='SUB'),
458                  dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)),
459             ),
460             draw_settings=draw_settings,
461         )
462 # -----------------------------------------------------------------------------
463 # Object Modes (named based on context.mode)
464
465
466 class _defs_edit_armature:
467
468     @ToolDef.from_fn
469     def roll():
470         return dict(
471             text="Roll",
472             icon="ops.armature.bone.roll",
473             widget=None,
474             keymap=(
475                 ("transform.transform",
476                  dict(release_confirm=True, mode='BONE_ROLL'),
477                  dict(type='EVT_TWEAK_A', value='ANY'),),
478             ),
479         )
480
481     @ToolDef.from_fn
482     def bone_envelope():
483         return dict(
484             text="Bone Envelope",
485             icon="ops.transform.bone_envelope",
486             widget=None,
487             keymap=(
488                 ("transform.transform",
489                  dict(release_confirm=True, mode='BONE_ENVELOPE'),
490                  dict(type='ACTIONMOUSE', value='PRESS')),
491             ),
492         )
493
494     @ToolDef.from_fn
495     def bone_size():
496         return dict(
497             text="Bone Size",
498             icon="ops.transform.bone_size",
499             widget=None,
500             keymap=(
501                 ("transform.transform",
502                  dict(release_confirm=True, mode='BONE_SIZE'),
503                  dict(type='ACTIONMOUSE', value='PRESS')),
504             ),
505         )
506
507     @ToolDef.from_fn
508     def extrude():
509         return dict(
510             text="Extrude",
511             icon="ops.armature.extrude_move",
512             widget=None,
513             keymap=(
514                 ("armature.extrude_move",
515                  dict(TRANSFORM_OT_translate=dict(release_confirm=True)),
516                  dict(type='EVT_TWEAK_A', value='ANY')),
517             ),
518         )
519
520     @ToolDef.from_fn
521     def extrude_cursor():
522         return dict(
523             text="Extrude to Cursor",
524             icon="ops.armature.extrude_cursor",
525             widget=None,
526             keymap=(
527                 ("armature.click_extrude", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
528             ),
529         )
530
531
532 class _defs_edit_mesh:
533
534     @ToolDef.from_fn
535     def cube_add():
536         return dict(
537             text="Add Cube",
538             icon="ops.mesh.primitive_cube_add_gizmo",
539             widget=None,
540             keymap=(
541                 ("view3d.cursor3d", dict(), dict(type='ACTIONMOUSE', value='CLICK')),
542                 ("mesh.primitive_cube_add_gizmo", dict(), dict(type='EVT_TWEAK_A', value='ANY')),
543             ),
544         )
545
546     @ToolDef.from_fn
547     def rip_region():
548         def draw_settings(context, layout, tool):
549             props = tool.operator_properties("mesh.rip_move")
550             props_macro = props.MESH_OT_rip
551             layout.prop(props_macro, "use_fill")
552
553         return dict(
554             text="Rip Region",
555             icon="ops.mesh.rip",
556             widget=None,
557             keymap=(
558                 ("mesh.rip_move",
559                  dict(TRANSFORM_OT_translate=dict(release_confirm=True)),
560                  dict(type='ACTIONMOUSE', value='PRESS')),
561             ),
562             draw_settings=draw_settings,
563         )
564
565     @ToolDef.from_fn
566     def rip_edge():
567         return dict(
568             text="Rip Edge",
569             icon="ops.mesh.rip_edge",
570             widget=None,
571             keymap=(
572                 ("mesh.rip_edge_move",
573                  dict(TRANSFORM_OT_translate=dict(release_confirm=True)),
574                  dict(type='ACTIONMOUSE', value='PRESS')),
575             ),
576         )
577
578     @ToolDef.from_fn
579     def poly_build():
580         return dict(
581             text="Poly Build",
582             icon="ops.mesh.polybuild_hover",
583             widget="VIEW3D_GGT_mesh_preselect_elem",
584             keymap=(
585                 ("mesh.polybuild_face_at_cursor_move",
586                  dict(TRANSFORM_OT_translate=dict(release_confirm=True)),
587                  dict(type='ACTIONMOUSE', value='PRESS')),
588                 ("mesh.polybuild_split_at_cursor_move",
589                  dict(TRANSFORM_OT_translate=dict(release_confirm=True)),
590                  dict(type='ACTIONMOUSE', value='PRESS', ctrl=True)),
591                 ("mesh.polybuild_dissolve_at_cursor", dict(), dict(type='ACTIONMOUSE', value='CLICK', alt=True)),
592             ),
593         )
594
595     @ToolDef.from_fn
596     def edge_slide():
597         def draw_settings(context, layout, tool):
598             props = tool.operator_properties("transform.edge_slide")
599             layout.prop(props, "correct_uv")
600
601         return dict(
602             text="Edge Slide",
603             icon="ops.transform.edge_slide",
604             widget=None,
605             keymap=(
606                 ("transform.edge_slide", dict(release_confirm=True),
607                  dict(type='ACTIONMOUSE', value='PRESS')
608                  ),
609             ),
610             draw_settings=draw_settings,
611         )
612
613     @ToolDef.from_fn
614     def vert_slide():
615         def draw_settings(context, layout, tool):
616             props = tool.operator_properties("transform.vert_slide")
617             layout.prop(props, "correct_uv")
618
619         return dict(
620             text="Vertex Slide",
621             icon="ops.transform.vert_slide",
622             widget=None,
623             keymap=(
624                 ("transform.vert_slide", dict(release_confirm=True),
625                  dict(type='ACTIONMOUSE', value='PRESS')),
626             ),
627             draw_settings=draw_settings,
628         )
629
630     @ToolDef.from_fn
631     def spin():
632         def draw_settings(context, layout, tool):
633             props = tool.operator_properties("mesh.spin")
634             layout.prop(props, "steps")
635             props = tool.gizmo_group_properties("MESH_GGT_spin")
636             layout.prop(props, "axis")
637
638         return dict(
639             text="Spin",
640             icon="ops.mesh.spin",
641             widget="MESH_GGT_spin",
642             keymap=(
643                 ("mesh.spin", dict(),
644                  dict(type='EVT_TWEAK_A', value='ANY')),
645             ),
646             draw_settings=draw_settings,
647         )
648
649     @ToolDef.from_fn
650     def spin_duplicate():
651         def draw_settings(context, layout, tool):
652             props = tool.operator_properties("mesh.spin")
653             layout.prop(props, "steps")
654             props = tool.gizmo_group_properties("MESH_GGT_spin")
655             layout.prop(props, "axis")
656
657         return dict(
658             text="Spin (Duplicate)",
659             icon="ops.mesh.spin.duplicate",
660             widget="MESH_GGT_spin",
661             keymap=(
662                 ("mesh.spin", dict(dupli=True),
663                  dict(type='EVT_TWEAK_A', value='ANY')),
664             ),
665             draw_settings=draw_settings,
666         )
667
668     @ToolDef.from_fn
669     def inset():
670         def draw_settings(context, layout, tool):
671             props = tool.operator_properties("mesh.inset")
672             layout.prop(props, "use_outset")
673             layout.prop(props, "use_individual")
674             layout.prop(props, "use_even_offset")
675             layout.prop(props, "use_relative_offset")
676
677         return dict(
678             text="Inset Faces",
679             icon="ops.mesh.inset",
680             widget=None,
681             keymap=(
682                 ("mesh.inset", dict(release_confirm=True),
683                  dict(type='ACTIONMOUSE', value='PRESS')),
684             ),
685             draw_settings=draw_settings,
686         )
687
688     @ToolDef.from_fn
689     def bevel():
690         def draw_settings(context, layout, tool):
691             props = tool.operator_properties("mesh.bevel")
692             layout.prop(props, "offset_type")
693             layout.prop(props, "segments")
694             layout.prop(props, "profile", slider=True)
695             layout.prop(props, "vertex_only")
696
697         return dict(
698             text="Bevel",
699             icon="ops.mesh.bevel",
700             widget=None,
701             keymap=(
702                 ("mesh.bevel", dict(release_confirm=True),
703                  dict(type='ACTIONMOUSE', value='PRESS')),
704             ),
705             draw_settings=draw_settings,
706         )
707
708     @ToolDef.from_fn
709     def extrude():
710         def draw_settings(context, layout, tool):
711             props = tool.gizmo_group_properties("MESH_GGT_extrude")
712             layout.prop(props, "axis_type", expand=True)
713         return dict(
714             text="Extrude Region",
715             # The operator description isn't useful in this case, give our own.
716             description=(
717                 "Extrude freely or along an axis"
718             ),
719             icon="ops.mesh.extrude_region_move",
720             widget="MESH_GGT_extrude",
721             # Important to use same operator as 'E' key.
722             operator="view3d.edit_mesh_extrude_move_normal",
723             keymap=(
724                 ("mesh.extrude_context_move",
725                  dict(TRANSFORM_OT_translate=dict(release_confirm=True)),
726                  dict(type='EVT_TWEAK_A', value='ANY')),
727             ),
728             draw_settings=draw_settings,
729         )
730
731     @ToolDef.from_fn
732     def extrude_normals():
733         def draw_settings(context, layout, tool):
734             props = tool.operator_properties("mesh.extrude_region_shrink_fatten")
735             props_macro = props.TRANSFORM_OT_shrink_fatten
736             layout.prop(props_macro, "use_even_offset")
737         return dict(
738             text="Extrude Along Normals",
739             icon="ops.mesh.extrude_region_shrink_fatten",
740             widget=None,
741             operator="mesh.extrude_region_shrink_fatten",
742             keymap=(
743                 ("mesh.extrude_region_shrink_fatten",
744                  dict(TRANSFORM_OT_shrink_fatten=dict(release_confirm=True)),
745                  dict(type='EVT_TWEAK_A', value='ANY')),
746             ),
747             draw_settings=draw_settings,
748         )
749
750     @ToolDef.from_fn
751     def extrude_individual():
752         return dict(
753             text="Extrude Individual",
754             icon="ops.mesh.extrude_faces_move",
755             widget=None,
756             keymap=(
757                 ("mesh.extrude_faces_move", dict(TRANSFORM_OT_shrink_fatten=dict(release_confirm=True)),
758                  dict(type='EVT_TWEAK_A', value='ANY')),
759             ),
760         )
761
762     @ToolDef.from_fn
763     def extrude_cursor():
764         def draw_settings(context, layout, tool):
765             props = tool.operator_properties("mesh.dupli_extrude_cursor")
766             layout.prop(props, "rotate_source")
767
768         return dict(
769             text="Extrude to Cursor",
770             icon="ops.mesh.dupli_extrude_cursor",
771             widget=None,
772             keymap=(
773                 ("mesh.dupli_extrude_cursor", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
774             ),
775             draw_settings=draw_settings,
776         )
777
778     @ToolDef.from_fn
779     def loopcut_slide():
780
781         def draw_settings(context, layout, tool):
782             props = tool.operator_properties("mesh.loopcut_slide")
783             props_macro = props.MESH_OT_loopcut
784             layout.prop(props_macro, "number_cuts")
785             props_macro = props.TRANSFORM_OT_edge_slide
786             layout.prop(props_macro, "correct_uv")
787
788         return dict(
789             text="Loop Cut",
790             icon="ops.mesh.loopcut_slide",
791             widget="VIEW3D_GGT_mesh_preselect_edgering",
792             keymap=(
793                 ("mesh.loopcut_slide",
794                  dict(TRANSFORM_OT_edge_slide=dict(release_confirm=True)),
795                  dict(type='ACTIONMOUSE', value='PRESS')),
796             ),
797             draw_settings=draw_settings,
798         )
799
800     @ToolDef.from_fn
801     def offset_edge_loops_slide():
802         return dict(
803             text="Offset Edge Loop Cut",
804             icon="ops.mesh.offset_edge_loops_slide",
805             widget=None,
806             keymap=(
807                 ("mesh.offset_edge_loops_slide", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
808             ),
809         )
810
811     @ToolDef.from_fn
812     def vertex_smooth():
813         def draw_settings(context, layout, tool):
814             props = tool.operator_properties("mesh.vertices_smooth")
815             layout.prop(props, "repeat")
816         return dict(
817             text="Smooth",
818             icon="ops.mesh.vertices_smooth",
819             widget="WM_GGT_value_operator_redo",
820             keymap=(
821                 # Use 0.0, so dragging increases from nothing.
822                 ("mesh.vertices_smooth", dict(factor=0.0),
823                  dict(type='ACTIONMOUSE', value='PRESS')),
824             ),
825             draw_settings=draw_settings,
826         )
827
828     @ToolDef.from_fn
829     def vertex_randomize():
830         def draw_settings(context, layout, tool):
831             props = tool.operator_properties("transform.vertex_random")
832             layout.prop(props, "uniform")
833             layout.prop(props, "normal")
834             layout.prop(props, "seed")
835         return dict(
836             text="Randomize",
837             icon="ops.transform.vertex_random",
838             widget="WM_GGT_value_operator_redo",
839             keymap=(
840                 # Use 0.0, so dragging increases from nothing.
841                 ("transform.vertex_random", dict(offset=0.0),
842                  dict(type='ACTIONMOUSE', value='PRESS')),
843             ),
844             draw_settings=draw_settings,
845         )
846
847     @ToolDef.from_fn
848     def shear():
849         def draw_settings(context, layout, tool):
850             props = tool.operator_properties("transform.shear")
851             layout.label(text="View Axis:")
852             layout.prop(props, "shear_axis", expand=True)
853         return dict(
854             text="Shear",
855             icon="ops.transform.shear",
856             widget="VIEW3D_GGT_xform_shear",
857             keymap=(
858                 ("transform.shear", dict(release_confirm=True),
859                  dict(type='EVT_TWEAK_A', value='ANY')),
860             ),
861             draw_settings=draw_settings,
862         )
863
864     @ToolDef.from_fn
865     def tosphere():
866         return dict(
867             text="To Sphere",
868             icon="ops.transform.tosphere",
869             widget=None,
870             keymap=(
871                 ("transform.tosphere", dict(release_confirm=True),
872                  dict(type='ACTIONMOUSE', value='PRESS')),
873             ),
874         )
875
876     @ToolDef.from_fn
877     def shrink_fatten():
878         def draw_settings(context, layout, tool):
879             props = tool.operator_properties("transform.shrink_fatten")
880             layout.prop(props, "use_even_offset")
881
882         return dict(
883             text="Shrink/Fatten",
884             icon="ops.transform.shrink_fatten",
885             widget=None,
886             keymap=(
887                 ("transform.shrink_fatten", dict(release_confirm=True),
888                  dict(type='ACTIONMOUSE', value='PRESS')),
889             ),
890             draw_settings=draw_settings,
891         )
892
893     @ToolDef.from_fn
894     def push_pull():
895         return dict(
896             text="Push/Pull",
897             icon="ops.transform.push_pull",
898             widget=None,
899             keymap=(
900                 ("transform.push_pull", dict(release_confirm=True),
901                  dict(type='ACTIONMOUSE', value='PRESS')),
902             ),
903         )
904
905     @ToolDef.from_fn
906     def knife():
907         def draw_settings(context, layout, tool):
908             props = tool.operator_properties("mesh.knife_tool")
909             layout.prop(props, "use_occlude_geometry")
910             layout.prop(props, "only_selected")
911
912         return dict(
913             text="Knife",
914             icon="ops.mesh.knife_tool",
915             widget=None,
916             keymap=(
917                 ("mesh.knife_tool",
918                  dict(wait_for_input=False),
919                  dict(type='ACTIONMOUSE', value='PRESS')),
920             ),
921             draw_settings=draw_settings,
922         )
923
924     @ToolDef.from_fn
925     def bisect():
926         def draw_settings(context, layout, tool):
927             props = tool.operator_properties("mesh.bisect")
928             layout.prop(props, "use_fill")
929             layout.prop(props, "clear_inner")
930             layout.prop(props, "clear_outer")
931             layout.prop(props, "threshold")
932         return dict(
933             text="Bisect",
934             icon="ops.mesh.bisect",
935             widget=None,
936             keymap=(
937                 ("mesh.bisect",
938                  dict(),
939                  dict(type='EVT_TWEAK_A', value='ANY')),
940             ),
941             draw_settings=draw_settings,
942         )
943
944
945 class _defs_edit_curve:
946
947     @ToolDef.from_fn
948     def draw():
949         def draw_settings(context, layout, tool):
950             # Tool settings initialize operator options.
951             tool_settings = context.tool_settings
952             cps = tool_settings.curve_paint_settings
953
954             col = layout.row()
955
956             col.prop(cps, "curve_type")
957
958             if cps.curve_type == 'BEZIER':
959                 col.prop(cps, "error_threshold")
960                 col.prop(cps, "fit_method")
961                 col.prop(cps, "use_corners_detect")
962
963                 col = layout.row()
964                 col.active = cps.use_corners_detect
965                 col.prop(cps, "corner_angle")
966
967         return dict(
968             text="Draw",
969             cursor='PAINT_BRUSH',
970             icon="ops.curve.draw",
971             widget=None,
972             keymap=(
973                 ("curve.draw", dict(wait_for_input=False), dict(type='ACTIONMOUSE', value='PRESS')),
974             ),
975             draw_settings=draw_settings,
976         )
977
978     @ToolDef.from_fn
979     def extrude():
980         return dict(
981             text="Extrude",
982             icon="ops.curve.extrude_move",
983             widget=None,
984             keymap=(
985                 ("curve.extrude_move",
986                  dict(TRANSFORM_OT_translate=dict(release_confirm=True)),
987                  dict(type='EVT_TWEAK_A', value='ANY')),
988             ),
989         )
990
991     @ToolDef.from_fn
992     def extrude_cursor():
993         return dict(
994             text="Extrude Cursor",
995             icon="ops.curve.extrude_cursor",
996             widget=None,
997             keymap=(
998                 ("curve.vertex_add", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
999             ),
1000         )
1001
1002
1003 class _defs_pose:
1004
1005     @ToolDef.from_fn
1006     def breakdown():
1007         return dict(
1008             text="Breakdowner",
1009             icon="ops.pose.breakdowner",
1010             widget=None,
1011             keymap=(
1012                 ("pose.breakdown", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
1013             ),
1014         )
1015
1016     @ToolDef.from_fn
1017     def push():
1018         return dict(
1019             text="Push",
1020             icon="ops.pose.push",
1021             widget=None,
1022             keymap=(
1023                 ("pose.push", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
1024             ),
1025         )
1026
1027     @ToolDef.from_fn
1028     def relax():
1029         return dict(
1030             text="Relax",
1031             icon="ops.pose.relax",
1032             widget=None,
1033             keymap=(
1034                 ("pose.relax", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
1035             ),
1036         )
1037
1038
1039 class _defs_particle:
1040
1041     @staticmethod
1042     def generate_from_brushes(context):
1043         return generate_from_enum_ex(
1044             context,
1045             icon_prefix="brush.particle.",
1046             data=context.tool_settings.particle_edit,
1047             attr="tool",
1048         )
1049
1050
1051 class _defs_sculpt:
1052
1053     @staticmethod
1054     def generate_from_brushes(context):
1055         return generate_from_brushes_ex(
1056             context,
1057             icon_prefix="brush.sculpt.",
1058             brush_test_attr="use_paint_sculpt",
1059             brush_category_attr="sculpt_tool",
1060             brush_category_layout=(
1061                 ('DRAW',),
1062                 ('GRAB', 'THUMB'),
1063                 ('SNAKE_HOOK',),
1064                 ('BLOB', 'INFLATE'),
1065                 ('SMOOTH', 'SCRAPE', 'FLATTEN'),
1066                 ('CREASE', 'PINCH'),
1067                 ('CLAY', 'CLAY_STRIPS'),
1068                 ('LAYER',),
1069                 ('NUDGE', 'ROTATE'),
1070                 ('FILL',),
1071                 ('SIMPLIFY',),
1072                 ('MASK',),
1073             )
1074         )
1075
1076     @ToolDef.from_fn
1077     def hide_border():
1078         return dict(
1079             text="Box Hide",
1080             icon="ops.sculpt.border_hide",
1081             widget=None,
1082             keymap=(
1083                 ("paint.hide_show", dict(action='HIDE'), dict(type='EVT_TWEAK_A', value='ANY')),
1084                 ("paint.hide_show", dict(action='SHOW'), dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)),
1085                 ("paint.hide_show", dict(action='SHOW', area='ALL'), dict(type='SELECTMOUSE', value='PRESS')),
1086             ),
1087         )
1088
1089     @ToolDef.from_fn
1090     def mask_border():
1091         return dict(
1092             text="Box Mask",
1093             icon="ops.sculpt.border_mask",
1094             widget=None,
1095             keymap=(
1096                 ("view3d.select_box", dict(mode='ADD'), dict(type='EVT_TWEAK_A', value='ANY')),
1097                 ("view3d.select_box", dict(mode='SUB'), dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)),
1098             ),
1099         )
1100
1101
1102 class _defs_vertex_paint:
1103
1104     @staticmethod
1105     def poll_select_mask(context):
1106         ob = context.active_object
1107         return ob.type == 'MESH' and ob.data.use_paint_mask
1108
1109     @staticmethod
1110     def generate_from_brushes(context):
1111         return generate_from_brushes_ex(
1112             context,
1113             icon_prefix="brush.paint_vertex.",
1114             brush_test_attr="use_paint_vertex",
1115             brush_category_attr="vertex_tool",
1116             brush_category_layout=(
1117                 ('MIX',),
1118                 ('BLUR', 'AVERAGE'),
1119                 ('SMEAR',),
1120                 (
1121                     'ADD', 'SUB', 'MUL', 'LIGHTEN', 'DARKEN',
1122                     'COLORDODGE', 'DIFFERENCE', 'SCREEN', 'HARDLIGHT',
1123                     'OVERLAY', 'SOFTLIGHT', 'EXCLUSION', 'LUMINOCITY',
1124                     'SATURATION', 'HUE', 'ERASE_ALPHA', 'ADD_ALPHA',
1125                 ),
1126             )
1127         )
1128
1129
1130 class _defs_texture_paint:
1131
1132     @staticmethod
1133     def generate_from_brushes(context):
1134         return generate_from_brushes_ex(
1135             context,
1136             icon_prefix="brush.paint_texture.",
1137             brush_test_attr="use_paint_image",
1138             brush_category_attr="image_tool",
1139             brush_category_layout=(
1140                 ('DRAW',),
1141                 ('SOFTEN',),
1142                 ('SMEAR',),
1143                 ('CLONE',),
1144                 ('FILL',),
1145                 ('MASK',),
1146             )
1147         )
1148
1149
1150 class _defs_weight_paint:
1151
1152     @staticmethod
1153     def poll_select_mask(context):
1154         ob = context.active_object
1155         return (ob.type == 'MESH' and
1156                 (ob.data.use_paint_mask or
1157                  ob.data.use_paint_mask_vertex))
1158
1159     @staticmethod
1160     def generate_from_brushes(context):
1161         return generate_from_brushes_ex(
1162             context,
1163             icon_prefix="brush.paint_weight.",
1164             brush_test_attr="use_paint_weight",
1165             brush_category_attr="vertex_tool",
1166             brush_category_layout=(
1167                 ('MIX',),
1168                 ('BLUR', 'AVERAGE'),
1169                 ('SMEAR',),
1170                 (
1171                     'ADD', 'SUB', 'MUL', 'LIGHTEN', 'DARKEN',
1172                     'COLORDODGE', 'DIFFERENCE', 'SCREEN', 'HARDLIGHT',
1173                     'OVERLAY', 'SOFTLIGHT', 'EXCLUSION', 'LUMINOCITY',
1174                     'SATURATION', 'HUE',
1175                 ),
1176             )
1177         )
1178
1179     @ToolDef.from_fn
1180     def sample_weight():
1181         return dict(
1182             text="Sample Weight",
1183             icon="ops.paint.weight_sample",
1184             widget=None,
1185             keymap=(
1186                 ("paint.weight_sample", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
1187             ),
1188         )
1189
1190     @ToolDef.from_fn
1191     def sample_weight_group():
1192         return dict(
1193             text="Sample Vertex Group",
1194             icon="ops.paint.weight_sample_group",
1195             widget=None,
1196             keymap=(
1197                 ("paint.weight_sample_group", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
1198             ),
1199         )
1200
1201     @ToolDef.from_fn
1202     def gradient():
1203         def draw_settings(context, layout, tool):
1204             brush = context.tool_settings.weight_paint.brush
1205             if brush is not None:
1206                 from .properties_paint_common import UnifiedPaintPanel
1207                 UnifiedPaintPanel.prop_unified_weight(layout, context, brush, "weight", slider=True, text="Weight")
1208             props = tool.operator_properties("paint.weight_gradient")
1209             layout.prop(props, "type")
1210
1211         return dict(
1212             text="Gradient",
1213             icon="ops.paint.weight_gradient",
1214             widget=None,
1215             keymap=(
1216                 ("paint.weight_gradient", dict(), dict(type='EVT_TWEAK_A', value='ANY')),
1217             ),
1218             draw_settings=draw_settings,
1219         )
1220
1221
1222 class _defs_image_generic:
1223
1224     @staticmethod
1225     def poll_uvedit(context):
1226         ob = context.edit_object
1227         if ob is not None:
1228             data = ob.data
1229             if data is not None:
1230                 return bool(getattr(data, "uv_layers", False))
1231         return False
1232
1233     @ToolDef.from_fn
1234     def cursor():
1235         return dict(
1236             text="Cursor",
1237             description=(
1238                 "Set the cursor location, drag to transform"
1239             ),
1240             icon="ops.generic.cursor",
1241             keymap=(
1242                 ("uv.cursor_set", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
1243                 ("transform.translate",
1244                  dict(release_confirm=True, cursor_transform=True),
1245                  dict(type='EVT_TWEAK_A', value='ANY'),
1246                  ),
1247             ),
1248         )
1249
1250
1251 class _defs_image_uv_transform:
1252
1253     @ToolDef.from_fn
1254     def transform():
1255         return dict(
1256             text="Transform",
1257             description=(
1258                 "Supports any combination of grab, rotate & scale at once"
1259             ),
1260             icon="ops.transform.transform",
1261             widget="IMAGE_GGT_gizmo2d",
1262             # No keymap default action, only for gizmo!
1263         )
1264
1265
1266 class _defs_image_uv_select:
1267
1268     @ToolDef.from_fn
1269     def border():
1270         def draw_settings(context, layout, tool):
1271             props = tool.operator_properties("uv.select_box")
1272             layout.prop(props, "deselect")
1273         return dict(
1274             text="Select Box",
1275             icon="ops.generic.select_box",
1276             widget=None,
1277             keymap=(
1278                 ("uv.select_box",
1279                  dict(),
1280                  dict(type='EVT_TWEAK_A', value='ANY')),
1281                 ("uv.select_box",
1282                  dict(deselect=True),
1283                  dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)),
1284             ),
1285             draw_settings=draw_settings,
1286         )
1287
1288     @ToolDef.from_fn
1289     def circle():
1290         def draw_settings(context, layout, tool):
1291             props = tool.operator_properties("uv.select_circle")
1292             layout.prop(props, "radius")
1293         return dict(
1294             text="Select Circle",
1295             icon="ops.generic.select_circle",
1296             widget=None,
1297             keymap=(
1298                 ("uv.select_circle",
1299                  dict(deselect=False),
1300                  dict(type='ACTIONMOUSE', value='PRESS')),
1301                 ("uv.select_circle",
1302                  dict(deselect=True),
1303                  dict(type='ACTIONMOUSE', value='PRESS', ctrl=True)),
1304             ),
1305             draw_settings=draw_settings,
1306         )
1307
1308     @ToolDef.from_fn
1309     def lasso():
1310         return dict(
1311             text="Select Lasso",
1312             icon="ops.generic.select_lasso",
1313             widget=None,
1314             keymap=(
1315                 ("uv.select_lasso",
1316                  dict(deselect=False),
1317                  dict(type='EVT_TWEAK_A', value='ANY')),
1318                 # ("uv.select_lasso",
1319                 #  dict(deselect=True),
1320                 #  dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)),
1321             ),
1322         )
1323
1324
1325 class _defs_image_uv_sculpt:
1326
1327     @staticmethod
1328     def generate_from_brushes(context):
1329         return generate_from_enum_ex(
1330             context,
1331             icon_prefix="brush.uv_sculpt.",
1332             data=context.tool_settings,
1333             attr="uv_sculpt_tool",
1334         )
1335
1336
1337 class _defs_gpencil_paint:
1338     @staticmethod
1339     def draw_color_selector(context, layout):
1340         brush = context.active_gpencil_brush
1341         gp_settings = brush.gpencil_settings
1342         ma = gp_settings.material
1343         row = layout.row(align=True)
1344
1345         icon_id = 0
1346         if ma:
1347             icon_id = ma.id_data.preview.icon_id
1348             txt_ma = ma.name
1349             maxw = 25
1350             if len(txt_ma) > maxw:
1351                 txt_ma = txt_ma[:maxw - 5] + '..' + txt_ma[-3:]
1352         else:
1353             txt_ma = ""
1354
1355         row.label(text="Material:")
1356         sub = row.row()
1357         sub.ui_units_x = 8
1358         sub.popover(
1359             panel="TOPBAR_PT_gpencil_materials",
1360             text=txt_ma,
1361             icon_value=icon_id,
1362         )
1363
1364         row.prop(gp_settings, "pin_material", text="")
1365
1366     @staticmethod
1367     def draw_settings_common(context, layout, tool):
1368         ob = context.active_object
1369         if ob and ob.mode == 'GPENCIL_PAINT':
1370             brush = context.active_gpencil_brush
1371             gp_settings = brush.gpencil_settings
1372
1373             if gp_settings.tool == 'ERASE':
1374                 row = layout.row(align=True)
1375                 row.prop(brush, "size", text="Radius")
1376                 row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE')
1377                 if gp_settings.eraser_mode == 'SOFT':
1378                     row = layout.row(align=True)
1379                     row.prop(gp_settings, "pen_strength", slider=True)
1380                     row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE')
1381             elif gp_settings.tool == 'FILL':
1382                 row = layout.row()
1383                 row.prop(gp_settings, "fill_leak", text="Leak Size")
1384                 row.prop(brush, "size", text="Thickness")
1385                 row.prop(gp_settings, "fill_simplify_level", text="Simplify")
1386
1387                 _defs_gpencil_paint.draw_color_selector(context, layout)
1388
1389                 row = layout.row(align=True)
1390                 row.prop(gp_settings, "fill_draw_mode", text="")
1391                 row.prop(gp_settings, "fill_show_boundary", text="", icon='GRID')
1392
1393             else:  # bgpsettings.tool == 'DRAW':
1394                 row = layout.row(align=True)
1395                 row.prop(brush, "size", text="Radius")
1396                 row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE')
1397                 row = layout.row(align=True)
1398                 row.prop(gp_settings, "pen_strength", slider=True)
1399                 row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE')
1400
1401                 _defs_gpencil_paint.draw_color_selector(context, layout)
1402
1403     @staticmethod
1404     def generate_from_brushes(context):
1405         return generate_from_brushes_ex(
1406             context,
1407             icon_prefix="brush.gpencil.",
1408             brush_test_attr="use_paint_grease_pencil",
1409             brush_category_attr="grease_pencil_tool",
1410             brush_category_layout=(
1411                 ('PENCIL',),
1412                 ('PEN',),
1413                 ('INK',),
1414                 ('INKNOISE',),
1415                 ('BLOCK',),
1416                 ('MARKER',),
1417                 ('FILL',),
1418                 ('SOFT',),
1419                 ('HARD',),
1420                 ('STROKE',),
1421             )
1422         )
1423
1424
1425 class _defs_gpencil_edit:
1426     @ToolDef.from_fn
1427     def bend():
1428         return dict(
1429             text="Bend",
1430             icon="ops.gpencil.edit_bend",
1431             widget=None,
1432             keymap=(
1433                 ("transform.bend",
1434                  dict(release_confirm=True),
1435                  dict(type='EVT_TWEAK_A', value='ANY')),
1436             ),
1437         )
1438
1439     @ToolDef.from_fn
1440     def box_select():
1441         return dict(
1442             text="Select Box",
1443             icon="ops.generic.select_box",
1444             widget=None,
1445             keymap=(
1446                 ("gpencil.select_box",
1447                  dict(),
1448                  dict(type='EVT_TWEAK_A', value='ANY')),
1449             ),
1450         )
1451
1452     @ToolDef.from_fn
1453     def circle_select():
1454         return dict(
1455             text="Select Circle",
1456             icon="ops.generic.select_circle",
1457             widget=None,
1458             keymap=(
1459                 ("gpencil.select_circle",
1460                  dict(),
1461                  dict(type='EVT_TWEAK_A', value='ANY')),
1462             ),
1463         )
1464
1465     @ToolDef.from_fn
1466     def lasso_select():
1467         return dict(
1468             text="Select Lasso",
1469             icon="ops.generic.select_lasso",
1470             widget=None,
1471             keymap=(
1472                 ("gpencil.select_lasso",
1473                  dict(),
1474                  dict(type='EVT_TWEAK_A', value='ANY')),
1475             ),
1476         )
1477
1478     @ToolDef.from_fn
1479     def shear():
1480         return dict(
1481             text="Shear",
1482             icon="ops.gpencil.edit_shear",
1483             widget=None,
1484             keymap=(
1485                 ("transform.shear",
1486                  dict(release_confirm=True),
1487                  dict(type='EVT_TWEAK_A', value='ANY')),
1488             ),
1489         )
1490
1491     @ToolDef.from_fn
1492     def tosphere():
1493         return dict(
1494             text="To Sphere",
1495             icon="ops.transform.tosphere",
1496             widget=None,
1497             keymap=(
1498                 ("transform.tosphere",
1499                  dict(release_confirm=True),
1500                  dict(type='EVT_TWEAK_A', value='ANY')),
1501             ),
1502         )
1503
1504
1505 class _defs_gpencil_sculpt:
1506     @staticmethod
1507     def draw_settings_common(context, layout, tool):
1508         ob = context.active_object
1509         if ob and ob.mode == 'GPENCIL_SCULPT':
1510             tool_settings = context.tool_settings
1511             settings = tool_settings.gpencil_sculpt
1512             tool = settings.tool
1513             brush = settings.brush
1514
1515             layout.prop(brush, "size", slider=True)
1516
1517             row = layout.row(align=True)
1518             row.prop(brush, "strength", slider=True)
1519             row.prop(brush, "use_pressure_strength", text="")
1520
1521             if tool in {'THICKNESS', 'STRENGTH', 'PINCH', 'TWIST'}:
1522                 row.separator()
1523                 row.prop(brush, "direction", expand=True, text="")
1524
1525     @ToolDef.from_fn
1526     def smooth():
1527         def draw_settings(context, layout, tool):
1528             _defs_gpencil_sculpt.draw_settings_common(context, layout, tool)
1529
1530         return dict(
1531             text="Smooth",
1532             icon="ops.gpencil.sculpt_smooth",
1533             widget=None,
1534             keymap=(
1535                 ("gpencil.brush_paint",
1536                  dict(mode='SMOOTH', wait_for_input=False),
1537                  dict(type='EVT_TWEAK_A', value='ANY')),
1538             ),
1539             draw_settings=draw_settings,
1540         )
1541
1542     @ToolDef.from_fn
1543     def thickness():
1544         def draw_settings(context, layout, tool):
1545             _defs_gpencil_sculpt.draw_settings_common(context, layout, tool)
1546
1547         return dict(
1548             text="Thickness",
1549             icon="ops.gpencil.sculpt_thickness",
1550             widget=None,
1551             keymap=(
1552                 ("gpencil.brush_paint",
1553                  dict(mode='THICKNESS', wait_for_input=False),
1554                  dict(type='EVT_TWEAK_A', value='ANY')),
1555             ),
1556             draw_settings=draw_settings,
1557         )
1558
1559     @ToolDef.from_fn
1560     def strength():
1561         def draw_settings(context, layout, tool):
1562             _defs_gpencil_sculpt.draw_settings_common(context, layout, tool)
1563
1564         return dict(
1565             text="Strength",
1566             icon="ops.gpencil.sculpt_strength",
1567             widget=None,
1568             keymap=(
1569                 ("gpencil.brush_paint",
1570                  dict(mode='STRENGTH', wait_for_input=False),
1571                  dict(type='EVT_TWEAK_A', value='ANY')),
1572             ),
1573             draw_settings=draw_settings,
1574         )
1575
1576     @ToolDef.from_fn
1577     def grab():
1578         def draw_settings(context, layout, tool):
1579             _defs_gpencil_sculpt.draw_settings_common(context, layout, tool)
1580
1581         return dict(
1582             text="Grab",
1583             icon="ops.gpencil.sculpt_grab",
1584             widget=None,
1585             keymap=(
1586                 ("gpencil.brush_paint",
1587                  dict(mode='GRAB', wait_for_input=False),
1588                  dict(type='EVT_TWEAK_A', value='ANY')),
1589             ),
1590             draw_settings=draw_settings,
1591         )
1592
1593     @ToolDef.from_fn
1594     def push():
1595         def draw_settings(context, layout, tool):
1596             _defs_gpencil_sculpt.draw_settings_common(context, layout, tool)
1597
1598         return dict(
1599             text="Push",
1600             icon="ops.gpencil.sculpt_push",
1601             widget=None,
1602             keymap=(
1603                 ("gpencil.brush_paint",
1604                  dict(mode='PUSH', wait_for_input=False),
1605                  dict(type='EVT_TWEAK_A', value='ANY')),
1606             ),
1607             draw_settings=draw_settings,
1608         )
1609
1610     @ToolDef.from_fn
1611     def twist():
1612         def draw_settings(context, layout, tool):
1613             _defs_gpencil_sculpt.draw_settings_common(context, layout, tool)
1614
1615         return dict(
1616             text="Twist",
1617             icon="ops.gpencil.sculpt_twist",
1618             widget=None,
1619             keymap=(
1620                 ("gpencil.brush_paint",
1621                  dict(mode='TWIST', wait_for_input=False),
1622                  dict(type='EVT_TWEAK_A', value='ANY')),
1623             ),
1624             draw_settings=draw_settings,
1625         )
1626
1627     @ToolDef.from_fn
1628     def pinch():
1629         def draw_settings(context, layout, tool):
1630             _defs_gpencil_sculpt.draw_settings_common(context, layout, tool)
1631
1632         return dict(
1633             text="Pinch",
1634             icon="ops.gpencil.sculpt_pinch",
1635             widget=None,
1636             keymap=(
1637                 ("gpencil.brush_paint",
1638                  dict(mode='PINCH', wait_for_input=False),
1639                  dict(type='EVT_TWEAK_A', value='ANY')),
1640             ),
1641             draw_settings=draw_settings,
1642         )
1643
1644     @ToolDef.from_fn
1645     def randomize():
1646         def draw_settings(context, layout, tool):
1647             _defs_gpencil_sculpt.draw_settings_common(context, layout, tool)
1648
1649         return dict(
1650             text="Randomize",
1651             icon="ops.gpencil.sculpt_randomize",
1652             widget=None,
1653             keymap=(
1654                 ("gpencil.brush_paint",
1655                  dict(mode='RANDOMIZE', wait_for_input=False),
1656                  dict(type='EVT_TWEAK_A', value='ANY')),
1657             ),
1658             draw_settings=draw_settings,
1659         )
1660
1661     @ToolDef.from_fn
1662     def clone():
1663         def draw_settings(context, layout, tool):
1664             _defs_gpencil_sculpt.draw_settings_common(context, layout, tool)
1665
1666         return dict(
1667             text="Clone",
1668             icon="ops.gpencil.sculpt_clone",
1669             widget=None,
1670             keymap=(
1671                 ("gpencil.brush_paint",
1672                  dict(mode='CLONE', wait_for_input=False),
1673                  dict(type='EVT_TWEAK_A', value='ANY')),
1674             ),
1675             draw_settings=draw_settings,
1676         )
1677
1678
1679 class _defs_gpencil_weight:
1680     @staticmethod
1681     def draw_settings_common(context, layout, tool):
1682         ob = context.active_object
1683         if ob and ob.mode == 'GPENCIL_WEIGHT':
1684             settings = context.tool_settings.gpencil_sculpt
1685             brush = settings.brush
1686
1687             layout.prop(brush, "size", slider=True)
1688
1689             row = layout.row(align=True)
1690             row.prop(brush, "strength", slider=True)
1691             row.prop(brush, "use_pressure_strength", text="")
1692
1693     @ToolDef.from_fn
1694     def paint():
1695         def draw_settings(context, layout, tool):
1696             _defs_gpencil_weight.draw_settings_common(context, layout, tool)
1697
1698         return dict(
1699             text="Draw",
1700             icon="ops.gpencil.sculpt_weight",
1701             widget=None,
1702             keymap=(
1703                 ("gpencil.brush_paint",
1704                  dict(mode='WEIGHT', wait_for_input=False),
1705                  dict(type='EVT_TWEAK_A', value='ANY')),
1706             ),
1707             draw_settings=draw_settings,
1708         )
1709
1710
1711 class TOPBAR_PT_gpencil_materials(Panel):
1712     bl_space_type = 'VIEW_3D'
1713     bl_region_type = 'HEADER'
1714     bl_label = "Materials"
1715     bl_ui_units_x = 14
1716
1717     @classmethod
1718     def poll(cls, context):
1719         ob = context.object
1720         return ob and ob.type == 'GPENCIL'
1721
1722     @staticmethod
1723     def draw(self, context):
1724         layout = self.layout
1725         ob = context.object
1726
1727         if ob:
1728             is_sortable = len(ob.material_slots) > 1
1729             rows = 1
1730             if (is_sortable):
1731                 rows = 10
1732
1733             row = layout.row()
1734
1735             row.template_list("GPENCIL_UL_matslots", "", ob, "material_slots", ob, "active_material_index", rows=rows)
1736
1737             col = row.column(align=True)
1738             col.menu("GPENCIL_MT_color_specials", icon='DOWNARROW_HLT', text="")
1739
1740             if is_sortable:
1741                 col.separator()
1742
1743                 col.operator("object.material_slot_move", icon='TRIA_UP', text="").direction = 'UP'
1744                 col.operator("object.material_slot_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
1745
1746                 col.separator()
1747
1748                 sub = col.column(align=True)
1749                 sub.operator("gpencil.color_isolate", icon='LOCKED', text="").affect_visibility = False
1750                 sub.operator("gpencil.color_isolate", icon='HIDE_OFF', text="").affect_visibility = True
1751
1752
1753 class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel):
1754     bl_space_type = 'IMAGE_EDITOR'
1755     bl_region_type = 'TOOLS'
1756     bl_label = "Tools"  # not visible
1757     bl_options = {'HIDE_HEADER'}
1758
1759     # Satisfy the 'ToolSelectPanelHelper' API.
1760     keymap_prefix = "Image Editor Tool:"
1761
1762     @classmethod
1763     def tools_from_context(cls, context, mode=None):
1764         if mode is None:
1765             if context.space_data is None:
1766                 mode = 'VIEW'
1767             else:
1768                 mode = context.space_data.mode
1769         for tools in (cls._tools[None], cls._tools.get(mode, ())):
1770             for item in tools:
1771                 if not (type(item) is ToolDef) and callable(item):
1772                     yield from item(context)
1773                 else:
1774                     yield item
1775
1776     @classmethod
1777     def tools_all(cls):
1778         yield from cls._tools.items()
1779
1780     # for reuse
1781     _tools_transform = (
1782         _defs_image_uv_transform.transform,
1783     )
1784
1785     _tools_select = (
1786         (
1787             _defs_image_uv_select.border,
1788             _defs_image_uv_select.circle,
1789             _defs_image_uv_select.lasso,
1790         ),
1791     )
1792
1793     _tools_annotate = (
1794         (
1795             _defs_annotate_image.scribble,
1796             _defs_annotate_image.line,
1797             _defs_annotate_image.poly,
1798             _defs_annotate_image.eraser,
1799         ),
1800     )
1801
1802     _tools = {
1803         None: [
1804             # for all modes
1805         ],
1806         'VIEW': [
1807         ],
1808         'UV': [
1809             _defs_image_generic.cursor,
1810             *_tools_select,
1811             None,
1812             *_tools_transform,
1813             None,
1814             *_tools_annotate,
1815             None,
1816             lambda context: (
1817                 _defs_image_uv_sculpt.generate_from_brushes(context)
1818                 if _defs_image_generic.poll_uvedit(context)
1819                 else ()
1820             ),
1821         ],
1822         'MASK': [
1823             None,
1824         ],
1825         'PAINT': [
1826             _defs_texture_paint.generate_from_brushes,
1827         ],
1828     }
1829
1830
1831 class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
1832     bl_space_type = 'VIEW_3D'
1833     bl_region_type = 'TOOLS'
1834     bl_label = "Tools"  # not visible
1835     bl_options = {'HIDE_HEADER'}
1836
1837     # Satisfy the 'ToolSelectPanelHelper' API.
1838     keymap_prefix = "3D View Tool:"
1839
1840     @classmethod
1841     def tools_from_context(cls, context, mode=None):
1842         if mode is None:
1843             mode = context.mode
1844         for tools in (cls._tools[None], cls._tools.get(mode, ())):
1845             for item in tools:
1846                 if not (type(item) is ToolDef) and callable(item):
1847                     yield from item(context)
1848                 else:
1849                     yield item
1850
1851     @classmethod
1852     def tools_all(cls):
1853         yield from cls._tools.items()
1854
1855     # for reuse
1856     _tools_transform = (
1857         _defs_transform.transform,
1858         _defs_transform.translate,
1859         _defs_transform.rotate,
1860         (
1861             _defs_transform.scale,
1862             _defs_transform.scale_cage,
1863         ),
1864     )
1865
1866     _tools_select = (
1867         (
1868             _defs_view3d_select.border,
1869             _defs_view3d_select.circle,
1870             _defs_view3d_select.lasso,
1871         ),
1872     )
1873
1874     _tools_annotate = (
1875         (
1876             _defs_annotate_view3d.scribble,
1877             _defs_annotate_view3d.line,
1878             _defs_annotate_view3d.poly,
1879             _defs_annotate_view3d.eraser,
1880         ),
1881         _defs_view3d_generic.ruler,
1882     )
1883
1884     _tools_gpencil_select = (
1885         (
1886             _defs_gpencil_edit.box_select,
1887             _defs_gpencil_edit.circle_select,
1888             _defs_gpencil_edit.lasso_select,
1889         ),
1890     )
1891
1892     _tools = {
1893         None: [
1894             # Don't use this! because of paint modes.
1895             # _defs_view3d_generic.cursor,
1896             # End group.
1897         ],
1898         'OBJECT': [
1899             _defs_view3d_generic.cursor,
1900             *_tools_select,
1901             None,
1902             *_tools_transform,
1903             None,
1904             *_tools_annotate,
1905         ],
1906         'POSE': [
1907             _defs_view3d_generic.cursor,
1908             *_tools_select,
1909             None,
1910             *_tools_transform,
1911             None,
1912             *_tools_annotate,
1913             None,
1914             (
1915                 _defs_pose.breakdown,
1916                 _defs_pose.push,
1917                 _defs_pose.relax,
1918             ),
1919         ],
1920         'EDIT_ARMATURE': [
1921             _defs_view3d_generic.cursor,
1922             *_tools_select,
1923             None,
1924             *_tools_transform,
1925             None,
1926             *_tools_annotate,
1927             _defs_edit_armature.roll,
1928             (
1929                 _defs_edit_armature.bone_size,
1930                 _defs_edit_armature.bone_envelope,
1931             ),
1932             None,
1933             (
1934                 _defs_edit_armature.extrude,
1935                 _defs_edit_armature.extrude_cursor,
1936             ),
1937         ],
1938         'EDIT_MESH': [
1939             _defs_view3d_generic.cursor,
1940             *_tools_select,
1941             None,
1942             *_tools_transform,
1943             None,
1944             *_tools_annotate,
1945             None,
1946             _defs_edit_mesh.cube_add,
1947             None,
1948             (
1949                 _defs_edit_mesh.extrude,
1950                 _defs_edit_mesh.extrude_normals,
1951                 _defs_edit_mesh.extrude_individual,
1952                 _defs_edit_mesh.extrude_cursor,
1953             ),
1954             _defs_edit_mesh.inset,
1955             _defs_edit_mesh.bevel,
1956             (
1957                 _defs_edit_mesh.loopcut_slide,
1958                 _defs_edit_mesh.offset_edge_loops_slide,
1959             ),
1960             (
1961                 _defs_edit_mesh.knife,
1962                 _defs_edit_mesh.bisect,
1963             ),
1964             _defs_edit_mesh.poly_build,
1965             (
1966                 _defs_edit_mesh.spin,
1967                 _defs_edit_mesh.spin_duplicate,
1968             ),
1969             (
1970                 _defs_edit_mesh.vertex_smooth,
1971                 _defs_edit_mesh.vertex_randomize,
1972             ),
1973             (
1974                 _defs_edit_mesh.edge_slide,
1975                 _defs_edit_mesh.vert_slide,
1976             ),
1977             (
1978                 _defs_edit_mesh.shrink_fatten,
1979                 _defs_edit_mesh.push_pull,
1980             ),
1981             (
1982                 _defs_edit_mesh.shear,
1983                 _defs_edit_mesh.tosphere,
1984             ),
1985             (
1986                 _defs_edit_mesh.rip_region,
1987                 _defs_edit_mesh.rip_edge,
1988             ),
1989         ],
1990         'EDIT_CURVE': [
1991             _defs_view3d_generic.cursor,
1992             *_tools_select,
1993             None,
1994             *_tools_transform,
1995             None,
1996             *_tools_annotate,
1997             None,
1998             _defs_edit_curve.draw,
1999             (
2000                 _defs_edit_curve.extrude,
2001                 _defs_edit_curve.extrude_cursor,
2002             )
2003         ],
2004         'PARTICLE': [
2005             _defs_view3d_generic.cursor,
2006             _defs_particle.generate_from_brushes,
2007         ],
2008         'SCULPT': [
2009             _defs_sculpt.generate_from_brushes,
2010             None,
2011             _defs_sculpt.hide_border,
2012             _defs_sculpt.mask_border,
2013         ],
2014         'PAINT_TEXTURE': [
2015             _defs_texture_paint.generate_from_brushes,
2016         ],
2017         'PAINT_VERTEX': [
2018             _defs_vertex_paint.generate_from_brushes,
2019             None,
2020             lambda context: (
2021                 VIEW3D_PT_tools_active._tools_select
2022                 if _defs_vertex_paint.poll_select_mask(context)
2023                 else ()
2024             ),
2025         ],
2026         'PAINT_WEIGHT': [
2027             # TODO, check for mixed pose mode
2028             _defs_view3d_generic.cursor,
2029             _defs_weight_paint.generate_from_brushes,
2030             None,
2031             _defs_weight_paint.sample_weight,
2032             _defs_weight_paint.sample_weight_group,
2033             None,
2034             lambda context: (
2035                 VIEW3D_PT_tools_active._tools_select
2036                 if _defs_weight_paint.poll_select_mask(context)
2037                 else ()
2038             ),
2039             None,
2040             _defs_weight_paint.gradient,
2041         ],
2042         'GPENCIL_PAINT': [
2043             _defs_gpencil_paint.generate_from_brushes,
2044         ],
2045         'GPENCIL_EDIT': [
2046             _defs_view3d_generic.cursor,
2047             _defs_gpencil_edit.box_select,
2048             _defs_gpencil_edit.circle_select,
2049             _defs_gpencil_edit.lasso_select,
2050             None,
2051             *_tools_transform,
2052             None,
2053             _defs_gpencil_edit.bend,
2054             _defs_gpencil_edit.shear,
2055             _defs_gpencil_edit.tosphere,
2056         ],
2057         'GPENCIL_SCULPT': [
2058             _defs_gpencil_sculpt.smooth,
2059             _defs_gpencil_sculpt.thickness,
2060             _defs_gpencil_sculpt.strength,
2061             _defs_gpencil_sculpt.grab,
2062             _defs_gpencil_sculpt.push,
2063             _defs_gpencil_sculpt.twist,
2064             _defs_gpencil_sculpt.pinch,
2065             _defs_gpencil_sculpt.randomize,
2066             _defs_gpencil_sculpt.clone,
2067             None,
2068             *_tools_gpencil_select,
2069         ],
2070         'GPENCIL_WEIGHT': [
2071             _defs_gpencil_weight.paint,
2072         ],
2073     }
2074
2075 class TOPBAR_PT_annotation_layers(Panel, AnnotationDataPanel):
2076     bl_space_type = 'VIEW_3D'
2077     bl_region_type = 'HEADER'
2078     bl_label = "Layers"
2079     bl_ui_units_x = 14
2080
2081
2082 classes = (
2083     IMAGE_PT_tools_active,
2084     VIEW3D_PT_tools_active,
2085     TOPBAR_PT_gpencil_materials,
2086     TOPBAR_PT_annotation_layers,
2087 )
2088
2089 if __name__ == "__main__":  # only for live edit.
2090     from bpy.utils import register_class
2091     for cls in classes:
2092         register_class(cls)