From f7af3d5c2c9f5d54b6975a523dae812449fe4ff1 Mon Sep 17 00:00:00 2001 From: doc-code Date: Sun, 8 Sep 2024 22:45:58 +0200 Subject: [PATCH] Refactor, UI fixes, fix move op --- addon/__init__.py | 9 +++- addon/sw/funcs.py | 39 +++++++++++--- addon/sw/menu.py | 2 + addon/sw/ops.py | 124 ++++++++++++++++++++++++++++++++------------- addon/sw/topbar.py | 3 +- 5 files changed, 132 insertions(+), 45 deletions(-) diff --git a/addon/__init__.py b/addon/__init__.py index 51f4100..edf5c63 100644 --- a/addon/__init__.py +++ b/addon/__init__.py @@ -14,8 +14,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . import bpy -from .sw.funcs import select_all, workspace_menu -from .sw.ops import SW_OT_workspace, SW_OT_link_workspace, SW_OT_move, SW_OT_link_all, SW_OT_remove, SW_OT_switch, SW_OT_rename, SW_OT_copy_from_scene +from .sw.funcs import select_all, workspace_menu, assign_shortcuts, remove_shortcuts +from .sw.ops import SW_OT_workspace, SW_OT_link_workspace, SW_OT_move, SW_OT_link_all, SW_OT_remove, SW_OT_switch, SW_OT_rename, SW_OT_copy_from_scene, SW_OT_activate from .sw.topbar import TOPBAR_HT_upper_bar from .sw.panel import SW_PT_select_scene_workspaces from .sw.menu import SW_MT_missing_workspaces, SW_MT_copy_from @@ -38,6 +38,7 @@ classes = [ SW_OT_move, SW_OT_remove, SW_OT_rename, + SW_OT_activate, SW_OT_link_workspace, SW_OT_link_all, SW_OT_copy_from_scene, @@ -56,6 +57,8 @@ def register(): for c in classes: bpy.utils.register_class(c) bpy.types.TOPBAR_MT_workspace_menu.prepend(workspace_menu) + + assign_shortcuts() def unregister(): for c in reversed(classes): @@ -63,5 +66,7 @@ def unregister(): bpy.utils.register_class(og_header) bpy.types.TOPBAR_MT_workspace_menu.remove(workspace_menu) + remove_shortcuts() + if __name__ == "__main__": register() diff --git a/addon/sw/funcs.py b/addon/sw/funcs.py index eecc97d..56d7e36 100644 --- a/addon/sw/funcs.py +++ b/addon/sw/funcs.py @@ -34,6 +34,29 @@ def select_all(): def get_other_scene_names(): return [s.name for s in bpy.data.scenes if s.name != bpy.context.scene.name] +def tag_redraw_topbar(): + for area in bpy.context.window.screen.areas: + if area.type == 'TOPBAR': + area.tag_redraw() + break + +def assign_shortcuts(): + wm = bpy.context.window_manager + km = wm.keyconfigs.addon.keymaps.new(name='Window', space_type='EMPTY') + + prev = km.keymap_items.new('sw.activate', 'PAGE_UP', 'PRESS', ctrl=True, shift=True) + prev.properties.next = False + + next = km.keymap_items.new('sw.activate', 'PAGE_DOWN', 'PRESS', ctrl=True, shift=True) + next.properties.next = True + +def remove_shortcuts(): + wm = bpy.context.window_manager + km = wm.keyconfigs.addon.keymaps.new(name='Window', space_type='EMPTY') + for kmi in km.keymap_items: + if kmi.idname in ['sw.activate', 'sw.move'] : + km.keymap_items.remove(kmi) + def workspace_ops(layout, i, menu_mode=False, name=None): layout.separator() if menu_mode: @@ -51,24 +74,28 @@ def workspace_ops(layout, i, menu_mode=False, name=None): rename.current_name = name if name else bpy.context.window.workspace.name rename.new_name = rename.current_name layout.separator() - top = layout.operator("sw.move", text="Reorder to Front" if menu_mode else "", + top = layout.operator("sw.move", text="Reorder to Front (scene only)" if menu_mode else "", icon='TRIA_LEFT_BAR' if menu_mode else 'TRIA_UP_BAR') top.index = i top.top = True - up = layout.operator("sw.move", text="Move Left" if menu_mode else "", + up = layout.operator("sw.move", text="Move Left (scene only)" if menu_mode else "", icon='TRIA_LEFT' if menu_mode else 'TRIA_UP') up.index = i up.up = 1 - down = layout.operator("sw.move", text="Move Right" if menu_mode else "", + down = layout.operator("sw.move", text="Move Right (scene only)" if menu_mode else "", icon='TRIA_RIGHT' if menu_mode else 'TRIA_DOWN') down.index = i down.up = -1 - bottom = layout.operator("sw.move", text="Reorder to Back" if menu_mode else "", + bottom = layout.operator("sw.move", text="Reorder to Back (scene only)" if menu_mode else "", icon='TRIA_RIGHT_BAR' if menu_mode else 'TRIA_DOWN_BAR') bottom.index = i bottom.top = False - if not menu_mode: - layout.separator() + layout.separator() + if menu_mode and bpy.context.window.workspace.name in get_scene_workspaces(): + layout.separator() + layout.operator("sw.activate", text="Previous Workspace (scene)" if menu_mode else "").next = False + layout.operator("sw.activate", text="Next Workspace (scene)" if menu_mode else "").next = True + else: remove(layout) def workspace_menu(self, context): diff --git a/addon/sw/menu.py b/addon/sw/menu.py index eef69ed..a72165a 100644 --- a/addon/sw/menu.py +++ b/addon/sw/menu.py @@ -7,6 +7,8 @@ class SW_MT_copy_from(Menu): def draw(self, context): layout = self.layout + layout.label(text="Copy workspaces from scene") + layout.separator() for s in bpy.data.scenes: if s.name != bpy.context.scene.name: layout.operator("sw.copy_from_scene", text=s.name, icon="SCENE_DATA").scene = s.name diff --git a/addon/sw/ops.py b/addon/sw/ops.py index 7dbd519..b2b6070 100644 --- a/addon/sw/ops.py +++ b/addon/sw/ops.py @@ -2,65 +2,113 @@ from .. import __package__ as base_package import bpy from bpy.types import Operator from bpy.props import StringProperty, BoolProperty, IntProperty -from .funcs import select_all, get_scene_workspaces, set_scene_workspaces, get_use_topbar, has_data, prefs +from .funcs import select_all, get_scene_workspaces, set_scene_workspaces, get_use_topbar, has_data, prefs, tag_redraw_topbar + class SW_OT_switch(Operator): bl_idname = "sw.switch" bl_label = "Show/Hide Scene Workspaces" bl_description = "Switch workspaces topbar between Scene Workspaces and default" - + def execute(self, context): prefs().use_topbar = not get_use_topbar() - context.preferences.is_dirty = True - return {'FINISHED'} - + context.preferences.is_dirty = True + return {'FINISHED'} + + class SW_OT_workspace(Operator): bl_idname = "sw.workspace" bl_label = "Workspace" bl_description = "Make this workspace active (or remove it from the scene, if missing)" - + workspace: StringProperty(name="workspace") - + def invoke(self, context, ev): if self.workspace in [x.name for x in bpy.data.workspaces]: bpy.context.window.workspace = bpy.data.workspaces[self.workspace] - else: + else: l = get_scene_workspaces() l.pop(l.index(self.workspace)) - set_scene_workspaces(l) + set_scene_workspaces(l) return {'FINISHED'} - + + class SW_OT_link_all(Operator): bl_idname = "sw.link_all" bl_label = "Link all workspaces to this Scene" bl_description = "Link all workspaces avaliable to this scene" - + def execute(self, context): select_all() return {'FINISHED'} - + + class SW_OT_move(Operator): bl_idname = "sw.move" bl_label = "Reorder Workspace" bl_description = "Reorder this workspace in the desired location (only for this Scene)" - - up: IntProperty(name="up", default=0) # 0 means using `top` for reorder back/front + + # 0 means using `top` for reorder back/front + up: IntProperty(name="up", default=0) index: IntProperty(name="index", default=-1) top: BoolProperty(name="top", default=False) + def valid(self, context): + l = get_scene_workspaces() + i = self.index if self.index != -1 else l.index(bpy.context.window.workspace.name) + max_i = len(l) - 1 + return (self.up == 1 and i > 0) or (self.up == -1 and i < max_i) or (self.up == 0 and ((self.top and i > 0) or (not self.top and i < max_i))) + def execute(self, context): l = get_scene_workspaces() - if (self.up > 0 and self.index > 0) or (self.up < 0 and self.index < len(get_scene_workspaces()) - 1): - l.insert(self.index-self.up, l.pop(self.index)) - set_scene_workspaces(l) - elif self.up == 0: - l.insert(0 if self.top else len(l)-1, l.pop(self.index)) - set_scene_workspaces(l) + if self.valid(context): + try: + i = self.index if self.index != -1 else l.index(bpy.context.window.workspace.name) + if self.up == 0: + l.insert(0 if self.top else len(l)-1, l.pop(i)) + else: + l.insert(i-self.up, l.pop(i)) - self.up = 0 - self.top = False + set_scene_workspaces(l) + self.up = 0 + self.top = False + except: + pass return {'FINISHED'} - + + +class SW_OT_activate(Operator): + bl_idname = "sw.activate" + bl_label = "Activate Next/Previous Workspace" + + next: BoolProperty(name="next") + + @classmethod + def description(cls, context, props): + return f"Activate {'the next' if props.next else 'the previous'} workspace" + + def execute(self, context): + l = get_scene_workspaces() + ws = bpy.data.workspaces + try: + active_i = l.index(bpy.context.window.workspace.name) + w = None + if self.next: + if active_i < len(l)-1: + w = ws[l[active_i+1]] + else: + w = ws[l[0]] + else: + if active_i == 0: + w = ws[l[len(l)-1]] + else: + w = ws[l[active_i-1]] + bpy.context.window.workspace = w + except: + pass + return {'FINISHED'} + + class SW_OT_rename(Operator): bl_idname = "sw.rename" bl_label = "Rename workspace" @@ -83,53 +131,57 @@ class SW_OT_rename(Operator): l.pop(i) l.insert(i, self.new_name) set_scene_workspaces(l, s.name) - finally: - continue + except: + pass return {'FINISHED'} def invoke(self, context, event): return context.window_manager.invoke_props_dialog(self, title=f"Rename Workspace '{self.current_name}'") - + + class SW_OT_remove(Operator): bl_idname = "sw.remove" bl_label = "Unlink Workspace" bl_description = "Unlink this workspace from this scene (this will not delete the workspace)" - + index: IntProperty(name="index", default=-1) - + def execute(self, context): l = get_scene_workspaces() l.pop(self.index) set_scene_workspaces(l) return {'FINISHED'} - + + class SW_OT_copy_from_scene(Operator): bl_idname = "sw.copy_from_scene" bl_label = "Copy from another Scene" bl_description = "Copy linked workspaces from this scene" - + scene: StringProperty(name="scene") - + def execute(self, context): l = get_scene_workspaces(self.scene) set_scene_workspaces(l) return {'FINISHED'} - + + class SW_OT_link_workspace(Operator): bl_idname = "sw.link_workspace" bl_label = "Link Workspace" - + workspace: StringProperty(name="workspace") @classmethod def description(cls, context, props): return "This is the active workspace, but it's not linked to this scene. Click to link" if props.workspace == bpy.context.window.workspace.name else "Link this workspace to this Scene" - + def execute(self, context): if not has_data(): set_scene_workspaces([]) if self.workspace in get_scene_workspaces(): - set_scene_workspaces([x for x in get_scene_workspaces() if x != self.workspace]) - else: + set_scene_workspaces( + [x for x in get_scene_workspaces() if x != self.workspace]) + else: set_scene_workspaces([*get_scene_workspaces(), self.workspace]) return {'FINISHED'} diff --git a/addon/sw/topbar.py b/addon/sw/topbar.py index 607228f..6e88322 100644 --- a/addon/sw/topbar.py +++ b/addon/sw/topbar.py @@ -99,7 +99,8 @@ class TOPBAR_HT_upper_bar(Header): # Original Add Workspace menu c = r.column() c.scale_x = size - c.operator("workspace.add", text="", icon="ADD") + c = c.box() + c.operator("workspace.add", text="", icon="ADD", emboss=False) if not compact: r.separator()