Vertex Paint: support vertex masking
[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             prefs = context.preferences
199             layout.prop(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
867                 (ob.data.use_paint_mask or
868                  ob.data.use_paint_mask_vertex))
869
870     @staticmethod
871     def generate_from_brushes(context):
872         return generate_from_enum_ex(
873             context,
874             icon_prefix="brush.paint_vertex.",
875             type=bpy.types.Brush,
876             attr="vertex_tool",
877         )
878
879
880 class _defs_texture_paint:
881
882     @staticmethod
883     def generate_from_brushes(context):
884         return generate_from_enum_ex(
885             context,
886             icon_prefix="brush.paint_texture.",
887             type=bpy.types.Brush,
888             attr="image_tool",
889         )
890
891
892 class _defs_weight_paint:
893
894     @staticmethod
895     def poll_select_mask(context):
896         ob = context.active_object
897         return (ob.type == 'MESH' and
898                 (ob.data.use_paint_mask or
899                  ob.data.use_paint_mask_vertex))
900
901     @staticmethod
902     def generate_from_brushes(context):
903         return generate_from_enum_ex(
904             context,
905             icon_prefix="brush.paint_weight.",
906             type=bpy.types.Brush,
907             attr="weight_tool",
908         )
909
910     @ToolDef.from_fn
911     def sample_weight():
912         return dict(
913             text="Sample Weight",
914             icon="ops.paint.weight_sample",
915             widget=None,
916             keymap=(),
917         )
918
919     @ToolDef.from_fn
920     def sample_weight_group():
921         return dict(
922             text="Sample Vertex Group",
923             icon="ops.paint.weight_sample_group",
924             widget=None,
925             keymap=(),
926         )
927
928     @ToolDef.from_fn
929     def gradient():
930         def draw_settings(context, layout, tool):
931             brush = context.tool_settings.weight_paint.brush
932             if brush is not None:
933                 from .properties_paint_common import UnifiedPaintPanel
934                 UnifiedPaintPanel.prop_unified_weight(layout, context, brush, "weight", slider=True, text="Weight")
935             props = tool.operator_properties("paint.weight_gradient")
936             layout.prop(props, "type")
937
938         return dict(
939             text="Gradient",
940             icon="ops.paint.weight_gradient",
941             widget=None,
942             keymap=(),
943             draw_settings=draw_settings,
944         )
945
946
947 class _defs_image_generic:
948
949     @staticmethod
950     def poll_uvedit(context):
951         ob = context.edit_object
952         if ob is not None:
953             data = ob.data
954             if data is not None:
955                 return bool(getattr(data, "uv_layers", False))
956         return False
957
958     @ToolDef.from_fn
959     def cursor():
960         return dict(
961             text="Cursor",
962             description=(
963                 "Set the cursor location, drag to transform"
964             ),
965             icon="ops.generic.cursor",
966             keymap=(),
967         )
968
969
970 class _defs_image_uv_transform:
971
972     @ToolDef.from_fn
973     def transform():
974         return dict(
975             text="Transform",
976             description=(
977                 "Supports any combination of grab, rotate & scale at once"
978             ),
979             icon="ops.transform.transform",
980             widget="IMAGE_GGT_gizmo2d",
981             # No keymap default action, only for gizmo!
982         )
983
984
985 class _defs_image_uv_select:
986
987     @ToolDef.from_fn
988     def select():
989         def draw_settings(context, layout, tool):
990             pass
991         return dict(
992             text="Select",
993             icon="ops.generic.select",
994             widget=None,
995             keymap=(),
996             draw_settings=draw_settings,
997         )
998
999     @ToolDef.from_fn
1000     def box():
1001         def draw_settings(context, layout, tool):
1002             props = tool.operator_properties("uv.select_box")
1003             layout.prop(props, "mode", expand=True)
1004         return dict(
1005             text="Select Box",
1006             icon="ops.generic.select_box",
1007             widget=None,
1008             keymap=(),
1009             draw_settings=draw_settings,
1010         )
1011
1012     @ToolDef.from_fn
1013     def lasso():
1014         def draw_settings(context, layout, tool):
1015             props = tool.operator_properties("uv.select_lasso")
1016             layout.prop(props, "mode", expand=True)
1017         return dict(
1018             text="Select Lasso",
1019             icon="ops.generic.select_lasso",
1020             widget=None,
1021             keymap=(),
1022             draw_settings=draw_settings,
1023         )
1024
1025     @ToolDef.from_fn
1026     def circle():
1027         def draw_settings(context, layout, tool):
1028             props = tool.operator_properties("uv.select_circle")
1029             layout.prop(props, "radius")
1030         return dict(
1031             text="Select Circle",
1032             icon="ops.generic.select_circle",
1033             widget=None,
1034             keymap=(),
1035             draw_settings=draw_settings,
1036         )
1037
1038
1039 class _defs_image_uv_sculpt:
1040
1041     @staticmethod
1042     def generate_from_brushes(context):
1043         return generate_from_enum_ex(
1044             context,
1045             icon_prefix="brush.uv_sculpt.",
1046             type=bpy.types.ToolSettings,
1047             attr="uv_sculpt_tool",
1048         )
1049
1050
1051 class _defs_gpencil_paint:
1052
1053     @staticmethod
1054     def generate_from_brushes(context):
1055         return generate_from_enum_ex(
1056             context,
1057             icon_prefix="brush.gpencil_draw.",
1058             type=bpy.types.Brush,
1059             attr="gpencil_tool",
1060             tooldef_keywords=dict(
1061                 operator="gpencil.draw",
1062             ),
1063         )
1064
1065     @ToolDef.from_fn
1066     def line():
1067         return dict(
1068             text="Line",
1069             icon="ops.gpencil.primitive_line",
1070             cursor='CROSSHAIR',
1071             widget=None,
1072             keymap=(),
1073         )
1074
1075     @ToolDef.from_fn
1076     def box():
1077         return dict(
1078             text="Box",
1079             icon="ops.gpencil.primitive_box",
1080             cursor='CROSSHAIR',
1081             widget=None,
1082             keymap=(),
1083         )
1084
1085     @ToolDef.from_fn
1086     def circle():
1087         return dict(
1088             text="Circle",
1089             icon="ops.gpencil.primitive_circle",
1090             cursor='CROSSHAIR',
1091             widget=None,
1092             keymap=(),
1093         )
1094
1095     @ToolDef.from_fn
1096     def arc():
1097         return dict(
1098             text="Arc",
1099             icon="ops.gpencil.primitive_arc",
1100             cursor='CROSSHAIR',
1101             widget=None,
1102             keymap=(),
1103         )
1104
1105     @ToolDef.from_fn
1106     def curve():
1107         return dict(
1108             text="Curve",
1109             icon="ops.gpencil.primitive_curve",
1110             cursor='CROSSHAIR',
1111             widget=None,
1112             keymap=(),
1113         )
1114
1115
1116 class _defs_gpencil_edit:
1117     @ToolDef.from_fn
1118     def bend():
1119         return dict(
1120             text="Bend",
1121             icon="ops.gpencil.edit_bend",
1122             widget=None,
1123             keymap=(),
1124         )
1125
1126     @ToolDef.from_fn
1127     def select():
1128         def draw_settings(context, layout, tool):
1129             pass
1130         return dict(
1131             text="Select",
1132             icon="ops.generic.select",
1133             widget=None,
1134             keymap=(),
1135             draw_settings=draw_settings,
1136         )
1137
1138     @ToolDef.from_fn
1139     def box_select():
1140         def draw_settings(context, layout, tool):
1141             props = tool.operator_properties("gpencil.select_box")
1142             layout.prop(props, "mode", expand=True)
1143         return dict(
1144             text="Select Box",
1145             icon="ops.generic.select_box",
1146             widget=None,
1147             keymap=(),
1148             draw_settings=draw_settings,
1149         )
1150
1151     @ToolDef.from_fn
1152     def lasso_select():
1153         def draw_settings(context, layout, tool):
1154             props = tool.operator_properties("gpencil.select_lasso")
1155             layout.prop(props, "mode", expand=True)
1156         return dict(
1157             text="Select Lasso",
1158             icon="ops.generic.select_lasso",
1159             widget=None,
1160             keymap=(),
1161             draw_settings=draw_settings,
1162         )
1163
1164     @ToolDef.from_fn
1165     def circle_select():
1166         return dict(
1167             text="Select Circle",
1168             icon="ops.generic.select_circle",
1169             widget=None,
1170             keymap=(),
1171         )
1172
1173     @ToolDef.from_fn
1174     def shear():
1175         return dict(
1176             text="Shear",
1177             icon="ops.gpencil.edit_shear",
1178             widget=None,
1179             keymap=(),
1180         )
1181
1182     @ToolDef.from_fn
1183     def tosphere():
1184         return dict(
1185             text="To Sphere",
1186             icon="ops.transform.tosphere",
1187             widget=None,
1188             keymap=(),
1189         )
1190
1191
1192 class _defs_gpencil_sculpt:
1193
1194     @staticmethod
1195     def generate_from_brushes(context):
1196         return generate_from_enum_ex(
1197             context,
1198             icon_prefix="ops.gpencil.sculpt_",
1199             type=bpy.types.GPencilSculptSettings,
1200             attr="sculpt_tool",
1201         )
1202
1203
1204 class _defs_gpencil_weight:
1205
1206     @staticmethod
1207     def generate_from_brushes(context):
1208         return generate_from_enum_ex(
1209             context,
1210             icon_prefix="ops.gpencil.sculpt_",
1211             type=bpy.types.GPencilSculptSettings,
1212             attr="weight_tool",
1213         )
1214
1215
1216 class _defs_node_select:
1217
1218     @ToolDef.from_fn
1219     def select():
1220         def draw_settings(context, layout, tool):
1221             pass
1222         return dict(
1223             text="Select",
1224             icon="ops.generic.select",
1225             widget=None,
1226             keymap="Node Tool: Select",
1227             draw_settings=draw_settings,
1228         )
1229
1230     @ToolDef.from_fn
1231     def box():
1232         def draw_settings(context, layout, tool):
1233             props = tool.operator_properties("node.select_box")
1234             layout.prop(props, "deselect")
1235             pass
1236         return dict(
1237             text="Select Box",
1238             icon="ops.generic.select_box",
1239             widget=None,
1240             keymap="Node Tool: Select Box",
1241             draw_settings=draw_settings,
1242         )
1243
1244     @ToolDef.from_fn
1245     def lasso():
1246         def draw_settings(context, layout, tool):
1247             props = tool.operator_properties("node.select_lasso")
1248             layout.prop(props, "deselect")
1249             pass
1250         return dict(
1251             text="Select Lasso",
1252             icon="ops.generic.select_lasso",
1253             widget=None,
1254             keymap="Node Tool: Select Lasso",
1255             draw_settings=draw_settings,
1256         )
1257
1258
1259 class _defs_node_edit:
1260
1261     @ToolDef.from_fn
1262     def links_cut():
1263         return dict(
1264             text="Links Cut",
1265             icon="ops.mesh.knife_tool",
1266             widget=None,
1267             keymap="Node Tool: Links Cut",
1268         )
1269
1270
1271 class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel):
1272     bl_space_type = 'IMAGE_EDITOR'
1273     bl_region_type = 'TOOLS'
1274     bl_label = "Tools"  # not visible
1275     bl_options = {'HIDE_HEADER'}
1276
1277     # Satisfy the 'ToolSelectPanelHelper' API.
1278     keymap_prefix = "Image Editor Tool:"
1279
1280     @classmethod
1281     def tools_from_context(cls, context, mode=None):
1282         if mode is None:
1283             if context.space_data is None:
1284                 mode = 'VIEW'
1285             else:
1286                 mode = context.space_data.mode
1287         for tools in (cls._tools[None], cls._tools.get(mode, ())):
1288             for item in tools:
1289                 if not (type(item) is ToolDef) and callable(item):
1290                     yield from item(context)
1291                 else:
1292                     yield item
1293
1294     @classmethod
1295     def tools_all(cls):
1296         yield from cls._tools.items()
1297
1298     # for reuse
1299     _tools_transform = (
1300         _defs_image_uv_transform.transform,
1301     )
1302
1303     _tools_select = (
1304         (
1305             _defs_image_uv_select.select,
1306             _defs_image_uv_select.box,
1307             _defs_image_uv_select.circle,
1308             _defs_image_uv_select.lasso,
1309         ),
1310     )
1311
1312     _tools_annotate = (
1313         (
1314             _defs_annotate.scribble,
1315             _defs_annotate.line,
1316             _defs_annotate.poly,
1317             _defs_annotate.eraser,
1318         ),
1319     )
1320
1321     _tools = {
1322         None: [
1323             # for all modes
1324         ],
1325         'VIEW': [
1326         ],
1327         'UV': [
1328             *_tools_select,
1329             _defs_image_generic.cursor,
1330             None,
1331             *_tools_transform,
1332             None,
1333             *_tools_annotate,
1334             None,
1335             lambda context: (
1336                 _defs_image_uv_sculpt.generate_from_brushes(context)
1337                 if _defs_image_generic.poll_uvedit(context)
1338                 else ()
1339             ),
1340         ],
1341         'MASK': [
1342             None,
1343         ],
1344         'PAINT': [
1345             _defs_texture_paint.generate_from_brushes,
1346         ],
1347     }
1348
1349
1350 class NODE_PT_tools_active(ToolSelectPanelHelper, Panel):
1351     bl_space_type = 'NODE_EDITOR'
1352     bl_region_type = 'TOOLS'
1353     bl_label = "Tools"  # not visible
1354     bl_options = {'HIDE_HEADER'}
1355
1356     # Satisfy the 'ToolSelectPanelHelper' API.
1357     keymap_prefix = "Node Editor Tool:"
1358
1359     @classmethod
1360     def tools_from_context(cls, context, mode=None):
1361         if mode is None:
1362             if context.space_data is None:
1363                 mode = None
1364             else:
1365                 mode = context.space_data.tree_type
1366         for tools in (cls._tools[None], cls._tools.get(mode, ())):
1367             for item in tools:
1368                 if not (type(item) is ToolDef) and callable(item):
1369                     yield from item(context)
1370                 else:
1371                     yield item
1372
1373     @classmethod
1374     def tools_all(cls):
1375         yield from cls._tools.items()
1376
1377     _tools_select = (
1378         (
1379             _defs_node_select.select,
1380             _defs_node_select.box,
1381             _defs_node_select.lasso,
1382         ),
1383     )
1384
1385     _tools_annotate = (
1386         (
1387             _defs_annotate.scribble,
1388             _defs_annotate.line,
1389             _defs_annotate.poly,
1390             _defs_annotate.eraser,
1391         ),
1392     )
1393
1394     _tools = {
1395         None: [
1396             *_tools_select,
1397             None,
1398             *_tools_annotate,
1399             None,
1400             _defs_node_edit.links_cut,
1401         ],
1402     }
1403
1404
1405 class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
1406     bl_space_type = 'VIEW_3D'
1407     bl_region_type = 'TOOLS'
1408     bl_label = "Tools"  # not visible
1409     bl_options = {'HIDE_HEADER'}
1410
1411     # Satisfy the 'ToolSelectPanelHelper' API.
1412     keymap_prefix = "3D View Tool:"
1413
1414     @classmethod
1415     def tools_from_context(cls, context, mode=None):
1416         if mode is None:
1417             mode = context.mode
1418         for tools in (cls._tools[None], cls._tools.get(mode, ())):
1419             for item in tools:
1420                 if not (type(item) is ToolDef) and callable(item):
1421                     yield from item(context)
1422                 else:
1423                     yield item
1424
1425     @classmethod
1426     def tools_all(cls):
1427         yield from cls._tools.items()
1428
1429     # for reuse
1430     _tools_transform = (
1431         _defs_transform.transform,
1432         _defs_transform.translate,
1433         _defs_transform.rotate,
1434         (
1435             _defs_transform.scale,
1436             _defs_transform.scale_cage,
1437         ),
1438     )
1439
1440     _tools_select = (
1441         (
1442             _defs_view3d_select.select,
1443             _defs_view3d_select.box,
1444             _defs_view3d_select.circle,
1445             _defs_view3d_select.lasso,
1446         ),
1447     )
1448
1449     _tools_annotate = (
1450         (
1451             _defs_annotate.scribble,
1452             _defs_annotate.line,
1453             _defs_annotate.poly,
1454             _defs_annotate.eraser,
1455         ),
1456         _defs_view3d_generic.ruler,
1457     )
1458
1459     _tools_gpencil_select = (
1460         (
1461             _defs_gpencil_edit.select,
1462             _defs_gpencil_edit.box_select,
1463             _defs_gpencil_edit.circle_select,
1464             _defs_gpencil_edit.lasso_select,
1465         ),
1466     )
1467
1468     _tools_default = (
1469         *_tools_select,
1470         _defs_view3d_generic.cursor,
1471         None,
1472         *_tools_transform,
1473         None,
1474         *_tools_annotate,
1475     )
1476
1477     _tools = {
1478         None: [
1479             # Don't use this! because of paint modes.
1480             # _defs_view3d_generic.cursor,
1481             # End group.
1482         ],
1483         'OBJECT': [
1484             *_tools_default,
1485         ],
1486         'POSE': [
1487             *_tools_default,
1488             None,
1489             (
1490                 _defs_pose.breakdown,
1491                 _defs_pose.push,
1492                 _defs_pose.relax,
1493             ),
1494         ],
1495         'EDIT_ARMATURE': [
1496             *_tools_default,
1497             None,
1498             _defs_edit_armature.roll,
1499             (
1500                 _defs_edit_armature.bone_size,
1501                 _defs_edit_armature.bone_envelope,
1502             ),
1503             None,
1504             (
1505                 _defs_edit_armature.extrude,
1506                 _defs_edit_armature.extrude_cursor,
1507             ),
1508         ],
1509         'EDIT_MESH': [
1510             *_tools_default,
1511             None,
1512             _defs_edit_mesh.cube_add,
1513             None,
1514             (
1515                 _defs_edit_mesh.extrude,
1516                 _defs_edit_mesh.extrude_normals,
1517                 _defs_edit_mesh.extrude_individual,
1518                 _defs_edit_mesh.extrude_cursor,
1519             ),
1520             _defs_edit_mesh.inset,
1521             _defs_edit_mesh.bevel,
1522             (
1523                 _defs_edit_mesh.loopcut_slide,
1524                 _defs_edit_mesh.offset_edge_loops_slide,
1525             ),
1526             (
1527                 _defs_edit_mesh.knife,
1528                 _defs_edit_mesh.bisect,
1529             ),
1530             _defs_edit_mesh.poly_build,
1531             (
1532                 _defs_edit_mesh.spin,
1533                 _defs_edit_mesh.spin_duplicate,
1534             ),
1535             (
1536                 _defs_edit_mesh.vertex_smooth,
1537                 _defs_edit_mesh.vertex_randomize,
1538             ),
1539             (
1540                 _defs_edit_mesh.edge_slide,
1541                 _defs_edit_mesh.vert_slide,
1542             ),
1543             (
1544                 _defs_edit_mesh.shrink_fatten,
1545                 _defs_edit_mesh.push_pull,
1546             ),
1547             (
1548                 _defs_edit_mesh.shear,
1549                 _defs_edit_mesh.tosphere,
1550             ),
1551             (
1552                 _defs_edit_mesh.rip_region,
1553                 _defs_edit_mesh.rip_edge,
1554             ),
1555         ],
1556         'EDIT_CURVE': [
1557             *_tools_default,
1558             None,
1559             _defs_edit_curve.tilt,
1560             _defs_edit_curve.draw,
1561             (
1562                 _defs_edit_curve.extrude,
1563                 _defs_edit_curve.extrude_cursor,
1564             ),
1565         ],
1566         'EDIT_SURFACE': [
1567             *_tools_default,
1568         ],
1569         'EDIT_METABALL': [
1570             *_tools_default,
1571         ],
1572         'EDIT_LATTICE': [
1573             *_tools_default,
1574         ],
1575         'PARTICLE': [
1576             _defs_view3d_generic.cursor,
1577             _defs_particle.generate_from_brushes,
1578         ],
1579         'SCULPT': [
1580             _defs_sculpt.generate_from_brushes,
1581             None,
1582             _defs_sculpt.hide_border,
1583             _defs_sculpt.mask_border,
1584         ],
1585         'PAINT_TEXTURE': [
1586             _defs_texture_paint.generate_from_brushes,
1587         ],
1588         'PAINT_VERTEX': [
1589             _defs_vertex_paint.generate_from_brushes,
1590             None,
1591             lambda context: (
1592                 VIEW3D_PT_tools_active._tools_select
1593                 if _defs_vertex_paint.poll_select_mask(context)
1594                 else ()
1595             ),
1596         ],
1597         'PAINT_WEIGHT': [
1598             # TODO, check for mixed pose mode
1599             _defs_view3d_generic.cursor,
1600             _defs_weight_paint.generate_from_brushes,
1601             None,
1602             _defs_weight_paint.sample_weight,
1603             _defs_weight_paint.sample_weight_group,
1604             None,
1605             lambda context: (
1606                 VIEW3D_PT_tools_active._tools_select
1607                 if _defs_weight_paint.poll_select_mask(context)
1608                 else ()
1609             ),
1610             None,
1611             _defs_weight_paint.gradient,
1612         ],
1613         'PAINT_GPENCIL': [
1614             _defs_view3d_generic.cursor,
1615             None,
1616             _defs_gpencil_paint.generate_from_brushes,
1617             None,
1618             _defs_gpencil_paint.line,
1619             _defs_gpencil_paint.arc,
1620             _defs_gpencil_paint.curve,
1621             _defs_gpencil_paint.box,
1622             _defs_gpencil_paint.circle,
1623         ],
1624         'EDIT_GPENCIL': [
1625             *_tools_gpencil_select,
1626             _defs_view3d_generic.cursor,
1627             None,
1628             *_tools_transform,
1629             None,
1630             _defs_gpencil_edit.bend,
1631             _defs_gpencil_edit.shear,
1632             _defs_gpencil_edit.tosphere,
1633         ],
1634         'SCULPT_GPENCIL': [
1635             *_tools_gpencil_select,
1636             None,
1637             _defs_gpencil_sculpt.generate_from_brushes,
1638         ],
1639         'WEIGHT_GPENCIL': [
1640             _defs_gpencil_weight.generate_from_brushes,
1641         ],
1642     }
1643
1644
1645 classes = (
1646     IMAGE_PT_tools_active,
1647     NODE_PT_tools_active,
1648     VIEW3D_PT_tools_active,
1649 )
1650
1651 if __name__ == "__main__":  # only for live edit.
1652     from bpy.utils import register_class
1653     for cls in classes:
1654         register_class(cls)