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