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