Tool System: per space/mode tool support
[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 from bpy.types import Panel
28
29 from .space_toolsystem_common import (
30     ToolSelectPanelHelper,
31     ToolDef,
32 )
33
34
35 def generate_from_brushes_ex(
36         context, *,
37         icon_prefix,
38         brush_test_attr,
39         brush_category_attr,
40         brush_category_layout,
41 ):
42     # Categories
43     brush_categories = {}
44     for brush in context.blend_data.brushes:
45         if getattr(brush, brush_test_attr):
46             category = getattr(brush, brush_category_attr)
47             name = brush.name
48             brush_categories.setdefault(category, []).append(
49                 ToolDef.from_dict(
50                     dict(
51                         text=name,
52                         icon=icon_prefix + category.lower(),
53                         data_block=name,
54                     )
55                 )
56             )
57
58     def tools_from_brush_group(groups):
59         assert(type(groups) is tuple)
60         if len(groups) == 1:
61             tool_defs = tuple(brush_categories.pop(groups[0], ()))
62         else:
63             tool_defs = tuple(item for g in groups for item in brush_categories.pop(g, ()))
64         if len(tool_defs) > 1:
65             return (tool_defs,)
66         else:
67             return tool_defs
68
69     # Each item below is a single toolbar entry:
70     # Grouped for multiple or none if no brushes are found.
71     tool_defs = tuple(
72         tool_def
73         for category in brush_category_layout
74         for tool_def in tools_from_brush_group(category)
75     )
76     # Ensure we use all types.
77     if brush_categories:
78         print(brush_categories)
79     assert(len(brush_categories) == 0)
80     return tool_defs
81
82
83 class _defs_view3d_generic:
84     @ToolDef.from_fn
85     def cursor():
86         return dict(
87             text="Cursor",
88             icon="ops.generic.cursor",
89             keymap=(
90                 ("view3d.cursor3d", dict(), dict(type='ACTIONMOUSE', value='CLICK')),
91             ),
92         )
93
94     @ToolDef.from_fn
95     def ruler():
96         return dict(
97             text="Ruler/Protractor",
98             icon="ops.view3d.ruler",
99             widget="VIEW3D_WGT_ruler",
100             keymap=(
101                 ("view3d.ruler_add", dict(), dict(type='EVT_TWEAK_A', value='ANY')),
102             ),
103         )
104
105
106 class _defs_transform:
107
108     @ToolDef.from_fn
109     def translate():
110         return dict(
111             text="Move",
112             icon="ops.transform.translate",
113             widget="TRANSFORM_WGT_manipulator",
114             keymap=(
115                 ("transform.translate", dict(release_confirm=True), dict(type='EVT_TWEAK_A', value='ANY')),
116             ),
117         )
118
119     @ToolDef.from_fn
120     def rotate():
121         return dict(
122             text="Rotate",
123             icon="ops.transform.rotate",
124             widget="TRANSFORM_WGT_manipulator",
125             keymap=(
126                 ("transform.rotate", dict(release_confirm=True), dict(type='EVT_TWEAK_A', value='ANY')),
127             ),
128         )
129
130     @ToolDef.from_fn
131     def scale():
132         return dict(
133             text="Scale",
134             icon="ops.transform.resize",
135             widget="TRANSFORM_WGT_manipulator",
136             keymap=(
137                 ("transform.resize", dict(release_confirm=True), dict(type='EVT_TWEAK_A', value='ANY')),
138             ),
139         )
140
141     @ToolDef.from_fn
142     def scale_cage():
143         return dict(
144             text="Scale Cage",
145             icon="ops.transform.resize.cage",
146             widget="VIEW3D_WGT_xform_cage",
147         )
148
149     @ToolDef.from_fn
150     def transform():
151         return dict(
152             text="Transform",
153             icon="ops.transform.transform",
154             widget="TRANSFORM_WGT_manipulator",
155             # No keymap default action, only for manipulators!
156         )
157
158
159 class _defs_view3d_select:
160
161     @ToolDef.from_fn
162     def border():
163         return dict(
164             text="Select Border",
165             icon="ops.generic.select_border",
166             widget=None,
167             keymap=(
168                 ("view3d.select_border",
169                  dict(deselect=False),
170                  dict(type='EVT_TWEAK_A', value='ANY')),
171                 ("view3d.select_border",
172                  dict(deselect=True),
173                  dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)),
174             ),
175         )
176
177     @ToolDef.from_fn
178     def circle():
179         return dict(
180             text="Select Circle",
181             icon="ops.generic.select_circle",
182             widget=None,
183             keymap=(
184                 ("view3d.select_circle",
185                  dict(deselect=False),
186                  dict(type='ACTIONMOUSE', value='PRESS')),
187                 ("view3d.select_circle",
188                  dict(deselect=True),
189                  dict(type='ACTIONMOUSE', value='PRESS', ctrl=True)),
190             ),
191         )
192
193     @ToolDef.from_fn
194     def lasso():
195         return dict(
196             text="Select Lasso",
197             icon="ops.generic.select_lasso",
198             widget=None,
199             keymap=(
200                 ("view3d.select_lasso",
201                  dict(deselect=False),
202                  dict(type='EVT_TWEAK_A', value='ANY')),
203                 ("view3d.select_lasso",
204                  dict(deselect=True),
205                  dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)),
206             ),
207         )
208 # -----------------------------------------------------------------------------
209 # Object Modes (named based on context.mode)
210
211
212 class _defs_edit_armature:
213
214     @ToolDef.from_fn
215     def roll():
216         return dict(
217             text="Roll",
218             icon="ops.armature.bone.roll",
219             widget=None,
220             keymap=(
221                 ("transform.transform",
222                  dict(release_confirm=True, mode='BONE_ROLL'),
223                  dict(type='EVT_TWEAK_A', value='ANY'),),
224             ),
225         )
226
227     @ToolDef.from_fn
228     def bone_envelope():
229         return dict(
230             text="Bone Envelope",
231             icon="ops.transform.bone_envelope",
232             widget=None,
233             keymap=(
234                 ("transform.transform",
235                  dict(release_confirm=True, mode='BONE_ENVELOPE'),
236                  dict(type='ACTIONMOUSE', value='PRESS')),
237             ),
238         )
239
240     @ToolDef.from_fn
241     def bone_size():
242         return dict(
243             text="Bone Size",
244             icon="ops.transform.bone_size",
245             widget=None,
246             keymap=(
247                 ("transform.transform",
248                  dict(release_confirm=True, mode='BONE_SIZE'),
249                  dict(type='ACTIONMOUSE', value='PRESS')),
250             ),
251         )
252
253     @ToolDef.from_fn
254     def extrude():
255         return dict(
256             text="Extrude",
257             icon="ops.armature.extrude_move",
258             widget=None,
259             keymap=(
260                 ("armature.click_extrude", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
261             ),
262         )
263
264     @ToolDef.from_fn
265     def extrude_cursor():
266         return dict(
267             text="Extrude to Cursor",
268             icon="ops.armature.extrude_cursor",
269             widget=None,
270             keymap=(
271                 ("armature.click_extrude", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
272             ),
273         )
274
275
276 class _defs_edit_mesh:
277
278
279     @ToolDef.from_fn
280     def cube_add():
281         return dict(
282             text="Add Cube",
283             icon="ops.mesh.primitive_cube_add_manipulator",
284             widget=None,
285             keymap=(
286                 ("view3d.cursor3d", dict(), dict(type='ACTIONMOUSE', value='CLICK')),
287                 ("mesh.primitive_cube_add_manipulator", dict(), dict(type='EVT_TWEAK_A', value='ANY')),
288             ),
289         )
290
291     @ToolDef.from_fn
292     def rip_region():
293         def draw_settings(context, layout):
294             wm = context.window_manager
295             props = wm.operator_properties_last("mesh.rip_move")
296             props_macro = props.MESH_OT_rip
297             layout.prop(props_macro, "use_fill")
298
299         return dict(
300             text="Rip Region",
301             icon="ops.mesh.rip",
302             widget=None,
303             keymap=(
304                 ("mesh.rip_move",
305                  dict(TRANSFORM_OT_translate=dict(release_confirm=True)),
306                  dict(type='ACTIONMOUSE', value='PRESS')),
307             ),
308             draw_settings=draw_settings,
309         )
310
311     @ToolDef.from_fn
312     def rip_edge():
313         return dict(
314             text="Rip Edge",
315             icon="ops.mesh.rip_edge",
316             widget=None,
317             keymap=(
318                 ("mesh.rip_edge_edge_move", dict(),
319                  dict(type='ACTIONMOUSE', value='PRESS')),
320             ),
321         )
322
323     @ToolDef.from_fn
324     def poly_build():
325         return dict(
326             text="Poly Build",
327             icon="ops.mesh.polybuild_hover",
328             widget=None,
329             keymap=(
330                 ("mesh.polybuild_face_at_cursor_move",
331                  dict(TRANSFORM_OT_translate=dict(release_confirm=True)),
332                  dict(type='ACTIONMOUSE', value='PRESS')),
333                 ("mesh.polybuild_split_at_cursor_move",
334                  dict(TRANSFORM_OT_translate=dict(release_confirm=True)),
335                  dict(type='ACTIONMOUSE', value='PRESS', ctrl=True)),
336                 ("mesh.polybuild_dissolve_at_cursor", dict(), dict(type='ACTIONMOUSE', value='CLICK', alt=True)),
337                 ("mesh.polybuild_hover", dict(use_boundary=False), dict(type='MOUSEMOVE', value='ANY', alt=True)),
338                 ("mesh.polybuild_hover", dict(use_boundary=True), dict(type='MOUSEMOVE', value='ANY', any=True)),
339             ),
340         )
341
342     @ToolDef.from_fn
343     def edge_slide():
344         return dict(
345             text="Edge Slide",
346             icon="ops.transform.edge_slide",
347             widget=None,
348             keymap=(
349                 ("transform.edge_slide", dict(release_confirm=True),
350                  dict(type='ACTIONMOUSE', value='PRESS')
351                  ),
352             ),
353         )
354
355     @ToolDef.from_fn
356     def vert_slide():
357         return dict(
358             text="Vertex Slide",
359             icon="ops.transform.vert_slide",
360             widget=None,
361             keymap=(
362                 ("transform.vert_slide", dict(release_confirm=True),
363                  dict(type='ACTIONMOUSE', value='PRESS')),
364             ),
365         )
366
367     @ToolDef.from_fn
368     def spin():
369         return dict(
370             text="Spin",
371             icon="ops.mesh.spin",
372             widget=None,
373             keymap=(
374                 ("mesh.spin", dict(),
375                  dict(type='ACTIONMOUSE', value='PRESS')),
376             ),
377         )
378
379     @ToolDef.from_fn
380     def spin_duplicate():
381         return dict(
382             text="Spin (Duplicate)",
383             icon="ops.mesh.spin.duplicate",
384             widget=None,
385             keymap=(
386                 ("mesh.spin", dict(dupli=True),
387                  dict(type='ACTIONMOUSE', value='PRESS')),
388             ),
389         )
390
391     @ToolDef.from_fn
392     def inset():
393         def draw_settings(context, layout):
394             wm = context.window_manager
395             props = wm.operator_properties_last("mesh.inset")
396             layout.prop(props, "use_outset")
397             layout.prop(props, "use_individual")
398             layout.prop(props, "use_even_offset")
399             layout.prop(props, "use_relative_offset")
400
401         return dict(
402             text="Inset Faces",
403             icon="ops.mesh.inset",
404             widget=None,
405             keymap=(
406                 ("mesh.inset", dict(release_confirm=True),
407                  dict(type='ACTIONMOUSE', value='PRESS')),
408             ),
409             draw_settings=draw_settings,
410         )
411
412     @ToolDef.from_fn
413     def bevel():
414         return dict(
415             text="Bevel",
416             icon="ops.mesh.bevel",
417             widget=None,
418             keymap=(
419                 ("mesh.bevel", dict(),
420                  dict(type='ACTIONMOUSE', value='PRESS')),
421             ),
422         )
423
424     @ToolDef.from_fn
425     def extrude():
426         return dict(
427             text="Extrude Region",
428             icon="ops.mesh.extrude_region_move",
429             widget="MESH_WGT_extrude",
430             keymap=(
431                 ("mesh.extrude_context_move", dict(TRANSFORM_OT_translate=dict(release_confirm=True)),
432                  dict(type='EVT_TWEAK_A', value='ANY')),
433             ),
434         )
435
436     @ToolDef.from_fn
437     def extrude_individual():
438         return dict(
439             text="Extrude Individual",
440             icon="ops.mesh.extrude_faces_move",
441             widget=None,
442             keymap=(
443                 ("mesh.extrude_faces_move", dict(TRANSFORM_OT_shrink_fatten=dict(release_confirm=True)),
444                  dict(type='EVT_TWEAK_A', value='ANY')),
445             ),
446         )
447
448     @ToolDef.from_fn
449     def extrude_cursor():
450         return dict(
451             text="Extrude to Cursor",
452             icon="ops.mesh.dupli_extrude_cursor",
453             widget=None,
454             keymap=(
455                 ("mesh.dupli_extrude_cursor", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
456             ),
457         )
458
459     @ToolDef.from_fn
460     def loopcut_slide():
461         return dict(
462             text="Loop Cut",
463             icon="ops.mesh.loopcut_slide",
464             widget=None,
465             keymap=(
466                 ("mesh.loopcut_slide", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
467             ),
468         )
469
470     @ToolDef.from_fn
471     def offset_edge_loops_slide():
472         return dict(
473             text="Offset Edge Loop Cut",
474             icon="ops.mesh.offset_edge_loops_slide",
475             widget=None,
476             keymap=(
477                 ("mesh.offset_edge_loops_slide", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
478             ),
479         )
480
481     @ToolDef.from_fn
482     def vertex_smooth():
483         return dict(
484             text="Smooth",
485             icon="ops.mesh.vertices_smooth",
486             widget=None,
487             keymap=(
488                 ("mesh.vertices_smooth", dict(),
489                  dict(type='ACTIONMOUSE', value='PRESS')),
490             ),
491         )
492
493     @ToolDef.from_fn
494     def vertex_randomize():
495         return dict(
496             text="Randomize",
497             icon="ops.transform.vertex_random",
498             widget=None,
499             keymap=(
500                 ("transform.vertex_random", dict(),
501                  dict(type='ACTIONMOUSE', value='PRESS')),
502             ),
503         )
504
505     @ToolDef.from_fn
506     def shrink_fatten():
507         def draw_settings(context, layout):
508             wm = context.window_manager
509             props = wm.operator_properties_last("transform.shrink_fatten")
510             layout.prop(props, "use_even_offset")
511
512         return dict(
513             text="Shrink/Fatten",
514             icon="ops.transform.shrink_fatten",
515             widget=None,
516             keymap=(
517                 ("transform.shrink_fatten", dict(release_confirm=True),
518                  dict(type='ACTIONMOUSE', value='PRESS')),
519             ),
520             draw_settings=draw_settings,
521         )
522
523     @ToolDef.from_fn
524     def push_pull():
525         return dict(
526             text="Push/Pull",
527             icon="ops.transform.push_pull",
528             widget=None,
529             keymap=(
530                 ("transform.push_pull", dict(release_confirm=True),
531                  dict(type='ACTIONMOUSE', value='PRESS')),
532             ),
533         )
534
535     @ToolDef.from_fn
536     def knife():
537         def draw_settings(context, layout):
538             wm = context.window_manager
539             props = wm.operator_properties_last("mesh.knife_tool")
540             layout.prop(props, "use_occlude_geometry")
541             layout.prop(props, "only_selected")
542
543         return dict(
544             text="Knife",
545             icon="ops.mesh.knife_tool",
546             widget=None,
547             keymap=(
548                 ("mesh.knife_tool",
549                  dict(wait_for_input=False),
550                  dict(type='ACTIONMOUSE', value='PRESS')),
551             ),
552             draw_settings=draw_settings,
553         )
554
555     @ToolDef.from_fn
556     def bisect():
557         return dict(
558             text="Bisect",
559             icon="ops.mesh.bisect",
560             widget=None,
561             keymap=(
562                 ("mesh.bisect",
563                  dict(),
564                  dict(type='EVT_TWEAK_A', value='ANY')),
565             ),
566         )
567
568
569 class _defs_edit_curve:
570
571     @ToolDef.from_fn
572     def draw():
573         def draw_settings(context, layout):
574             # Tool settings initialize operator options.
575             tool_settings = context.tool_settings
576             cps = tool_settings.curve_paint_settings
577
578             col = layout.row()
579
580             col.prop(cps, "curve_type")
581
582             if cps.curve_type == 'BEZIER':
583                 col.prop(cps, "error_threshold")
584                 col.prop(cps, "fit_method")
585                 col.prop(cps, "use_corners_detect")
586
587                 col = layout.row()
588                 col.active = cps.use_corners_detect
589                 col.prop(cps, "corner_angle")
590
591         return dict(
592             text="Draw",
593             icon=None,
594             widget=None,
595             keymap=(
596                 ("curve.draw", dict(wait_for_input=False), dict(type='ACTIONMOUSE', value='PRESS')),
597             ),
598             draw_settings=draw_settings,
599         )
600
601     @ToolDef.from_fn
602     def extrude_cursor():
603         return dict(
604             text="Extrude Cursor",
605             icon=None,
606             widget=None,
607             keymap=(
608                 ("curve.vertex_add", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
609             ),
610         )
611
612
613 class _defs_pose:
614
615     @ToolDef.from_fn
616     def breakdown():
617         return dict(
618             text="Breakdowner",
619             icon="ops.pose.breakdowner",
620             widget=None,
621             keymap=(
622                 ("pose.breakdown", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
623             ),
624         )
625
626     @ToolDef.from_fn
627     def push():
628         return dict(
629             text="Push",
630             icon="ops.pose.push",
631             widget=None,
632             keymap=(
633                 ("pose.push", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
634             ),
635         )
636
637     @ToolDef.from_fn
638     def relax():
639         return dict(
640             text="Relax",
641             icon="ops.pose.relax",
642             widget=None,
643             keymap=(
644                 ("pose.relax", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
645             ),
646         )
647
648
649 class _defs_sculpt:
650
651     @staticmethod
652     def generate_from_brushes(context):
653         return generate_from_brushes_ex(
654             context,
655             icon_prefix="brush.sculpt.",
656             brush_test_attr="use_paint_sculpt",
657             brush_category_attr="sculpt_tool",
658             brush_category_layout=(
659                 ('DRAW',),
660                 ('GRAB', 'THUMB'),
661                 ('SNAKE_HOOK',),
662                 ('BLOB', 'INFLATE'),
663                 ('SMOOTH', 'SCRAPE', 'FLATTEN'),
664                 ('CREASE', 'PINCH'),
665                 ('CLAY', 'CLAY_STRIPS'),
666                 ('LAYER',),
667                 ('NUDGE', 'ROTATE'),
668                 ('FILL',),
669                 ('SIMPLIFY',),
670                 ('MASK',),
671             )
672         )
673
674
675 class _defs_vertex_paint:
676
677     @staticmethod
678     def generate_from_brushes(context):
679         return generate_from_brushes_ex(
680             context,
681             icon_prefix="brush.paint_vertex.",
682             brush_test_attr="use_paint_vertex",
683             brush_category_attr="vertex_tool",
684             brush_category_layout=(
685                 ('MIX',),
686                 ('BLUR', 'AVERAGE'),
687                 ('SMEAR',),
688                 (
689                     'ADD', 'SUB', 'MUL', 'LIGHTEN', 'DARKEN',
690                     'COLORDODGE', 'DIFFERENCE', 'SCREEN', 'HARDLIGHT',
691                     'OVERLAY', 'SOFTLIGHT', 'EXCLUSION', 'LUMINOCITY',
692                     'SATURATION', 'HUE', 'ERASE_ALPHA', 'ADD_ALPHA',
693                 ),
694             )
695         )
696
697
698 class _defs_texture_paint:
699
700     @staticmethod
701     def generate_from_brushes(context):
702         return generate_from_brushes_ex(
703             context,
704             icon_prefix="brush.paint_texture.",
705             brush_test_attr="use_paint_image",
706             brush_category_attr="image_tool",
707             brush_category_layout=(
708                 ('DRAW',),
709                 ('SOFTEN',),
710                 ('SMEAR',),
711                 ('CLONE',),
712                 ('FILL',),
713                 ('MASK',),
714             )
715         )
716
717
718 class _defs_weight_paint:
719
720     @staticmethod
721     def generate_from_brushes(context):
722         return generate_from_brushes_ex(
723             context,
724             icon_prefix="brush.paint_weight.",
725             brush_test_attr="use_paint_weight",
726             brush_category_attr="vertex_tool",
727             brush_category_layout=(
728                 ('MIX',),
729                 ('BLUR', 'AVERAGE'),
730                 ('SMEAR',),
731                 (
732                     'ADD', 'SUB', 'MUL', 'LIGHTEN', 'DARKEN',
733                     'COLORDODGE', 'DIFFERENCE', 'SCREEN', 'HARDLIGHT',
734                     'OVERLAY', 'SOFTLIGHT', 'EXCLUSION', 'LUMINOCITY',
735                     'SATURATION', 'HUE',
736                 ),
737             )
738         )
739
740     @ToolDef.from_fn
741     def sample_weight():
742         return dict(
743             text="Sample Weight",
744             icon="ops.paint.weight_sample",
745             widget=None,
746             keymap=(
747                 ("paint.weight_sample", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
748             ),
749         )
750
751     @ToolDef.from_fn
752     def sample_weight_group():
753         return dict(
754             text="Sample Vertex Group",
755             icon="ops.paint.weight_sample_group",
756             widget=None,
757             keymap=(
758                 ("paint.weight_sample_group", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
759             ),
760         )
761
762     @ToolDef.from_fn
763     def gradient():
764         def draw_settings(context, layout):
765             wm = context.window_manager
766             props = wm.operator_properties_last("paint.weight_gradient")
767             layout.prop(props, "type")
768
769         return dict(
770             text="Gradient",
771             icon="ops.paint.weight_gradient",
772             widget=None,
773             keymap=(
774                 ("paint.weight_gradient", dict(), dict(type='EVT_TWEAK_A', value='ANY')),
775             ),
776             draw_settings=draw_settings,
777         )
778
779
780 class _defs_uv_select:
781
782     @ToolDef.from_fn
783     def border():
784         return dict(
785             text="Select Border",
786             icon="ops.generic.select_border",
787             widget=None,
788             keymap=(
789                 ("uv.select_border",
790                  dict(deselect=False),
791                  dict(type='EVT_TWEAK_A', value='ANY')),
792                 # ("uv.select_border",
793                 #  dict(deselect=True),
794                 #  dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)),
795             ),
796         )
797
798     @ToolDef.from_fn
799     def circle():
800         return dict(
801             text="Select Circle",
802             icon="ops.generic.select_circle",
803             widget=None,
804             keymap=(
805                 ("uv.select_circle",
806                  dict(),  # dict(deselect=False),
807                  dict(type='ACTIONMOUSE', value='PRESS')),
808                 # ("uv.select_circle",
809                 #  dict(deselect=True),
810                 #  dict(type='ACTIONMOUSE', value='PRESS', ctrl=True)),
811             ),
812         )
813
814     @ToolDef.from_fn
815     def lasso():
816         return dict(
817             text="Select Lasso",
818             icon="ops.generic.select_lasso",
819             widget=None,
820             keymap=(
821                 ("uv.select_lasso",
822                  dict(deselect=False),
823                  dict(type='EVT_TWEAK_A', value='ANY')),
824                 # ("uv.select_lasso",
825                 #  dict(deselect=True),
826                 #  dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)),
827             ),
828         )
829
830
831 class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel):
832     bl_space_type = 'IMAGE_EDITOR'
833     bl_region_type = 'TOOLS'
834     bl_category = "Tools"
835     bl_label = "Tools"  # not visible
836     bl_options = {'HIDE_HEADER'}
837
838     # Satisfy the 'ToolSelectPanelHelper' API.
839     keymap_prefix = "Image Editor Tool: "
840
841     @classmethod
842     def tools_from_context(cls, context, mode=None):
843         if mode is None:
844             mode = context.space_data.mode
845         for tools in (cls._tools[None], cls._tools.get(mode, ())):
846             for item in tools:
847                 if not (type(item) is ToolDef) and callable(item):
848                     yield from item(context)
849                 else:
850                     yield item
851
852     @classmethod
853     def tools_all(cls):
854         yield from cls._tools.items()
855
856     # for reuse
857     _tools_select = (
858         (
859             _defs_uv_select.border,
860             _defs_uv_select.circle,
861             _defs_uv_select.lasso,
862         ),
863     )
864
865     _tools = {
866         None: [
867             # for all modes
868         ],
869         'VIEW': [
870             *_tools_select,
871
872         ],
873         'MASK': [
874             None,
875         ],
876         'PAINT': [
877             _defs_texture_paint.generate_from_brushes,
878         ],
879     }
880
881
882 class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
883     bl_space_type = 'VIEW_3D'
884     bl_region_type = 'TOOLS'
885     bl_category = "Tools"
886     bl_label = "Tools"  # not visible
887     bl_options = {'HIDE_HEADER'}
888
889     # Satisfy the 'ToolSelectPanelHelper' API.
890     keymap_prefix = "3D View Tool: "
891
892     @classmethod
893     def tools_from_context(cls, context, mode=None):
894         if mode is None:
895             mode = context.mode
896         for tools in (cls._tools[None], cls._tools.get(mode, ())):
897             for item in tools:
898                 if not (type(item) is ToolDef) and callable(item):
899                     yield from item(context)
900                 else:
901                     yield item
902
903     @classmethod
904     def tools_all(cls):
905         yield from cls._tools.items()
906
907     # for reuse
908     _tools_transform = (
909         (
910             _defs_transform.translate,
911             _defs_transform.transform,
912         ),
913         _defs_transform.rotate,
914         (
915             _defs_transform.scale,
916             _defs_transform.scale_cage,
917         ),
918         None,
919         _defs_view3d_generic.ruler,
920     )
921
922     _tools_select = (
923         (
924             _defs_view3d_select.border,
925             _defs_view3d_select.circle,
926             _defs_view3d_select.lasso,
927         ),
928     )
929
930     _tools = {
931         None: [
932             _defs_view3d_generic.cursor,
933             # End group.
934         ],
935         'OBJECT': [
936             *_tools_select,
937             None,
938             *_tools_transform,
939         ],
940         'POSE': [
941             *_tools_select,
942             *_tools_transform,
943             None,
944             (
945                 _defs_pose.breakdown,
946                 _defs_pose.push,
947                 _defs_pose.relax,
948             )
949         ],
950         'EDIT_ARMATURE': [
951             *_tools_select,
952             None,
953             *_tools_transform,
954             _defs_edit_armature.roll,
955             (
956                 _defs_edit_armature.bone_size,
957                 _defs_edit_armature.bone_envelope,
958             ),
959             None,
960             (
961                 _defs_edit_armature.extrude,
962                 _defs_edit_armature.extrude_cursor,
963             )
964         ],
965         'EDIT_MESH': [
966             *_tools_select,
967             None,
968             *_tools_transform,
969             None,
970             _defs_edit_mesh.cube_add,
971             None,
972             (
973                 _defs_edit_mesh.extrude,
974                 _defs_edit_mesh.extrude_individual,
975                 _defs_edit_mesh.extrude_cursor,
976             ),
977             _defs_edit_mesh.inset,
978             _defs_edit_mesh.bevel,
979             (
980                 _defs_edit_mesh.loopcut_slide,
981                 _defs_edit_mesh.offset_edge_loops_slide,
982             ),
983             (
984                 _defs_edit_mesh.knife,
985                 _defs_edit_mesh.bisect,
986             ),
987             _defs_edit_mesh.poly_build,
988             (
989                 _defs_edit_mesh.spin,
990                 _defs_edit_mesh.spin_duplicate,
991             ),
992             (
993                 _defs_edit_mesh.vertex_smooth,
994                 _defs_edit_mesh.vertex_randomize,
995             ),
996             (
997                 _defs_edit_mesh.edge_slide,
998                 _defs_edit_mesh.vert_slide,
999             ),
1000             (
1001                 _defs_edit_mesh.shrink_fatten,
1002                 _defs_edit_mesh.push_pull,
1003             ),
1004             (
1005                 _defs_edit_mesh.rip_region,
1006                 _defs_edit_mesh.rip_edge,
1007             ),
1008         ],
1009         'EDIT_CURVE': [
1010             *_tools_select,
1011             None,
1012             *_tools_transform,
1013             None,
1014             _defs_edit_curve.draw,
1015             _defs_edit_curve.extrude_cursor,
1016         ],
1017         'SCULPT': [
1018             _defs_sculpt.generate_from_brushes,
1019         ],
1020         'PAINT_TEXTURE': [
1021             _defs_texture_paint.generate_from_brushes,
1022         ],
1023         'PAINT_VERTEX': [
1024             _defs_vertex_paint.generate_from_brushes,
1025         ],
1026         'PAINT_WEIGHT': [
1027             _defs_weight_paint.generate_from_brushes,
1028             None,
1029             _defs_weight_paint.sample_weight,
1030             _defs_weight_paint.sample_weight_group,
1031             None,
1032             # TODO, override brush events
1033             *_tools_select,
1034             None,
1035             _defs_weight_paint.gradient,
1036         ],
1037     }
1038
1039
1040 classes = (
1041     IMAGE_PT_tools_active,
1042     VIEW3D_PT_tools_active,
1043 )
1044
1045 if __name__ == "__main__":  # only for live edit.
1046     from bpy.utils import register_class
1047     for cls in classes:
1048         register_class(cls)