CCCPaste

Move/Rotate/Scale - Vertex/Edge/Face - Worksp...

bl_info = {
    'name': 'Move/Rotate/Scale - Vertex/Edge/Face - Workspaces in Context Menu',
    'author': 'TBussinger',
    'version': (1, 0, 0),
    'blender': (4, 0, 0),
    'location': 'Right-Click in any 3D View',
    'description': 'Adds Move/Rotate/Scale - Vertex/Edge/Face - Workspaces to the 3D View context menus',
    'category': '3D View',
}

import bpy
from bpy.props import StringProperty

# --- Workspace switcher (flyout) ---
WORKSPACES = [
    'Layout', 'Modeling', 'Sculpting', 'UV Editing', 'Texture Paint',
    'Shading', 'Animation', 'Rendering', 'Compositing', 'Geometry Nodes', 'Scripting'
]

class WM_OT_switch_workspace_quick(bpy.types.Operator):
    bl_idname = 'wm.switch_workspace_quick'
    bl_label = 'Switch Workspace'
    bl_description = 'Switch the active workspace'
    bl_options = {'INTERNAL'}

    workspace: StringProperty(name='Workspace')

    @classmethod
    def description(cls, context, properties):
        ws = getattr(properties, 'workspace', '')
        return f'Switch to "{ws}" workspace' if ws else 'Switch the active workspace'

    def execute(self, context):
        ws = bpy.data.workspaces.get(self.workspace)
        if not ws:
            self.report({'WARNING'}, 'Workspace "%s" not found' % self.workspace)
            return {'CANCELLED'}
        context.window.workspace = ws
        return {'FINISHED'}

class VIEW3D_MT_workspace_quick(bpy.types.Menu):
    bl_label = 'Workspaces'
    bl_idname = 'VIEW3D_MT_workspace_quick'

    def draw(self, context):
        layout = self.layout
        col = layout.column(align=True)
        for name in WORKSPACES:
            col.operator('wm.switch_workspace_quick', text=name).workspace = name

# --- Context menu injection ---
def draw_trs(self, context):
    layout = self.layout
    layout.separator()

    col = layout.column(align=True)
    col.label(text='Tools')
    col.operator('wm.tool_set_by_id', text='Move').name   = 'builtin.move'
    col.operator('wm.tool_set_by_id', text='Rotate').name = 'builtin.rotate'
    col.operator('wm.tool_set_by_id', text='Scale').name  = 'builtin.scale'

    col = layout.column(align=True)
    col.label(text='Select Mode')
    col.enabled = (context.mode == 'EDIT_MESH')
    col.operator('mesh.select_mode', text='Vertex', icon='VERTEXSEL').type = 'VERT'
    col.operator('mesh.select_mode', text='Edge',   icon='EDGESEL').type   = 'EDGE'
    col.operator('mesh.select_mode', text='Face',   icon='FACESEL').type   = 'FACE'

    layout.separator()
    op = layout.operator('wm.call_menu', text='Workspaces...')
    op.name = 'VIEW3D_MT_workspace_quick'

MENUS = (
    'VIEW3D_MT_object_context_menu',
    'VIEW3D_MT_edit_mesh_context_menu',
    'VIEW3D_MT_edit_curve_context_menu',
    'VIEW3D_MT_edit_armature_context_menu',
    'VIEW3D_MT_pose_context_menu',
)

classes = (
    WM_OT_switch_workspace_quick,
    VIEW3D_MT_workspace_quick,
)

def register():
    for c in classes:
        bpy.utils.register_class(c)
    for menu_id in MENUS:
        menu = getattr(bpy.types, menu_id, None)
        if menu:
            menu.prepend(draw_trs)

def unregister():
    for menu_id in MENUS:
        menu = getattr(bpy.types, menu_id, None)
        if menu:
            try:
                menu.remove(draw_trs)
            except Exception:
                pass
    for c in reversed(classes):
        try:
            bpy.utils.unregister_class(c)
        except Exception:
            pass

if __name__ == '__main__':
    try:
        unregister()
    except Exception:
        pass
    register()