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