From e1e7b6db2e01d8ea2219d410200a8856d4597222 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 21 Oct 2017 16:19:48 +1100 Subject: WM: Initial Tool System The tool-system it's self is primitive and may be changed. Adding to 2.8 to develop operators and manipulators as tools. Currently this is exposed in the toolbar, collapsed by default. Work-flow remains unchanged if you don't change the active tool. Placing the 3D cursor is now a Click instead of a Press event, this allows tweak events to be mapped to tools such as border select, keeping click for 3D cursor placement when selection tools are set. --- release/scripts/startup/bl_ui/__init__.py | 6 + .../startup/bl_ui/space_toolsystem_common.py | 127 +++++++++++++++++++ .../startup/bl_ui/space_toolsystem_toolbar.py | 134 +++++++++++++++++++++ 3 files changed, 267 insertions(+) create mode 100644 release/scripts/startup/bl_ui/space_toolsystem_common.py create mode 100644 release/scripts/startup/bl_ui/space_toolsystem_toolbar.py (limited to 'release') diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index d7135ca202c..a12fd6352ea 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -64,6 +64,12 @@ _modules = [ "properties_scene", "properties_texture", "properties_world", + + # Generic Space Modules + # + # Depends on DNA_WORKSPACE_TOOL (C define). + "space_toolsystem_toolbar", + "space_clip", "space_console", "space_dopesheet", diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py new file mode 100644 index 00000000000..38971f73984 --- /dev/null +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -0,0 +1,127 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# +import bpy + +__all__ = ( + "ToolSelectPanelHelper", +) + + +class ToolSelectPanelHelper: + """ + Generic Class, can be used for any toolbar. + + - keymap_prefix: + The text prefix for each key-map for this spaces tools. + - tools_all(): + Returns all tools defined. + - tools_from_context(context): + Returns tools available in this context. + + Each tool is a triplet: + ``(tool_name, manipulator_group_idname, keymap_actions)`` + For a separator in the toolbar, use ``None``. + + Where: + ``tool_name`` + is the name to display in the interface. + ``manipulator_group_idname`` + is an optional manipulator group to activate when the tool is set. + ``keymap_actions`` + an optional triple of: ``(operator_id, operator_properties, keymap_item_args)`` + """ + + @classmethod + def _km_actionmouse_simple(cls, kc, text, actions): + + # standalone + def props_assign_recursive(rna_props, py_props): + for prop_id, value in py_props.items(): + if isinstance(value, dict): + props_assign_recursive(getattr(rna_props, prop_id), value) + else: + setattr(rna_props, prop_id, value) + + km_idname = cls.keymap_prefix + text + km = kc.keymaps.new(km_idname, space_type=cls.bl_space_type, region_type='WINDOW') + for op_idname, op_props_dict, kmi_kwargs in actions: + kmi = km.keymap_items.new(op_idname, **kmi_kwargs) + kmi_props = kmi.properties + if op_props_dict: + props_assign_recursive(kmi.properties, op_props_dict) + return km, km_idname + + @classmethod + def register(cls): + wm = bpy.context.window_manager + + # XXX, should we be manipulating the user-keyconfig on load? + # Perhaps this should only add when keymap items don't already exist. + # + # This needs some careful consideration. + kc = wm.keyconfigs.user + + # {tool_name: (keymap, keymap_idname, manipulator_group_idname), ...} + cls._tool_keymap = {} + + for t in cls.tools_all(): + text, mp_idname, actions = t + if actions is not None: + km, km_idname = cls._km_actionmouse_simple(kc, text, actions) + cls._tool_keymap[text] = km, km_idname + + def draw(self, context): + # XXX, this UI isn't very nice. + # We might need to create new button types for this. + # Since we probably want: + # - tool-tips that include multiple key shortcuts. + # - ability to click and hold to expose sub-tools. + + workspace = context.workspace + km_idname_active = workspace.tool_keymap or None + mp_idname_active = workspace.tool_manipulator_group or None + layout = self.layout + + for tool_items in self.tools_from_context(context): + if tool_items: + col = layout.box().column() + for item in tool_items: + if item is None: + col = layout.box().column() + continue + text, mp_idname, actions = item + + if actions is not None: + km, km_idname = self._tool_keymap[text] + else: + km = None + km_idname = None + + props = col.operator( + "wm.tool_set", + text=text, + emboss=( + km_idname_active == km_idname and + mp_idname_active == mp_idname + ), + ) + + props.keymap = km_idname or "" + props.manipulator_group = mp_idname or "" diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py new file mode 100644 index 00000000000..d8843d4fbce --- /dev/null +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -0,0 +1,134 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# + +# For now group all tools together +# we may want to move these into per space-type files. +# +# For now keep this in a single file since it's an area that may change, +# so avoid making changes all over the place. + +from bpy.types import Panel + +from .space_toolsystem_common import ( + ToolSelectPanelHelper, +) + + +class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'TOOLS' + bl_category = "Tools" + bl_label = "Active Tool (Test)" + bl_options = {'DEFAULT_CLOSED'} + + # Satisfy the 'ToolSelectPanelHelper' API. + keymap_prefix = "3D View Tool: " + + @classmethod + def tools_from_context(cls, context): + return (cls._tools[None], cls._tools.get(context.mode, ())) + + @classmethod + def tools_all(cls): + return [t for t_list in cls._tools.values() for t in t_list if t is not None] + + # Internal Data + + # for reuse + _tools_transform = ( + ("Translate", None, + (("transform.translate", dict(release_confirm=True), dict(type='EVT_TWEAK_A', value='ANY')),)), + ("Rotate", None, + (("transform.rotate", dict(release_confirm=True), dict(type='EVT_TWEAK_A', value='ANY')),)), + ("Scale", None, + (("transform.resize", dict(release_confirm=True), dict(type='EVT_TWEAK_A', value='ANY')),)), + ) + + _tools = { + None: [ + ("Cursor", None, + (("view3d.cursor3d", dict(), dict(type='ACTIONMOUSE', value='CLICK')),)), + ("Select Border", None, ( + ("view3d.select_border", dict(deselect=False), dict(type='EVT_TWEAK_A', value='ANY')), + ("view3d.select_border", dict(deselect=True), dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)), + )), + ("Select Circle", None, ( + ("view3d.select_circle", dict(deselect=False), dict(type='ACTIONMOUSE', value='PRESS')), + ("view3d.select_circle", dict(deselect=True), dict(type='ACTIONMOUSE', value='PRESS', ctrl=True)), + )), + ("Select Lasso", None, ( + ("view3d.select_lasso", + dict(deselect=False), dict(type='EVT_TWEAK_A', value='ANY')), + ("view3d.select_lasso", + dict(deselect=True), dict(type='EVT_TWEAK_A', value='ANY', ctrl=True)), + )), + ], + 'OBJECT': [ + *_tools_transform, + ], + 'POSE': [ + *_tools_transform, + ], + 'EDIT_ARMATURE': [ + *_tools_transform, + ("Roll", None, ( + ("transform.transform", + dict(release_confirm=True, mode='BONE_ROLL'), + dict(type='EVT_TWEAK_A', value='ANY')), + )), + None, + ("Extrude Cursor", None, + (("armature.click_extrude", dict(), dict(type='ACTIONMOUSE', value='PRESS')),)), + ], + 'EDIT_MESH': [ + *_tools_transform, + None, + ("Rip Region", None, ( + ("mesh.rip_move", dict(TRANSFORM_OT_translate=dict(release_confirm=True)), + dict(type='ACTIONMOUSE', value='PRESS')), + )), + ("Rip Edge", None, ( + ("mesh.rip_edge_move", dict(TRANSFORM_OT_translate=dict(release_confirm=True)), + dict(type='ACTIONMOUSE', value='PRESS')), + )), + ("Knife", None, (("mesh.knife_tool", dict(wait_for_input=False), dict(type='ACTIONMOUSE', value='PRESS')),)), + ("Bisect", None, (("mesh.bisect", dict(), dict(type='EVT_TWEAK_A', value='ANY')),)), + ("Extrude Cursor", None, + (("mesh.dupli_extrude_cursor", dict(), dict(type='ACTIONMOUSE', value='PRESS')),)), + ], + 'EDIT_CURVE': [ + *_tools_transform, + None, + ("Draw", None, + (("curve.draw", dict(wait_for_input=False), dict(type='ACTIONMOUSE', value='PRESS')),)), + ("Extrude Cursor", None, + (("curve.vertex_add", dict(), dict(type='ACTIONMOUSE', value='PRESS')),)), + ], + } + + +classes = ( + VIEW3D_PT_tools_active, +) + +if __name__ == "__main__": # only for live edit. + from bpy.utils import register_class + for cls in classes: + register_class(cls) -- cgit v1.2.3