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