Keymap: add experimental "Toolbar Toggle Pie" preference

When pressing N-key a pie menu is shown which can toggle regions,
typically the toolbar, sidebar and the header.

This supports toggling regions without having to add a separate shortcut
for each one.

The pie menu locations selected based on the region alignment.

See #107785.
This commit is contained in:
Campbell Barton 2023-07-26 15:23:31 +10:00
parent 566118f7f1
commit ab7161e41a
3 changed files with 166 additions and 9 deletions

@ -88,6 +88,15 @@ class Prefs(bpy.types.KeyConfigPreferences):
update=update_fn,
)
# Experimental: only show with developer extras, see: #107785.
use_toolbar_pie: BoolProperty(
name="Toolbar Toggle Pie",
description=(
"N-key opens a pie menu to toggle regions"
),
default=False,
update=update_fn,
)
# Experimental: only show with developer extras, see: #96544.
use_tweak_select_passthrough: BoolProperty(
name="Tweak Select: Mouse Select & Move",
@ -310,6 +319,9 @@ class Prefs(bpy.types.KeyConfigPreferences):
if show_developer_ui and (not is_select_left):
row = sub.row()
row.prop(self, "use_tweak_tool_lmb_interaction")
if show_developer_ui:
row = sub.row()
row.prop(self, "use_toolbar_pie")
# 3DView settings.
col = layout.column()
@ -357,6 +369,7 @@ def load():
use_mouse_emulate_3_button=use_mouse_emulate_3_button,
spacebar_action=kc_prefs.spacebar_action,
use_key_activate_tools=(kc_prefs.tool_key_mode == 'TOOL'),
use_toolbar_pie=(show_developer_ui and kc_prefs.use_toolbar_pie),
v3d_tilde_action=kc_prefs.v3d_tilde_action,
use_v3d_mmb_pan=(kc_prefs.v3d_mmb_action == 'PAN'),
v3d_alt_mmb_drag_action=kc_prefs.v3d_alt_mmb_drag_action,

@ -66,6 +66,8 @@ class Params:
"use_alt_click_leader",
# Transform keys G/S/R activate tools instead of immediately transforming.
"use_key_activate_tools",
# Side-bar toggle opens a pie menu instead of immediately toggling the side-bar.
"use_toolbar_pie",
# Optionally use a modifier to access tools.
"tool_modifier",
# Experimental option.
@ -113,6 +115,7 @@ class Params:
# User preferences.
spacebar_action='TOOL',
use_key_activate_tools=False,
use_toolbar_pie=False,
use_select_all_toggle=False,
use_gizmo_drag=True,
use_fallback_tool=False,
@ -193,6 +196,7 @@ class Params:
# User preferences:
self.spacebar_action = spacebar_action
self.use_key_activate_tools = use_key_activate_tools
self.use_toolbar_pie = use_toolbar_pie
self.use_gizmo_drag = use_gizmo_drag
self.use_select_all_toggle = use_select_all_toggle
@ -311,8 +315,21 @@ def _template_items_context_panel(menu, key_args_primary):
]
def _template_space_region_type_toggle(*, toolbar_key=None, sidebar_key=None, channels_key=None):
def _template_space_region_type_toggle(
params,
*,
toolbar_key=None,
sidebar_key=None,
channels_key=None,
):
items = []
if params.use_toolbar_pie:
pie_key = sidebar_key or sidebar_key or channels_key
if pie_key is not None:
items.append(op_menu_pie("WM_MT_toolbar_toggle_pie", pie_key))
return items
if toolbar_key is not None:
items.append(
("wm.context_toggle", toolbar_key,
@ -1412,7 +1429,7 @@ def km_uv_editor(params):
# Editor (3D View)
# 3D View: all regions.
def km_view3d_generic(_params):
def km_view3d_generic(params):
items = []
keymap = (
"3D View Generic",
@ -1422,6 +1439,7 @@ def km_view3d_generic(_params):
items.extend([
*_template_space_region_type_toggle(
params,
toolbar_key={"type": 'T', "value": 'PRESS'},
sidebar_key={"type": 'N', "value": 'PRESS'},
)
@ -1759,7 +1777,7 @@ def km_view3d(params):
# ------------------------------------------------------------------------------
# Editor (Graph Editor)
def km_graph_editor_generic(_params):
def km_graph_editor_generic(params):
items = []
keymap = (
"Graph Editor Generic",
@ -1769,6 +1787,7 @@ def km_graph_editor_generic(_params):
items.extend([
*_template_space_region_type_toggle(
params,
sidebar_key={"type": 'N', "value": 'PRESS'},
),
("graph.extrapolation_type", {"type": 'E', "value": 'PRESS', "shift": True}, None),
@ -1931,6 +1950,7 @@ def km_image_generic(params):
items.extend([
*_template_space_region_type_toggle(
params,
toolbar_key={"type": 'T', "value": 'PRESS'},
sidebar_key={"type": 'N', "value": 'PRESS'},
),
@ -2051,7 +2071,7 @@ def km_image(params):
# ------------------------------------------------------------------------------
# Editor (Node)
def km_node_generic(_params):
def km_node_generic(params):
items = []
keymap = (
"Node Generic",
@ -2061,6 +2081,7 @@ def km_node_generic(_params):
items.extend([
*_template_space_region_type_toggle(
params,
toolbar_key={"type": 'T', "value": 'PRESS'},
sidebar_key={"type": 'N', "value": 'PRESS'},
),
@ -2252,6 +2273,7 @@ def km_file_browser(params):
items.extend([
*_template_space_region_type_toggle(
params,
toolbar_key={"type": 'T', "value": 'PRESS'},
),
("wm.context_toggle", {"type": 'N', "value": 'PRESS'},
@ -2399,7 +2421,7 @@ def km_file_browser_buttons(_params):
# ------------------------------------------------------------------------------
# Editor (Dope Sheet)
def km_dopesheet_generic(_params):
def km_dopesheet_generic(params):
items = []
keymap = (
"Dopesheet Generic",
@ -2409,6 +2431,7 @@ def km_dopesheet_generic(_params):
items.extend([
*_template_space_region_type_toggle(
params,
sidebar_key={"type": 'N', "value": 'PRESS'},
),
("wm.context_set_enum", {"type": 'TAB', "value": 'PRESS', "ctrl": True},
@ -2536,7 +2559,7 @@ def km_dopesheet(params):
# ------------------------------------------------------------------------------
# Editor (NLA)
def km_nla_generic(_params):
def km_nla_generic(params):
items = []
keymap = (
"NLA Generic",
@ -2546,6 +2569,7 @@ def km_nla_generic(_params):
items.extend([
*_template_space_region_type_toggle(
params,
sidebar_key={"type": 'N', "value": 'PRESS'},
),
("nla.tweakmode_enter", {"type": 'TAB', "value": 'PRESS'},
@ -2667,7 +2691,7 @@ def km_nla_editor(params):
# ------------------------------------------------------------------------------
# Editor (Text)
def km_text_generic(_params):
def km_text_generic(params):
items = []
keymap = (
"Text Generic",
@ -2677,6 +2701,7 @@ def km_text_generic(_params):
items.extend([
*_template_space_region_type_toggle(
params,
sidebar_key={"type": 'T', "value": 'PRESS', "ctrl": True},
),
("text.start_find", {"type": 'F', "value": 'PRESS', "ctrl": True}, None),
@ -2841,6 +2866,7 @@ def km_sequencercommon(params):
items.extend([
*_template_space_region_type_toggle(
params,
toolbar_key={"type": 'T', "value": 'PRESS'},
sidebar_key={"type": 'N', "value": 'PRESS'},
),
@ -3147,7 +3173,7 @@ def km_console(_params):
# ------------------------------------------------------------------------------
# Editor (Clip)
def km_clip(_params):
def km_clip(params):
items = []
keymap = (
"Clip",
@ -3157,6 +3183,7 @@ def km_clip(_params):
items.extend([
*_template_space_region_type_toggle(
params,
toolbar_key={"type": 'T', "value": 'PRESS'},
sidebar_key={"type": 'N', "value": 'PRESS'},
),
@ -3380,7 +3407,7 @@ def km_clip_dopesheet_editor(_params):
# ------------------------------------------------------------------------------
# Editor (Spreadsheet)
def km_spreadsheet_generic(_params):
def km_spreadsheet_generic(params):
items = []
keymap = (
"Spreadsheet Generic",
@ -3390,6 +3417,7 @@ def km_spreadsheet_generic(_params):
items.extend([
*_template_space_region_type_toggle(
params,
sidebar_key={"type": 'N', "value": 'PRESS'},
channels_key={"type": 'T', "value": 'PRESS'},
),

@ -3301,6 +3301,121 @@ class WM_MT_splash_about(Menu):
col.operator("wm.url_open_preset", text="Development Fund", icon='FUND').type = 'FUND'
class WM_MT_toolbar_toggle_pie(Menu):
bl_label = "Toggle Toolbars"
# Map the `region.type` to the `space_data` attribute & text label.
# The order of items defines priority, so in the sequencer for e.g.
# when there is both a toolbar and channels, the toolbar gets the
# axis-aligned pie, and the channels don't.
_region_info = {
'TOOLS': "show_region_toolbar",
'UI': "show_region_ui",
# Note that the tool header is enabled/disabled along with the header,
# no need to include both in this list.
'HEADER': "show_region_header",
'FOOTER': "show_region_footer",
'CHANNELS': "show_region_channels",
}
# Map the `region.alignment` to the axis-aligned pie position.
_region_align_pie = {
'LEFT': 0,
'RIGHT': 1,
'BOTTOM': 2,
'TOP': 3,
}
# Map the axis-aligned pie position to it's opposite side, see `ui_radial_dir_order` in C++ source.
# The value is the preferred direction in order of priority, two diagonals, then the flipped direction.
_region_dir_pie_alternatives = {
0: (4, 6, 1),
1: (5, 7, 0),
2: (6, 7, 3),
3: (4, 5, 2),
}
@classmethod
def poll(cls, context):
return context.space_data is not None
@classmethod
def _draw_pie_regions_from_alignment(cls, context, pie):
space_data = context.space_data
# Store each region by it's type.
region_by_type = {}
for region in context.area.regions:
region_type = region.type
attr = cls._region_info.get(region_type, (None, None))
if attr is None:
continue
# In some cases channels exists but can't be toggled.
if region_type == 'CHANNELS':
if not hasattr(space_data, attr):
continue
# Technically possible these double-up, in practice this should never happen.
if region_type in region_by_type:
print("%s: Unexpected double-up of region types %r" % (type(self).__name__, region_type))
region_by_type[region_type] = region
# Axis aligned pie menu items to populate.
items = [[], [], [], [], [], [], [], []]
# Use predictable ordering.
for region_type in cls._region_info.keys():
region = region_by_type.get(region_type)
if region is None:
continue
index = cls._region_align_pie[region.alignment]
items[index].append(region_type)
# Handle any overflow (two or more regions with the same alignment).
# This happens in the sequencer (channels + toolbar),
# otherwise it should not be that common.
items_overflow = []
for index in range(4):
if len(items[index]) <= 1:
continue
for index_other in cls._region_dir_pie_alternatives[index]:
if not items[index_other]:
items[index_other].append(items[index].pop(1))
if len(items[index]) <= 1:
break
del index_other
for index in range(4):
if len(items[index]) <= 1:
continue
for index_other in range(4, 8):
if not items[index_other]:
items[index_other].append(items[index].pop(1))
if len(items[index]) <= 1:
break
# Only happens when there are more than 8 regions - practically never!
for index in range(4):
while len(items[index]) > 1:
items_overflow.append([items[index].pop(1)])
# Use to access the labels.
enum_items = bpy.types.Region.bl_rna.properties["type"].enum_items_static_ui
for region_type_list in (items + items_overflow):
if not region_type_list:
pie.separator()
continue
assert len(region_type_list) == 1
region_type = region_type_list[0]
text = enum_items[region_type].name
attr = cls._region_info[region_type]
value = getattr(space_data, attr)
props = pie.operator("wm.context_toggle", text=text, icon='CHECKBOX_HLT' if value else 'CHECKBOX_DEHLT')
props.data_path = "space_data." + attr
def draw(self, context):
layout = self.layout
pie = layout.menu_pie()
self._draw_pie_regions_from_alignment(context, pie)
class WM_OT_drop_blend_file(Operator):
bl_idname = "wm.drop_blend_file"
bl_label = "Handle dropped .blend file"
@ -3376,4 +3491,5 @@ classes = (
WM_MT_splash_quick_setup,
WM_MT_splash,
WM_MT_splash_about,
WM_MT_toolbar_toggle_pie
)