From ab7161e41a0ea6e5645524e6344a5410c06f2143 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 26 Jul 2023 15:23:31 +1000 Subject: [PATCH] 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. --- scripts/presets/keyconfig/Blender.py | 13 ++ .../keyconfig/keymap_data/blender_default.py | 46 +++++-- scripts/startup/bl_operators/wm.py | 116 ++++++++++++++++++ 3 files changed, 166 insertions(+), 9 deletions(-) diff --git a/scripts/presets/keyconfig/Blender.py b/scripts/presets/keyconfig/Blender.py index e6cab0a2425..7b0c67ae47b 100644 --- a/scripts/presets/keyconfig/Blender.py +++ b/scripts/presets/keyconfig/Blender.py @@ -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, diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index ee3ada32691..8dc2cd22a81 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -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'}, ), diff --git a/scripts/startup/bl_operators/wm.py b/scripts/startup/bl_operators/wm.py index 1de1385bbb1..00ff5802290 100644 --- a/scripts/startup/bl_operators/wm.py +++ b/scripts/startup/bl_operators/wm.py @@ -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 )