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