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