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