From 1ca3e1a91dbb4bbd99d8d8275e2a9c8dc0505d1c Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 2 Nov 2017 23:05:13 +1100 Subject: UI: support nested tools in toolbar --- release/scripts/startup/bl_ui/__init__.py | 1 + .../startup/bl_ui/space_toolsystem_common.py | 145 +++++++++++++++++++-- .../startup/bl_ui/space_toolsystem_toolbar.py | 51 +++++--- 3 files changed, 167 insertions(+), 30 deletions(-) (limited to 'release') diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index a12fd6352ea..037fe9618ec 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -68,6 +68,7 @@ _modules = [ # Generic Space Modules # # Depends on DNA_WORKSPACE_TOOL (C define). + "space_toolsystem_common", "space_toolsystem_toolbar", "space_clip", diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py index 1dade5b6735..69f4b0bbf2a 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_common.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -18,6 +18,9 @@ # import bpy +from bpy.types import ( + Menu, +) __all__ = ( "ToolSelectPanelHelper", @@ -48,6 +51,19 @@ class ToolSelectPanelHelper: an optional triple of: ``(operator_id, operator_properties, keymap_item_args)`` """ + @staticmethod + def _tool_is_group(tool): + return type(tool[0]) is not str + + @staticmethod + def _tools_flatten(tools): + for item in tools: + if ToolSelectPanelHelper._tool_is_group(item): + for sub_item in item: + yield sub_item + else: + yield item + @classmethod def _km_actionmouse_simple(cls, kc, text, actions): @@ -85,7 +101,7 @@ class ToolSelectPanelHelper: if kc is None: return - for t in cls.tools_all(): + for t in ToolSelectPanelHelper._tools_flatten(cls.tools_all()): text, mp_idname, actions = t if actions is not None: km, km_idname = cls._km_actionmouse_simple(kc, text, actions) @@ -101,6 +117,7 @@ class ToolSelectPanelHelper: workspace = context.workspace km_idname_active = workspace.tool_keymap or None mp_idname_active = workspace.tool_manipulator_group or None + index_active = workspace.tool_index layout = self.layout for tool_items in self.tools_from_context(context): @@ -110,22 +127,122 @@ class ToolSelectPanelHelper: if item is None: col = layout.column(align=True) continue - text, mp_idname, actions = item - if actions is not None: - km, km_idname = self._tool_keymap[text] + if self._tool_is_group(item): + index = 0 + is_active = False + for i, sub_item in enumerate(item): + text, mp_idname, actions = sub_item + km, km_idname = (None, None) if actions is None else self._tool_keymap[text] + is_active = ( + km_idname_active == km_idname and + mp_idname_active == mp_idname + ) + if is_active: + index = i + break + del i, sub_item + item = item[index] + use_menu = True else: - km = None - km_idname = None - - props = col.operator( - "wm.tool_set", - text=text, - depress=( - km_idname_active == km_idname and - mp_idname_active == mp_idname - ), + index = -1 + use_menu = False + + text, mp_idname, actions = item + km, km_idname = (None, None) if actions is None else self._tool_keymap[text] + is_active = ( + km_idname_active == km_idname and + mp_idname_active == mp_idname ) + if use_menu: + props = col.operator_menu_hold( + "wm.tool_set", + text=text, + depress=is_active, + menu="WM_MT_toolsystem_submenu", + ) + else: + props = col.operator( + "wm.tool_set", + text=text, + depress=is_active, + ) + props.keymap = km_idname or "" props.manipulator_group = mp_idname or "" + props.index = index + + def tools_from_context(cls, context): + return (cls._tools[None], cls._tools.get(context.mode, ())) + + +# The purpose of this menu is to be a generic popup to select between tools +# in cases when a single tool allows to select alternative tools. +class WM_MT_toolsystem_submenu(Menu): + bl_label = "" + + @staticmethod + def _tool_group_from_button(context): + # Lookup the tool definitions based on the space-type. + space_type = context.space_data.type + cls = next( + (cls for cls in ToolSelectPanelHelper.__subclasses__() + if cls.bl_space_type == space_type), + None + ) + if cls is not None: + props = context.button_operator + km_idname_button = props.keymap or None + mp_idname_button = props.manipulator_group or None + index_button = props.index + + for item_items in cls.tools_from_context(context): + for item_group in item_items: + if (item_group is not None) and ToolSelectPanelHelper._tool_is_group(item_group): + if index_button < len(item_group): + item = item_group[index_button] + text, mp_idname, actions = item + km, km_idname = (None, None) if actions is None else cls._tool_keymap[text] + is_active = ( + km_idname_button == km_idname and + mp_idname_button == mp_idname + ) + if is_active: + return cls, item_group, index_button + return None, None, -1 + + def draw(self, context): + layout = self.layout + cls, item_group, index_active = self._tool_group_from_button(context) + if item_group is None: + # Should never happen, just in case + layout.label(f"Unable to find toolbar group") + return + + index = 0 + for item in item_group: + if item is None: + layout.separator() + continue + text, mp_idname, actions = item + km, km_idname = (None, None) if actions is None else cls._tool_keymap[text] + + props = layout.operator( + "wm.tool_set", + text=text, + ) + props.keymap = km_idname or "" + props.manipulator_group = mp_idname or "" + props.index = index + index += 1 + + +classes = ( + WM_MT_toolsystem_submenu, +) + +if __name__ == "__main__": # only for live edit. + from bpy.utils import register_class + for cls in classes: + register_class(cls) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 71e41850a51..6667eb566c2 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -66,20 +66,25 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): 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)), - )), + + # 'Select' Group + ( + ("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)), + )), + ), + # End group. ], 'OBJECT': [ *_tools_transform, @@ -122,8 +127,22 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): ("mesh.polybuild_hover", dict(use_boundary=True), dict(type='MOUSEMOVE', value='ANY', any=True)), )), - ("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')),)), + # Knife Group + ( + ("Knife", None, ( + ("mesh.knife_tool", + dict(wait_for_input=False, use_occlude_geometry=True, only_selected=False), + dict(type='ACTIONMOUSE', value='PRESS')),)), + ("Knife (Selected)", None, ( + ("mesh.knife_tool", + dict(wait_for_input=False, use_occlude_geometry=False, only_selected=True), + dict(type='ACTIONMOUSE', value='PRESS')),)), + ("Bisect", None, ( + ("mesh.bisect", + dict(), + dict(type='EVT_TWEAK_A', value='ANY')),)), + ), + # End group. ("Extrude Cursor", None, (("mesh.dupli_extrude_cursor", dict(), dict(type='ACTIONMOUSE', value='PRESS')),)), ], -- cgit v1.2.3