From 6ffcddc10afa07b073ce01c25884e23fc2b661df Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 7 Dec 2019 03:45:50 +1100 Subject: Tool System: experimental fallback tool support Implement T66304 as an experimental option, available under the preferences "Experimental" section. - When enabled most tools in the 3D view have a gizmo. - Dragging outside the gizmo uses the 'fallback' tool. - The fallback tool can be changed or disabled in the tool options or from a pie menu (Alt-W). --- release/scripts/startup/bl_operators/wm.py | 63 +++- .../startup/bl_ui/space_toolsystem_common.py | 364 +++++++++++++++++---- .../startup/bl_ui/space_toolsystem_toolbar.py | 44 ++- release/scripts/startup/bl_ui/space_topbar.py | 18 + release/scripts/startup/bl_ui/space_userpref.py | 6 + 5 files changed, 414 insertions(+), 81 deletions(-) (limited to 'release/scripts/startup') diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 24670b2a37d..1452b6767b6 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -1645,6 +1645,12 @@ class WM_OT_tool_set_by_id(Operator): default=False, options={'SKIP_SAVE'}, ) + as_fallback: BoolProperty( + name="Set Fallback", + description="Set the fallback tool instead of the primary tool", + default=False, + options={'SKIP_SAVE', 'HIDDEN'}, + ) space_type: rna_space_type_prop @@ -1672,7 +1678,10 @@ class WM_OT_tool_set_by_id(Operator): space_type = context.space_data.type fn = activate_by_id_or_cycle if self.cycle else activate_by_id - if fn(context, space_type, self.name): + if fn(context, space_type, self.name, as_fallback=self.as_fallback): + if self.as_fallback: + tool_settings = context.tool_settings + tool_settings.workspace_tool_type = 'FALLBACK' return {'FINISHED'} else: self.report({'WARNING'}, f"Tool {self.name!r:s} not found for space {space_type!r:s}.") @@ -1699,13 +1708,20 @@ class WM_OT_tool_set_by_index(Operator): default=True, ) + as_fallback: BoolProperty( + name="Set Fallback", + description="Set the fallback tool instead of the primary", + default=False, + options={'SKIP_SAVE', 'HIDDEN'}, + ) + space_type: rna_space_type_prop def execute(self, context): from bl_ui.space_toolsystem_common import ( activate_by_id, activate_by_id_or_cycle, - item_from_index, + item_from_index_active, item_from_flat_index, ) @@ -1714,7 +1730,7 @@ class WM_OT_tool_set_by_index(Operator): else: space_type = context.space_data.type - fn = item_from_flat_index if self.expand else item_from_index + fn = item_from_flat_index if self.expand else item_from_index_active item = fn(context, space_type, self.index) if item is None: # Don't report, since the number of tools may change. @@ -1722,7 +1738,10 @@ class WM_OT_tool_set_by_index(Operator): # Same as: WM_OT_tool_set_by_id fn = activate_by_id_or_cycle if self.cycle else activate_by_id - if fn(context, space_type, item.idname): + if fn(context, space_type, item.idname, as_fallback=self.as_fallback): + if self.as_fallback: + tool_settings = context.tool_settings + tool_settings.workspace_tool_type = 'FALLBACK' return {'FINISHED'} else: # Since we already have the tool, this can't happen. @@ -1776,6 +1795,41 @@ class WM_OT_toolbar(Operator): return {'FINISHED'} +class WM_OT_toolbar_fallback_pie(Operator): + bl_idname = "wm.toolbar_fallback_pie" + bl_label = "Fallback Tool Pie Menu" + + @classmethod + def poll(cls, context): + return context.space_data is not None + + def invoke(self, context, event): + if not context.preferences.experimental.use_tool_fallback: + return {'PASS_THROUGH'} + + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + space_type = context.space_data.type + cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) + if cls is None: + return {'PASS_THROUGH'} + + # It's possible we don't have the fallback tool available. + # This can happen in the image editor for example when there is no selection + # in painting modes. + item, _ = cls._tool_get_by_id(context, space_type, cls.tool_fallback_id) + if item is None: + print("Tool", cls.tool_fallback_id, "not active in", cls) + return {'PASS_THROUGH'} + + def draw_cb(self, context): + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + ToolSelectPanelHelper.draw_fallback_tool_items_for_pie_menu(self.layout, context) + + wm = context.window_manager + wm.popup_menu_pie(draw_func=draw_cb, title="Fallback Tool", event=event) + return {'FINISHED'} + + class WM_OT_toolbar_prompt(Operator): """Leader key like functionality for accessing tools""" bl_idname = "wm.toolbar_prompt" @@ -2563,6 +2617,7 @@ classes = ( WM_OT_tool_set_by_id, WM_OT_tool_set_by_index, WM_OT_toolbar, + WM_OT_toolbar_fallback_pie, WM_OT_toolbar_prompt, BatchRenameAction, WM_OT_batch_rename, diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py index 7c7825403a9..a5501301015 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_common.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -162,7 +162,7 @@ class ToolActivePanelHelper: layout.use_property_decorate = False ToolSelectPanelHelper.draw_active_tool_header( context, - layout, + layout.column(), show_tool_name=True, tool_key=ToolSelectPanelHelper._tool_key_from_context(context, space_type=self.bl_space_type), ) @@ -262,76 +262,100 @@ class ToolSelectPanelHelper: else: yield item, -1 - @staticmethod - def _tool_get_active(context, space_type, mode, with_icon=False): + @classmethod + def _tool_get_active(cls, context, space_type, mode, with_icon=False): """ Return the active Python tool definition and icon name. """ - cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) - if cls is not None: - tool_active = ToolSelectPanelHelper._tool_active_from_context(context, space_type, mode) - tool_active_id = getattr(tool_active, "idname", None) - for item in ToolSelectPanelHelper._tools_flatten(cls.tools_from_context(context, mode)): - if item is not None: - if item.idname == tool_active_id: - if with_icon: - icon_value = ToolSelectPanelHelper._icon_value_from_icon_handle(item.icon) - else: - icon_value = 0 - return (item, tool_active, icon_value) + tool_active = ToolSelectPanelHelper._tool_active_from_context(context, space_type, mode) + tool_active_id = getattr(tool_active, "idname", None) + for item in ToolSelectPanelHelper._tools_flatten(cls.tools_from_context(context, mode)): + if item is not None: + if item.idname == tool_active_id: + if with_icon: + icon_value = ToolSelectPanelHelper._icon_value_from_icon_handle(item.icon) + else: + icon_value = 0 + return (item, tool_active, icon_value) return None, None, 0 - @staticmethod - def _tool_get_by_id(context, space_type, idname): + @classmethod + def _tool_get_by_id(cls, context, space_type, idname): """ Return the active Python tool definition and index (if in sub-group, else -1). """ - cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) - if cls is not None: - for item, index in ToolSelectPanelHelper._tools_flatten_with_tool_index(cls.tools_from_context(context)): - if item is not None: + for item, index in ToolSelectPanelHelper._tools_flatten_with_tool_index(cls.tools_from_context(context)): + if item is not None: + if item.idname == idname: + return (item, index) + return None, -1 + + @classmethod + def _tool_get_by_id_active(cls, context, space_type, idname): + """ + Return the active Python tool definition and index (if in sub-group, else -1). + """ + for item in cls.tools_from_context(context): + if item is not None: + if type(item) is tuple: + if item[0].idname == idname: + index = cls._tool_group_active.get(item[0].idname, 0) + return (item[index], index) + else: if item.idname == idname: - return (cls, item, index) - return None, None, -1 + return (item, -1) + return None, -1 - @staticmethod - def _tool_get_by_flat_index(context, space_type, tool_index): + @classmethod + def _tool_get_by_id_active_with_group(cls, context, space_type, idname): + """ + Return the active Python tool definition and index (if in sub-group, else -1). + """ + for item in cls.tools_from_context(context): + if item is not None: + if type(item) is tuple: + if item[0].idname == idname: + index = cls._tool_group_active.get(item[0].idname, 0) + return (item[index], index, item) + else: + if item.idname == idname: + return (item, -1, None) + return None, -1, None + + @classmethod + def _tool_get_by_flat_index(cls, context, space_type, tool_index): """ Return the active Python tool definition and index (if in sub-group, else -1). Return the index of the expanded list. """ - cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) - if cls is not None: - i = 0 - for item, index in ToolSelectPanelHelper._tools_flatten_with_tool_index(cls.tools_from_context(context)): - if item is not None: - if i == tool_index: - return (cls, item, index) - i += 1 - return None, None, -1 + i = 0 + for item, index in ToolSelectPanelHelper._tools_flatten_with_tool_index(cls.tools_from_context(context)): + if item is not None: + if i == tool_index: + return (item, index) + i += 1 + return None, -1 - @staticmethod - def _tool_get_by_index(context, space_type, tool_index): + @classmethod + def _tool_get_active_by_index(cls, context, space_type, tool_index): """ Return the active Python tool definition and index (if in sub-group, else -1). Return the index of the list without expanding. """ - cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) - if cls is not None: - i = 0 - for item in cls.tools_from_context(context): - if item is not None: - if i == tool_index: - if type(item) is tuple: - index = cls._tool_group_active.get(item[0].idname, 0) - item = item[index] - else: - index = -1 - return (cls, item, index) - i += 1 - return None, None, -1 + i = 0 + for item in cls.tools_from_context(context): + if item is not None: + if i == tool_index: + if type(item) is tuple: + index = cls._tool_group_active.get(item[0].idname, 0) + item = item[index] + else: + index = -1 + return (item, index) + i += 1 + return None, -1 @staticmethod def _tool_active_from_context(context, space_type, mode=None, create=False): @@ -633,6 +657,24 @@ class ToolSelectPanelHelper: space_type = context.space_data.type return ToolSelectPanelHelper._tool_active_from_context(context, space_type) + + @staticmethod + def draw_active_tool_fallback( + context, layout, tool, + *, + is_horizontal_layout=False, + ): + tool_fallback = tool.tool_fallback + space_type = tool.space_type + cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) + item_fallback, _index = cls._tool_get_by_id(context, space_type, tool_fallback) + if item_fallback is not None: + draw_settings = item_fallback.draw_settings + if draw_settings is not None: + if not is_horizontal_layout: + layout.separator() + draw_settings(context, layout, tool) + @staticmethod def draw_active_tool_header( context, layout, @@ -640,6 +682,7 @@ class ToolSelectPanelHelper: show_tool_name=False, tool_key=None, ): + is_horizontal_layout = layout.direction != 'VERTICAL' if tool_key is None: space_type, mode = ToolSelectPanelHelper._tool_key_from_context(context) else: @@ -647,7 +690,9 @@ class ToolSelectPanelHelper: if space_type is None: return None - item, tool, icon_value = ToolSelectPanelHelper._tool_get_active(context, space_type, mode, with_icon=True) + + cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) + item, tool, icon_value = cls._tool_get_active(context, space_type, mode, with_icon=True) if item is None: return None # Note: we could show 'item.text' here but it makes the layout jitter when switching tools. @@ -658,8 +703,128 @@ class ToolSelectPanelHelper: draw_settings = item.draw_settings if draw_settings is not None: draw_settings(context, layout, tool) + + if context.preferences.experimental.use_tool_fallback: + tool_fallback = tool.tool_fallback + else: + tool_fallback = None + + if tool_fallback and tool_fallback != item.idname: + tool_settings = context.tool_settings + + # Show popover which looks like an enum but isn't one. + if tool_settings.workspace_tool_type == 'FALLBACK': + tool_fallback_id = cls.tool_fallback_id + item, _select_index = cls._tool_get_by_id_active(context, space_type, tool_fallback_id) + label = item.label + else: + label = "Active Tool" + + split = layout.split(factor=0.5) + row = split.row() + row.alignment = 'RIGHT' + row.label(text="Drag") + row = split.row() + row.context_pointer_set("tool", tool) + row.popover(panel="TOPBAR_PT_tool_fallback", text=label) + return tool + # Show a list of tools in the popover. + @staticmethod + def draw_fallback_tool_items(layout, context): + + space_type = context.space_data.type + if space_type == 'PROPERTIES': + space_type = 'VIEW_3D' + + cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) + tool_fallback_id = cls.tool_fallback_id + + _item, _select_index, item_group = cls._tool_get_by_id_active_with_group(context, space_type, tool_fallback_id) + + if item_group is None: + # Could print comprehensive message - listing available items. + raise Exception("Fallback tool doesn't exist") + + col = layout.column(align=True) + tool_settings = context.tool_settings + col.prop_enum( + tool_settings, + "workspace_tool_type", + value='DEFAULT', + text="Active Tool", + ) + is_active_tool = (tool_settings.workspace_tool_type == 'DEFAULT') + + col = layout.column(align=True) + if is_active_tool: + index_current = -1 + else: + index_current = cls._tool_group_active.get(item_group[0].idname, 0) + for i, sub_item in enumerate(item_group): + is_active = (i == index_current) + + props = col.operator( + "wm.tool_set_by_id", + text=sub_item.label, + depress=is_active, + ) + props.name = sub_item.idname + props.as_fallback = True + props.space_type = space_type + + @staticmethod + def draw_fallback_tool_items_for_pie_menu(layout, context): + space_type = context.space_data.type + if space_type == 'PROPERTIES': + space_type = 'VIEW_3D' + + cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) + tool_fallback_id = cls.tool_fallback_id + + _item, _select_index, item_group = cls._tool_get_by_id_active_with_group(context, space_type, tool_fallback_id) + + if item_group is None: + # Could print comprehensive message - listing available items. + raise Exception("Fallback tool doesn't exist") + + # Allow changing the active tool, + # even though this isn't the purpose of the pie menu + # it's confusing from a user perspective if we don't allow it. + is_fallback_group_active = getattr( + ToolSelectPanelHelper._tool_active_from_context(context, space_type), + "idname", None, + ) in (item.idname for item in item_group) + + pie = layout.menu_pie() + tool_settings = context.tool_settings + pie.prop_enum( + tool_settings, + "workspace_tool_type", + value='DEFAULT', + text="Active Tool", + icon='TOOL_SETTINGS', # Could use a less generic icon. + ) + is_active_tool = (tool_settings.workspace_tool_type == 'DEFAULT') + + if is_active_tool: + index_current = -1 + else: + index_current = cls._tool_group_active.get(item_group[0].idname, 0) + for i, sub_item in enumerate(item_group): + is_active = (i == index_current) + props = pie.operator( + "wm.tool_set_by_id", + text=sub_item.label, + depress=is_active, + icon_value=ToolSelectPanelHelper._icon_value_from_icon_handle(sub_item.icon), + ) + props.name = sub_item.idname + props.space_type = space_type + if not is_fallback_group_active: + props.as_fallback = True + # 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. @@ -701,7 +866,43 @@ class WM_MT_toolsystem_submenu(Menu): ).name = item.idname -def _activate_by_item(context, space_type, item, index): +def _activate_by_item(context, space_type, item, index, *, as_fallback=False): + cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) + tool_fallback_id = cls.tool_fallback_id + + if as_fallback: + # To avoid complicating logic too much, isolate all fallback logic to this block. + # This will set the tool again, using the item for the fallback instead of the primary tool. + # + # If this ends up needing to be more complicated, + # it would be better to split it into a separate function. + + _item, _select_index, item_group = cls._tool_get_by_id_active_with_group(context, space_type, tool_fallback_id) + + if item_group is None: + # Could print comprehensive message - listing available items. + raise Exception("Fallback tool doesn't exist") + index_new = -1 + for i, sub_item in enumerate(item_group): + if sub_item.idname == item.idname: + index_new = i + break + if index_new == -1: + raise Exception("Fallback tool not found in group") + + cls._tool_group_active[tool_fallback_id] = index_new + + # Done, now get the current tool to replace the item & index. + tool_active = ToolSelectPanelHelper._tool_active_from_context(context, space_type) + item, index = cls._tool_get_by_id(context, space_type, getattr(tool_active, "idname", None)) + + # Find fallback keymap. + item_fallback = None + _item, select_index = cls._tool_get_by_id(context, space_type, tool_fallback_id) + if select_index != -1: + item_fallback, _index = cls._tool_get_active_by_index(context, space_type, select_index) + # End calculating fallback. + tool = ToolSelectPanelHelper._tool_active_from_context(context, space_type, create=True) tool.setup( idname=item.idname, @@ -711,6 +912,9 @@ def _activate_by_item(context, space_type, item, index): data_block=item.data_block or "", operator=item.operator or "", index=index, + + idname_fallback=item_fallback.idname if item_fallback else "", + keymap_fallback=(item_fallback.keymap[0] or "") if item_fallback else "", ) WindowManager = bpy.types.WindowManager @@ -729,18 +933,22 @@ def _activate_by_item(context, space_type, item, index): _activate_by_item._cursor_draw_handle = {} -def activate_by_id(context, space_type, text): - _cls, item, index = ToolSelectPanelHelper._tool_get_by_id(context, space_type, text) +def activate_by_id(context, space_type, idname, *, as_fallback=False): + cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) + if cls is None: + return False + item, index = cls._tool_get_by_id(context, space_type, idname) if item is None: return False - _activate_by_item(context, space_type, item, index) + _activate_by_item(context, space_type, item, index, as_fallback=as_fallback) return True -def activate_by_id_or_cycle(context, space_type, idname, offset=1): +def activate_by_id_or_cycle(context, space_type, idname, *, offset=1, as_fallback=False): # Only cycle when the active tool is activated again. - cls, item, _index = ToolSelectPanelHelper._tool_get_by_id(context, space_type, idname) + cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) + item, _index = cls._tool_get_by_id(context, space_type, idname) if item is None: return False @@ -774,7 +982,8 @@ def activate_by_id_or_cycle(context, space_type, idname, offset=1): def description_from_id(context, space_type, idname, *, use_operator=True): # Used directly for tooltips. - _cls, item, _index = ToolSelectPanelHelper._tool_get_by_id(context, space_type, idname) + cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) + item, _index = cls._tool_get_by_id(context, space_type, idname) if item is None: return False @@ -806,23 +1015,52 @@ def description_from_id(context, space_type, idname, *, use_operator=True): def item_from_id(context, space_type, idname): # Used directly for tooltips. - _cls, item, _index = ToolSelectPanelHelper._tool_get_by_id(context, space_type, idname) + cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) + if cls is None: + return None + item, _index = cls._tool_get_by_id(context, space_type, idname) + return item + + +def item_from_id_active(context, space_type, idname): + # Used directly for tooltips. + cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) + if cls is None: + return None + item, _index = cls._tool_get_by_id_active(context, space_type, idname) + return item + + +def item_from_id_active_with_group(context, space_type, idname): + cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) + if cls is None: + return None + cls, item, _index = cls._tool_get_by_id_active_with_group(context, space_type, idname) return item def item_from_flat_index(context, space_type, index): - _cls, item, _index = ToolSelectPanelHelper._tool_get_by_flat_index(context, space_type, index) + cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) + if cls is None: + return None + item, _index = cls._tool_get_by_flat_index(context, space_type, index) return item -def item_from_index(context, space_type, index): - _cls, item, _index = ToolSelectPanelHelper._tool_get_by_index(context, space_type, index) +def item_from_index_active(context, space_type, index): + cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) + if cls is None: + return None + item, _index = cls._tool_get_active_by_index(context, space_type, index) return item def keymap_from_id(context, space_type, idname): # Used directly for tooltips. - _cls, item, _index = ToolSelectPanelHelper._tool_get_by_id(context, space_type, idname) + cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) + if cls is None: + return None + item, _index = cls._tool_get_by_id(context, space_type, idname) if item is None: return False diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 9a5df62da46..68d39a3be64 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -297,8 +297,15 @@ class _defs_transform: if layout.use_property_split: layout.label(text="Gizmos:") - props = tool.gizmo_group_properties("VIEW3D_GGT_xform_gizmo") - layout.prop(props, "drag_action") + show_drag = True + if context.preferences.experimental.use_tool_fallback: + tool_settings = context.tool_settings + if tool_settings.workspace_tool_type == 'FALLBACK': + show_drag = False + + if show_drag: + props = tool.gizmo_group_properties("VIEW3D_GGT_xform_gizmo") + layout.prop(props, "drag_action") _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 1) @@ -472,7 +479,7 @@ class _defs_edit_mesh: idname="builtin.rip_region", label="Rip Region", icon="ops.mesh.rip", - widget=None, + widget="VIEW3D_GGT_tool_generic_handle_free", keymap=(), draw_settings=draw_settings, ) @@ -512,7 +519,7 @@ class _defs_edit_mesh: idname="builtin.edge_slide", label="Edge Slide", icon="ops.transform.edge_slide", - widget=None, + widget="VIEW3D_GGT_tool_generic_handle_normal", keymap=(), draw_settings=draw_settings, ) @@ -527,7 +534,7 @@ class _defs_edit_mesh: idname="builtin.vertex_slide", label="Vertex Slide", icon="ops.transform.vert_slide", - widget=None, + widget="VIEW3D_GGT_tool_generic_handle_free", keymap=(), draw_settings=draw_settings, ) @@ -579,7 +586,7 @@ class _defs_edit_mesh: idname="builtin.inset_faces", label="Inset Faces", icon="ops.mesh.inset", - widget=None, + widget="VIEW3D_GGT_tool_generic_handle_normal", keymap=(), draw_settings=draw_settings, ) @@ -597,7 +604,7 @@ class _defs_edit_mesh: idname="builtin.bevel", label="Bevel", icon="ops.mesh.bevel", - widget=None, + widget="VIEW3D_GGT_tool_generic_handle_normal", keymap=(), draw_settings=draw_settings, ) @@ -629,7 +636,7 @@ class _defs_edit_mesh: idname="builtin.extrude_along_normals", label="Extrude Along Normals", icon="ops.mesh.extrude_region_shrink_fatten", - widget=None, + widget="VIEW3D_GGT_tool_generic_handle_normal", operator="mesh.extrude_region_shrink_fatten", keymap=(), draw_settings=draw_settings, @@ -641,7 +648,7 @@ class _defs_edit_mesh: idname="builtin.extrude_individual", label="Extrude Individual", icon="ops.mesh.extrude_faces_move", - widget=None, + widget="VIEW3D_GGT_tool_generic_handle_normal", keymap=(), ) @@ -698,7 +705,7 @@ class _defs_edit_mesh: idname="builtin.smooth", label="Smooth", icon="ops.mesh.vertices_smooth", - widget=None, + widget="VIEW3D_GGT_tool_generic_handle_normal", keymap=(), draw_settings=draw_settings, ) @@ -714,7 +721,7 @@ class _defs_edit_mesh: idname="builtin.randomize", label="Randomize", icon="ops.transform.vertex_random", - widget=None, + widget="VIEW3D_GGT_tool_generic_handle_normal", keymap=(), draw_settings=draw_settings, ) @@ -753,7 +760,7 @@ class _defs_edit_mesh: idname="builtin.shrink_fatten", label="Shrink/Fatten", icon="ops.transform.shrink_fatten", - widget=None, + widget="VIEW3D_GGT_tool_generic_handle_normal", keymap=(), draw_settings=draw_settings, ) @@ -764,7 +771,7 @@ class _defs_edit_mesh: idname="builtin.push_pull", label="Push/Pull", icon="ops.transform.push_pull", - widget=None, + widget="VIEW3D_GGT_tool_generic_handle_normal", keymap=(), ) @@ -890,7 +897,7 @@ class _defs_edit_curve: idname="builtin.randomize", label="Randomize", icon="ops.curve.vertex_random", - widget=None, + widget="VIEW3D_GGT_tool_generic_handle_normal", keymap=(), draw_settings=draw_settings, ) @@ -1682,6 +1689,9 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel): # Satisfy the 'ToolSelectPanelHelper' API. keymap_prefix = "Image Editor Tool:" + # Default group to use as a fallback. + tool_fallback_id = "builtin.select" + @classmethod def tools_from_context(cls, context, mode=None): if mode is None: @@ -1766,6 +1776,9 @@ class NODE_PT_tools_active(ToolSelectPanelHelper, Panel): # Satisfy the 'ToolSelectPanelHelper' API. keymap_prefix = "Node Editor Tool:" + # Default group to use as a fallback. + tool_fallback_id = "builtin.select" + @classmethod def tools_from_context(cls, context, mode=None): if mode is None: @@ -1822,6 +1835,9 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): # Satisfy the 'ToolSelectPanelHelper' API. keymap_prefix = "3D View Tool:" + # Default group to use as a fallback. + tool_fallback_id = "builtin.select" + @classmethod def tools_from_context(cls, context, mode=None): if mode is None: diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index dcfeb6a210f..09531cb5ef6 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -78,6 +78,23 @@ class TOPBAR_HT_upper_bar(Header): unlink="scene.view_layer_remove") +class TOPBAR_PT_tool_fallback(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'HEADER' + bl_label = "Layers" + bl_ui_units_x = 8 + + def draw(self, context): + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + layout = self.layout + + tool_settings = context.tool_settings + ToolSelectPanelHelper.draw_fallback_tool_items(layout, context) + if tool_settings.workspace_tool_type == 'FALLBACK': + tool = context.tool + ToolSelectPanelHelper.draw_active_tool_fallback(context, layout, tool) + + class TOPBAR_PT_gpencil_layers(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'HEADER' @@ -772,6 +789,7 @@ classes = ( TOPBAR_MT_render, TOPBAR_MT_window, TOPBAR_MT_help, + TOPBAR_PT_tool_fallback, TOPBAR_PT_gpencil_layers, TOPBAR_PT_gpencil_primitive, TOPBAR_PT_gpencil_fill, diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index e9ccbbabdd3..130db518cb7 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2188,6 +2188,12 @@ class USERPREF_PT_experimental_all(ExperimentalPanel, Panel): # For the other settings create new panels # and make sure they are disabled if use_experimental_all is True + url_prefix = "https://developer.blender.org/" + + row = col.row() + row.prop(experimental, "use_tool_fallback") + + row.operator("wm.url_open", text="", icon='URL').url = url_prefix + "T66304" """ # Example panel, leave it here so we always have a template to follow even -- cgit v1.2.3