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