Tool System: Use tapping Alt as a leader key to switch tools

Now tapping Alt prompts for a second input to switch tools.

Initial implementation of T69992
This commit is contained in:
Campbell Barton 2019-10-28 22:43:15 +11:00
parent df9f1d91da
commit de152d0724
3 changed files with 127 additions and 9 deletions

View File

@ -25,7 +25,7 @@ __all__ = (
"generate",
)
def generate(context, space_type):
def generate(context, space_type, use_fallback_keys=True, use_reset=True):
"""
Keymap for popup toolbar, currently generated each time.
"""
@ -66,7 +66,7 @@ def generate(context, space_type):
# to 'drop' currently active tools (it's basically a 'none' tool).
# so this allows us to quickly go back to a state that allows
# a shortcut based workflow (before the tool system was added).
use_tap_reset = True
use_tap_reset = use_reset
# TODO: support other tools for modes which don't use this tool.
tap_reset_tool = "builtin.cursor"
# Check the tool is available in the current context.
@ -76,11 +76,11 @@ def generate(context, space_type):
from bl_operators.wm import use_toolbar_release_hack
# Pie-menu style release to activate.
use_release_confirm = True
use_release_confirm = use_reset
# Generate items when no keys are mapped.
use_auto_keymap_alpha = False # Map manually in the default key-map.
use_auto_keymap_num = True
use_auto_keymap_num = use_fallback_keys
# Temporary, only create so we can pass 'properties' to find_item_from_operator.
use_hack_properties = True

View File

@ -412,6 +412,9 @@ def km_window(params):
("wm.batch_rename", {"type": 'F2', "value": 'PRESS', "ctrl": True}, None),
("wm.search_menu", {"type": 'F3', "value": 'PRESS'}, None),
op_menu("TOPBAR_MT_file_context_menu", {"type": 'F4', "value": 'PRESS'}),
# Alt as "Leader-Key".
("wm.toolbar_prompt", {"type": 'LEFT_ALT', "value": 'CLICK'}, None),
("wm.toolbar_prompt", {"type": 'RIGHT_ALT', "value": 'CLICK'}, None),
])
if params.spacebar_action == 'TOOL':

View File

@ -1744,27 +1744,141 @@ class WM_OT_toolbar(Operator):
WM_OT_toolbar._key_held = event.type
return self.execute(context)
def execute(self, context):
@staticmethod
def keymap_from_toolbar(context, space_type, use_fallback_keys=True, use_reset=True):
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
from bl_keymap_utils import keymap_from_toolbar
space_type = context.space_data.type
cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
if cls is None:
return {'CANCELLED'}
return None, None
wm = context.window_manager
keymap = keymap_from_toolbar.generate(context, space_type)
return cls, keymap_from_toolbar.generate(
context,
space_type,
use_fallback_keys=use_fallback_keys,
use_reset=use_reset,
)
def execute(self, context):
space_type = context.space_data.type
cls, keymap = self.keymap_from_toolbar(context, space_type)
if keymap is None:
return {'CANCELLED'}
def draw_menu(popover, context):
layout = popover.layout
layout.operator_context = 'INVOKE_REGION_WIN'
cls.draw_cls(layout, context, detect_layout=False, scale_y=1.0)
wm = context.window_manager
wm.popover(draw_menu, ui_units_x=8, keymap=keymap)
return {'FINISHED'}
class WM_OT_toolbar_prompt(Operator):
"""Leader key like functionality for accessing tools"""
bl_idname = "wm.toolbar_prompt"
bl_label = "Toolbar Prompt"
def modal(self, context, event):
event_type = event.type
event_value = event.value
keymap = self._keymap
if event_type in {'LEFTMOUSE', 'RIGHTMOUSE', 'MIDDLEMOUSE', 'ESC'}:
context.workspace.status_text_set(None)
return {'CANCELLED', 'PASS_THROUGH'}
item = keymap.keymap_items.match_event(event)
if item is not None:
idname = item.idname
properties = item.properties
if idname == "wm.tool_set_by_id":
tool_idname = properties["name"]
bpy.ops.wm.tool_set_by_id(name=tool_idname)
context.workspace.status_text_set(None)
return {'FINISHED'}
return {'RUNNING_MODAL'}
def invoke(self, context, event):
space_type = context.space_data.type
cls, keymap = WM_OT_toolbar.keymap_from_toolbar(
context,
space_type,
use_fallback_keys=False,
use_reset=False,
)
if keymap is None:
return {'CANCELLED'}
self._init_event_type = event.type
# Strip Left/Right, since "Left Alt" isn't especially useful.
init_event_type_as_text = self._init_event_type.title().split("_")
if init_event_type_as_text[0] in {"Left", "Right"}:
del init_event_type_as_text[0]
init_event_type_as_text = " ".join(init_event_type_as_text)
def status_text_fn(self, context):
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
# The keymap doesn't have the same order the tools are declared in,
# while we could support this, it's simpler to apply order here.
tool_map_id_to_order = {}
# Map the
tool_map_id_to_label = {}
for item in ToolSelectPanelHelper._tools_flatten(cls.tools_from_context(context)):
if item is not None:
tool_map_id_to_label[item.idname] = item.label
tool_map_id_to_order[item.idname] = len(tool_map_id_to_order)
layout = self.layout
if True:
box = layout.row(align=True).box()
box.scale_x = 0.8
box.label(text=init_event_type_as_text)
status_items = []
for item in keymap.keymap_items:
name = item.name
key_str = item.to_string()
# These are duplicated from regular numbers.
if key_str.startswith("Numpad "):
continue
properties = item.properties
idname = item.idname
if idname == "wm.tool_set_by_id":
tool_idname = properties["name"]
name = tool_map_id_to_label[tool_idname]
name = name.replace("Annotate ", "")
else:
continue
status_items.append((tool_idname, name, item))
status_items.sort(
key=lambda a: tool_map_id_to_order[a[0]]
)
flow = layout.grid_flow(columns=len(status_items), align=True, row_major=True)
for _, name, item in status_items:
row = flow.row(align=True)
row.template_event_from_keymap_item(item, text=name)
self._keymap = keymap
context.workspace.status_text_set(status_text_fn)
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
class BatchRenameAction(bpy.types.PropertyGroup):
# category: StringProperty()
type: EnumProperty(
@ -2430,6 +2544,7 @@ classes = (
WM_OT_tool_set_by_id,
WM_OT_tool_set_by_index,
WM_OT_toolbar,
WM_OT_toolbar_prompt,
BatchRenameAction,
WM_OT_batch_rename,
WM_MT_splash,