1 # ##### BEGIN GPL LICENSE BLOCK #####
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.
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.
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.
17 # ##### END GPL LICENSE BLOCK #####
21 # For now group all tools together
22 # we may want to move these into per space-type files.
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.
27 from bpy.types import Panel
29 from .space_toolsystem_common import (
30 ToolSelectPanelHelper,
35 def generate_from_brushes_ex(
40 brush_category_layout,
44 for brush in context.blend_data.brushes:
45 if getattr(brush, brush_test_attr):
46 category = getattr(brush, brush_category_attr)
48 brush_categories.setdefault(category, []).append(
52 icon=icon_prefix + category.lower(),
58 def tools_from_brush_group(groups):
59 assert(type(groups) is tuple)
61 tool_defs = tuple(brush_categories.pop(groups[0], ()))
63 tool_defs = tuple(item for g in groups for item in brush_categories.pop(g, ()))
64 if len(tool_defs) > 1:
69 # Each item below is a single toolbar entry:
70 # Grouped for multiple or none if no brushes are found.
73 for category in brush_category_layout
74 for tool_def in tools_from_brush_group(category)
76 # Ensure we use all types.
78 print(brush_categories)
79 assert(len(brush_categories) == 0)
83 class _defs_view3d_generic:
88 icon="ops.generic.cursor",
90 ("view3d.cursor3d", dict(), dict(type='ACTIONMOUSE', value='CLICK')),
97 text="Ruler/Protractor",
98 icon="ops.view3d.ruler",
99 widget="VIEW3D_WGT_ruler",
101 ("view3d.ruler_add", dict(), dict(type='EVT_TWEAK_A', value='ANY')),
106 class _defs_transform:
112 icon="ops.transform.translate",
113 widget="TRANSFORM_WGT_manipulator",
115 ("transform.translate", dict(release_confirm=True), dict(type='EVT_TWEAK_A', value='ANY')),
123 icon="ops.transform.rotate",
124 widget="TRANSFORM_WGT_manipulator",
126 ("transform.rotate", dict(release_confirm=True), dict(type='EVT_TWEAK_A', value='ANY')),
134 icon="ops.transform.resize",
135 widget="TRANSFORM_WGT_manipulator",
137 ("transform.resize", dict(release_confirm=True), dict(type='EVT_TWEAK_A', value='ANY')),
145 icon="ops.transform.resize.cage",
146 widget="VIEW3D_WGT_xform_cage",
153 icon="ops.transform.transform",
154 widget="TRANSFORM_WGT_manipulator",
155 # No keymap default action, only for manipulators!
159 class _defs_view3d_select:
164 text="Select Border",
165 icon="ops.generic.select_border",
168 ("view3d.select_border",
169 dict(deselect=False),
170 dict(type='EVT_TWEAK_A', value='ANY')),
171 ("view3d.select_border",
173 dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)),
180 text="Select Circle",
181 icon="ops.generic.select_circle",
184 ("view3d.select_circle",
185 dict(deselect=False),
186 dict(type='ACTIONMOUSE', value='PRESS')),
187 ("view3d.select_circle",
189 dict(type='ACTIONMOUSE', value='PRESS', ctrl=True)),
197 icon="ops.generic.select_lasso",
200 ("view3d.select_lasso",
201 dict(deselect=False),
202 dict(type='EVT_TWEAK_A', value='ANY')),
203 ("view3d.select_lasso",
205 dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)),
208 # -----------------------------------------------------------------------------
209 # Object Modes (named based on context.mode)
212 class _defs_edit_armature:
218 icon="ops.armature.bone.roll",
221 ("transform.transform",
222 dict(release_confirm=True, mode='BONE_ROLL'),
223 dict(type='EVT_TWEAK_A', value='ANY'),),
230 text="Bone Envelope",
231 icon="ops.transform.bone_envelope",
234 ("transform.transform",
235 dict(release_confirm=True, mode='BONE_ENVELOPE'),
236 dict(type='ACTIONMOUSE', value='PRESS')),
244 icon="ops.transform.bone_size",
247 ("transform.transform",
248 dict(release_confirm=True, mode='BONE_SIZE'),
249 dict(type='ACTIONMOUSE', value='PRESS')),
257 icon="ops.armature.extrude_move",
260 ("armature.click_extrude", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
265 def extrude_cursor():
267 text="Extrude to Cursor",
268 icon="ops.armature.extrude_cursor",
271 ("armature.click_extrude", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
276 class _defs_edit_mesh:
283 icon="ops.mesh.primitive_cube_add_manipulator",
286 ("view3d.cursor3d", dict(), dict(type='ACTIONMOUSE', value='CLICK')),
287 ("mesh.primitive_cube_add_manipulator", dict(), dict(type='EVT_TWEAK_A', value='ANY')),
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")
305 dict(TRANSFORM_OT_translate=dict(release_confirm=True)),
306 dict(type='ACTIONMOUSE', value='PRESS')),
308 draw_settings=draw_settings,
315 icon="ops.mesh.rip_edge",
318 ("mesh.rip_edge_edge_move", dict(),
319 dict(type='ACTIONMOUSE', value='PRESS')),
327 icon="ops.mesh.polybuild_hover",
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)),
346 icon="ops.transform.edge_slide",
349 ("transform.edge_slide", dict(release_confirm=True),
350 dict(type='ACTIONMOUSE', value='PRESS')
359 icon="ops.transform.vert_slide",
362 ("transform.vert_slide", dict(release_confirm=True),
363 dict(type='ACTIONMOUSE', value='PRESS')),
371 icon="ops.mesh.spin",
374 ("mesh.spin", dict(),
375 dict(type='ACTIONMOUSE', value='PRESS')),
380 def spin_duplicate():
382 text="Spin (Duplicate)",
383 icon="ops.mesh.spin.duplicate",
386 ("mesh.spin", dict(dupli=True),
387 dict(type='ACTIONMOUSE', value='PRESS')),
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")
403 icon="ops.mesh.inset",
406 ("mesh.inset", dict(release_confirm=True),
407 dict(type='ACTIONMOUSE', value='PRESS')),
409 draw_settings=draw_settings,
416 icon="ops.mesh.bevel",
419 ("mesh.bevel", dict(),
420 dict(type='ACTIONMOUSE', value='PRESS')),
427 text="Extrude Region",
428 icon="ops.mesh.extrude_region_move",
429 widget="MESH_WGT_extrude",
431 ("mesh.extrude_context_move", dict(TRANSFORM_OT_translate=dict(release_confirm=True)),
432 dict(type='EVT_TWEAK_A', value='ANY')),
437 def extrude_individual():
439 text="Extrude Individual",
440 icon="ops.mesh.extrude_faces_move",
443 ("mesh.extrude_faces_move", dict(TRANSFORM_OT_shrink_fatten=dict(release_confirm=True)),
444 dict(type='EVT_TWEAK_A', value='ANY')),
449 def extrude_cursor():
451 text="Extrude to Cursor",
452 icon="ops.mesh.dupli_extrude_cursor",
455 ("mesh.dupli_extrude_cursor", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
463 icon="ops.mesh.loopcut_slide",
466 ("mesh.loopcut_slide", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
471 def offset_edge_loops_slide():
473 text="Offset Edge Loop Cut",
474 icon="ops.mesh.offset_edge_loops_slide",
477 ("mesh.offset_edge_loops_slide", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
485 icon="ops.mesh.vertices_smooth",
488 ("mesh.vertices_smooth", dict(),
489 dict(type='ACTIONMOUSE', value='PRESS')),
494 def vertex_randomize():
497 icon="ops.transform.vertex_random",
500 ("transform.vertex_random", dict(),
501 dict(type='ACTIONMOUSE', value='PRESS')),
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")
513 text="Shrink/Fatten",
514 icon="ops.transform.shrink_fatten",
517 ("transform.shrink_fatten", dict(release_confirm=True),
518 dict(type='ACTIONMOUSE', value='PRESS')),
520 draw_settings=draw_settings,
527 icon="ops.transform.push_pull",
530 ("transform.push_pull", dict(release_confirm=True),
531 dict(type='ACTIONMOUSE', value='PRESS')),
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")
545 icon="ops.mesh.knife_tool",
549 dict(wait_for_input=False),
550 dict(type='ACTIONMOUSE', value='PRESS')),
552 draw_settings=draw_settings,
559 icon="ops.mesh.bisect",
564 dict(type='EVT_TWEAK_A', value='ANY')),
569 class _defs_edit_curve:
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
580 col.prop(cps, "curve_type")
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")
588 col.active = cps.use_corners_detect
589 col.prop(cps, "corner_angle")
596 ("curve.draw", dict(wait_for_input=False), dict(type='ACTIONMOUSE', value='PRESS')),
598 draw_settings=draw_settings,
602 def extrude_cursor():
604 text="Extrude Cursor",
608 ("curve.vertex_add", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
619 icon="ops.pose.breakdowner",
622 ("pose.breakdown", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
630 icon="ops.pose.push",
633 ("pose.push", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
641 icon="ops.pose.relax",
644 ("pose.relax", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
652 def generate_from_brushes(context):
653 return generate_from_brushes_ex(
655 icon_prefix="brush.sculpt.",
656 brush_test_attr="use_paint_sculpt",
657 brush_category_attr="sculpt_tool",
658 brush_category_layout=(
663 ('SMOOTH', 'SCRAPE', 'FLATTEN'),
665 ('CLAY', 'CLAY_STRIPS'),
675 class _defs_vertex_paint:
678 def generate_from_brushes(context):
679 return generate_from_brushes_ex(
681 icon_prefix="brush.paint_vertex.",
682 brush_test_attr="use_paint_vertex",
683 brush_category_attr="vertex_tool",
684 brush_category_layout=(
689 'ADD', 'SUB', 'MUL', 'LIGHTEN', 'DARKEN',
690 'COLORDODGE', 'DIFFERENCE', 'SCREEN', 'HARDLIGHT',
691 'OVERLAY', 'SOFTLIGHT', 'EXCLUSION', 'LUMINOCITY',
692 'SATURATION', 'HUE', 'ERASE_ALPHA', 'ADD_ALPHA',
698 class _defs_texture_paint:
701 def generate_from_brushes(context):
702 return generate_from_brushes_ex(
704 icon_prefix="brush.paint_texture.",
705 brush_test_attr="use_paint_image",
706 brush_category_attr="image_tool",
707 brush_category_layout=(
718 class _defs_weight_paint:
721 def generate_from_brushes(context):
722 return generate_from_brushes_ex(
724 icon_prefix="brush.paint_weight.",
725 brush_test_attr="use_paint_weight",
726 brush_category_attr="vertex_tool",
727 brush_category_layout=(
732 'ADD', 'SUB', 'MUL', 'LIGHTEN', 'DARKEN',
733 'COLORDODGE', 'DIFFERENCE', 'SCREEN', 'HARDLIGHT',
734 'OVERLAY', 'SOFTLIGHT', 'EXCLUSION', 'LUMINOCITY',
743 text="Sample Weight",
744 icon="ops.paint.weight_sample",
747 ("paint.weight_sample", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
752 def sample_weight_group():
754 text="Sample Vertex Group",
755 icon="ops.paint.weight_sample_group",
758 ("paint.weight_sample_group", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
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")
771 icon="ops.paint.weight_gradient",
774 ("paint.weight_gradient", dict(), dict(type='EVT_TWEAK_A', value='ANY')),
776 draw_settings=draw_settings,
780 class _defs_uv_select:
785 text="Select Border",
786 icon="ops.generic.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)),
801 text="Select Circle",
802 icon="ops.generic.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)),
818 icon="ops.generic.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)),
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'}
838 # Satisfy the 'ToolSelectPanelHelper' API.
839 keymap_prefix = "Image Editor Tool: "
842 def tools_from_context(cls, context, mode=None):
844 mode = context.space_data.mode
845 for tools in (cls._tools[None], cls._tools.get(mode, ())):
847 if not (type(item) is ToolDef) and callable(item):
848 yield from item(context)
854 yield from cls._tools.items()
859 _defs_uv_select.border,
860 _defs_uv_select.circle,
861 _defs_uv_select.lasso,
877 _defs_texture_paint.generate_from_brushes,
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'}
889 # Satisfy the 'ToolSelectPanelHelper' API.
890 keymap_prefix = "3D View Tool: "
893 def tools_from_context(cls, context, mode=None):
896 for tools in (cls._tools[None], cls._tools.get(mode, ())):
898 if not (type(item) is ToolDef) and callable(item):
899 yield from item(context)
905 yield from cls._tools.items()
910 _defs_transform.translate,
911 _defs_transform.transform,
913 _defs_transform.rotate,
915 _defs_transform.scale,
916 _defs_transform.scale_cage,
919 _defs_view3d_generic.ruler,
924 _defs_view3d_select.border,
925 _defs_view3d_select.circle,
926 _defs_view3d_select.lasso,
932 _defs_view3d_generic.cursor,
945 _defs_pose.breakdown,
954 _defs_edit_armature.roll,
956 _defs_edit_armature.bone_size,
957 _defs_edit_armature.bone_envelope,
961 _defs_edit_armature.extrude,
962 _defs_edit_armature.extrude_cursor,
970 _defs_edit_mesh.cube_add,
973 _defs_edit_mesh.extrude,
974 _defs_edit_mesh.extrude_individual,
975 _defs_edit_mesh.extrude_cursor,
977 _defs_edit_mesh.inset,
978 _defs_edit_mesh.bevel,
980 _defs_edit_mesh.loopcut_slide,
981 _defs_edit_mesh.offset_edge_loops_slide,
984 _defs_edit_mesh.knife,
985 _defs_edit_mesh.bisect,
987 _defs_edit_mesh.poly_build,
989 _defs_edit_mesh.spin,
990 _defs_edit_mesh.spin_duplicate,
993 _defs_edit_mesh.vertex_smooth,
994 _defs_edit_mesh.vertex_randomize,
997 _defs_edit_mesh.edge_slide,
998 _defs_edit_mesh.vert_slide,
1001 _defs_edit_mesh.shrink_fatten,
1002 _defs_edit_mesh.push_pull,
1005 _defs_edit_mesh.rip_region,
1006 _defs_edit_mesh.rip_edge,
1014 _defs_edit_curve.draw,
1015 _defs_edit_curve.extrude_cursor,
1018 _defs_sculpt.generate_from_brushes,
1021 _defs_texture_paint.generate_from_brushes,
1024 _defs_vertex_paint.generate_from_brushes,
1027 _defs_weight_paint.generate_from_brushes,
1029 _defs_weight_paint.sample_weight,
1030 _defs_weight_paint.sample_weight_group,
1032 # TODO, override brush events
1035 _defs_weight_paint.gradient,
1041 IMAGE_PT_tools_active,
1042 VIEW3D_PT_tools_active,
1045 if __name__ == "__main__": # only for live edit.
1046 from bpy.utils import register_class