Add copy from scene menu, refactor, UI updates

This commit is contained in:
Francesco Bellini 2024-09-07 16:21:43 +02:00
parent c8acd91d45
commit 782e507709
7 changed files with 150 additions and 116 deletions

@ -14,13 +14,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import bpy
from bpy.app.handlers import persistent
from .classes.funcs import init_data, draw_workspace_menu
from .classes.ops import WBS_OT_workspace, WBS_OT_sel_workspace, WBS_OT_move, WBS_OT_sel_all, WBS_OT_remove, WBS_OT_switch, WBS_OT_rename
from .classes.funcs import select_all, draw_workspace_menu
from .classes.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 .classes.topbar import TOPBAR_HT_upper_bar
from .classes.panel import WBS_PT_select_scene_workspaces
from .classes.menu import WBS_MT_missing_workspaces
from .classes.prefs import WBSPreferences
from .classes.panel import SW_PT_select_scene_workspaces
from .classes.menu import SW_MT_missing_workspaces, SW_MT_copy_from
from .classes.prefs import SWPreferences
bl_info = {
"id": "scene_workspaces",
@ -33,21 +32,19 @@ bl_info = {
"blender_manifest": "blender_manifest.toml"
}
@persistent
def load_handler(dummy):
init_data()
classes = [
WBS_OT_workspace,
WBS_OT_switch,
WBS_OT_move,
WBS_OT_remove,
WBS_OT_sel_all,
WBS_OT_sel_workspace,
WBS_OT_rename,
WBS_MT_missing_workspaces,
WBS_PT_select_scene_workspaces,
WBSPreferences,
SW_OT_workspace,
SW_OT_switch,
SW_OT_move,
SW_OT_remove,
SW_OT_link_all,
SW_OT_link_workspace,
SW_OT_rename,
SW_OT_copy_from_scene,
SW_MT_missing_workspaces,
SW_MT_copy_from,
SW_PT_select_scene_workspaces,
SWPreferences,
TOPBAR_HT_upper_bar
]
@ -58,14 +55,12 @@ def register():
og_header = bpy.types.TOPBAR_HT_upper_bar;
for c in classes:
bpy.utils.register_class(c)
bpy.app.handlers.load_post.append(load_handler)
bpy.types.TOPBAR_MT_workspace_menu.append(draw_workspace_menu)
bpy.types.TOPBAR_MT_workspace_menu.prepend(draw_workspace_menu)
def unregister():
for c in reversed(classes):
bpy.utils.unregister_class(c)
bpy.utils.register_class(og_header)
bpy.app.handlers.load_post.remove(load_handler)
bpy.types.TOPBAR_MT_workspace_menu.remove(draw_workspace_menu)
if __name__ == "__main__":

@ -1,20 +1,12 @@
import bpy
from .. import __package__ as base_package
def redraw_props():
for area in bpy.context.window.screen.areas:
if area.type == 'TOPBAR':
area.tag_redraw()
for area in bpy.context.screen.areas:
if area.type == 'PROPERTIES':
area.tag_redraw()
def has_data():
return 'scene_workspaces' in bpy.data.scenes[bpy.context.scene.name]
def get_scene_workspaces():
def get_scene_workspaces(scene = None):
if has_data():
return bpy.data.scenes[bpy.context.scene.name]['scene_workspaces']
return bpy.data.scenes[scene if scene else bpy.context.scene.name]['scene_workspaces']
else:
return []
@ -23,46 +15,48 @@ def get_use_global():
def set_scene_workspaces(workspaces):
bpy.data.scenes[bpy.context.scene.name]['scene_workspaces'] = workspaces
redraw_props()
def init_data():
def select_all():
workspaces = []
for w in bpy.data.workspaces:
workspaces.append(w.name)
set_scene_workspaces(workspaces)
def get_other_scene_names():
return [s.name for s in bpy.data.scenes if s.name != bpy.context.scene.name]
def draw_workspace_ops(layout, i, text=False, name=None):
layout.separator()
remove = layout.operator(
"sw.remove", text="Unlink workspace from scene" if text else "", icon='X')
remove.index = i
layout.separator()
rename = layout.operator(
"wbs.rename", text="Rename" if text else "", icon="TEXT")
"sw.rename", text="Rename" if text else "", icon="TEXT")
rename.current_name = name if name else bpy.context.window.workspace.name
rename.new_name = rename.current_name
layout.separator()
top = layout.operator("wbs.move", text="Reorder to Front" if text else "",
top = layout.operator("sw.move", text="Reorder to Front" if text else "",
icon='TRIA_LEFT_BAR' if text else 'TRIA_UP_BAR')
top.index = i
top.top = True
up = layout.operator("wbs.move", text="Move Left" if text else "",
up = layout.operator("sw.move", text="Move Left" if text else "",
icon='TRIA_LEFT' if text else 'TRIA_UP')
up.index = i
up.up = 1
down = layout.operator("wbs.move", text="Move Right" if text else "",
down = layout.operator("sw.move", text="Move Right" if text else "",
icon='TRIA_RIGHT' if text else 'TRIA_DOWN')
down.index = i
down.up = -1
bottom = layout.operator("wbs.move", text="Reorder to Back" if text else "",
bottom = layout.operator("sw.move", text="Reorder to Back" if text else "",
icon='TRIA_RIGHT_BAR' if text else 'TRIA_DOWN_BAR')
bottom.index = i
bottom.top = False
layout.separator()
remove = layout.operator(
"wbs.remove", text="Unlink workspace from scene" if text else "", icon='X')
remove.index = i
def draw_workspace_menu(self, context):
layout = self.layout
use_global = get_use_global()
if not use_global and has_data():
layout.separator()
draw_workspace_ops(layout, get_scene_workspaces().index(
context.window.workspace.name), True)
layout.separator()

@ -2,17 +2,24 @@ import bpy
from bpy.types import Menu
from .funcs import get_scene_workspaces
class WBS_MT_missing_workspaces(Menu):
bl_label = "Link other workspaces"
class SW_MT_copy_from(Menu):
bl_label = "Copy and link workspaces from other scenes"
def draw(self, context):
layout = self.layout
present = False
for s in bpy.data.scenes:
if s.name != bpy.context.scene.name:
layout.operator("sw.copy_from_scene", text=s.name, icon="SCENE").scene = s.name
class SW_MT_missing_workspaces(Menu):
bl_label = "Link other workspaces to this scene"
def draw(self, context):
layout = self.layout
present = len(bpy.data.workspaces) > len(get_scene_workspaces())
if present:
layout.operator("sw.link_all", text="Link all workspaces")
layout.separator()
for w in bpy.data.workspaces:
if w.name not in get_scene_workspaces():
present = True
layout.operator("wbs.sel_workspace", text=w.name, icon="ADD").workspace = w.name
if present:
layout.separator()
layout.operator("wbs.sel_all", text="Link all workspaces")
layout.operator("sw.link_workspace", text=w.name, icon="ADD").workspace = w.name

@ -1,25 +1,23 @@
from .. import __package__ as base_package
import time
import bpy
from bpy.types import Operator
from bpy.props import StringProperty, BoolProperty, IntProperty
from .topbar import TOPBAR_HT_upper_bar
from .funcs import init_data, redraw_props, set_scene_workspaces, draw_workspace_ops, get_use_global, has_data
from .get_workspaces import get_scene_workspaces
import time
from .funcs import select_all, get_scene_workspaces, set_scene_workspaces, get_use_global, has_data
class WBS_OT_switch(Operator):
bl_idname = "wbs.switch"
bl_label = "Scene Workspaces Switch"
bl_description = "Switch between Scene Workspaces and global workspaces"
class SW_OT_switch(Operator):
bl_idname = "sw.switch"
bl_label = "Scene Workspaces On/Off"
bl_description = "Switch workspaces topbar between Scene Workspaces and default"
def execute(self, context):
context.preferences.addons[base_package].preferences.use_global = not get_use_global()
context.preferences.is_dirty = True
return {'FINISHED'}
class WBS_OT_workspace(Operator):
bl_idname = "wbs.workspace"
bl_label = "Select Workspace"
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")
@ -33,18 +31,19 @@ class WBS_OT_workspace(Operator):
set_scene_workspaces(l)
return {'FINISHED'}
class WBS_OT_sel_all(Operator):
bl_idname = "wbs.sel_all"
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):
init_data()
select_all()
return {'FINISHED'}
class WBS_OT_move(Operator):
bl_idname = "wbs.move"
class SW_OT_move(Operator):
bl_idname = "sw.move"
bl_label = "Reorder Workspace"
bl_description = "Re-order this workspace in the desired location (only for this Scene)"
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
index: IntProperty(name="index", default=-1)
@ -63,10 +62,10 @@ class WBS_OT_move(Operator):
self.top = False
return {'FINISHED'}
class WBS_OT_rename(Operator):
bl_idname = "wbs.rename"
class SW_OT_rename(Operator):
bl_idname = "sw.rename"
bl_label = "Rename workspace"
bl_description = "Rename the current workspace"
bl_description = "Rename the current workspace (this rename both the global workspace and the Scene workspace)"
current_name: StringProperty(name="Current Name", options={'HIDDEN'})
new_name: StringProperty(
@ -86,10 +85,10 @@ class WBS_OT_rename(Operator):
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self, title=f"Rename Workspace '{self.current_name}'")
class WBS_OT_remove(Operator):
bl_idname = "wbs.remove"
class SW_OT_remove(Operator):
bl_idname = "sw.remove"
bl_label = "Unlink Workspace"
bl_description = "Unlink this workspace from this Scene"
bl_description = "Unlink this workspace from this scene (this will not delete the workspace)"
index: IntProperty(name="index", default=-1)
@ -99,10 +98,22 @@ class WBS_OT_remove(Operator):
set_scene_workspaces(l)
return {'FINISHED'}
class WBS_OT_sel_workspace(Operator):
bl_idname = "wbs.sel_workspace"
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"
bl_description = "This is the active workspace, but it's not linked to the current Scene. Click to link"
bl_description = "Link this workspace to this Scene"
workspace: StringProperty(name="workspace")
@ -111,9 +122,6 @@ class WBS_OT_sel_workspace(Operator):
set_scene_workspaces([])
if self.workspace in get_scene_workspaces():
set_scene_workspaces([x for x in get_scene_workspaces() if x != self.workspace])
self.report({'INFO'}, f"{self.workspace} unchecked!")
else:
set_scene_workspaces([*get_scene_workspaces(), self.workspace])
self.report({'INFO'}, f"{self.workspace} checked!")
return {'FINISHED'}

@ -1,8 +1,9 @@
import bpy
from bpy.types import Panel
from .funcs import get_scene_workspaces, draw_workspace_ops
class WBS_PT_select_scene_workspaces(Panel):
bl_idname = "wbs.select_scene_workspaces"
class SW_PT_select_scene_workspaces(Panel):
bl_idname = "sw.select_scene_workspaces"
bl_label = "Scene Workspaces"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
@ -32,11 +33,16 @@ class WBS_PT_select_scene_workspaces(Panel):
layout.label(text="Link other workspaces")
l = [x for x in bpy.data.workspaces if x.name not in sw]
box2 = layout.box().split()
box2.label(text="All workspaces are currently linked.")
box2 = layout.box()
row = box2.row()
row_count = 0
for w in l:
row = box2.column()
row.operator("wbs.sel_workspace", text=w.name).workspace = w.name
if row_count > 0 and row_count % 4 == 0:
row = box2.row()
row.operator("sw.link_workspace", text=w.name).workspace = w.name
row_count += 1
if l:
layout.separator()
layout.operator("wbs.sel_all")
layout.operator("sw.link_all")
else:
box2.label(text="All workspaces are currently linked.")

@ -3,10 +3,10 @@ from bpy.types import AddonPreferences
from bpy.props import BoolProperty
from .. import __package__ as base_package
class WBSPreferences(AddonPreferences):
class SWPreferences(AddonPreferences):
bl_idname = base_package
use_global: BoolProperty(name="Use default workspaces", description="", default=False)
use_global: BoolProperty(name="Use default workspaces", description="Switch the workspace topbar between Scene Workspaces and the default one", default=False)
def draw(self, context):
layout = self.layout

@ -31,46 +31,70 @@ class TOPBAR_HT_upper_bar(Header):
use_global = get_use_global()
if not screen.show_fullscreen:
main_col = layout.column()
spacer_row = main_col.row()
spacer_row.scale_y = .33
main_row = main_col.row()
sy = 1.18
sx = 1.1
col = main_row.column()
col.scale_y = sy
col.operator("sw.switch", text="", emboss=False, icon="RADIOBUT_OFF" if use_global else "RADIOBUT_ON")
if use_global:
# Original
layout.template_ID_tabs(window, "workspace", new="workspace.add", menu="TOPBAR_MT_workspace_menu")
else:
r = layout.row(align=True)
r.scale_x = 0.89
r.scale_y = 1.24
r = main_row.row(align=True)
r.scale_x = 0.87
r.scale_y = sy
ws = get_scene_workspaces()
if not ws and bpy.data.workspaces:
c = r.column()
c.scale_x = 1.4
c.operator("sw.link_all", text="Link all workspaces", icon="LINKED")
active_not_here = True
for w in ws:
i = ws.index(w)
active = bpy.context.window.workspace.name == w
if active and i > 0:
r.separator()
ab = r.box()
ab = ab.row(align=True)
if active:
col = r.column()
col.scale_x = 1.5
col = ab.column()
col.scale_x = sx
col.operator("sw.remove", text="", icon="X", emboss=False).index = i
exist = w in [x.name for x in bpy.data.workspaces]
ab.operator("sw.workspace", text=w, icon="NONE" if exist else "ERROR", emboss=active, depress=active).workspace = w
if active:
col = ab.column()
col.scale_x = sx
col.menu("TOPBAR_MT_workspace_menu", text="", icon="OPTIONS")
active_not_here = False
box = r.box()
exist = w in [x.name for x in bpy.data.workspaces]
box.operator("wbs.workspace", text=w, icon="NONE" if exist else "ERROR", emboss=active, depress=active).workspace = w
if active_not_here:
r.separator()
col = r.column()
col.scale_x = 1.5
col.menu("TOPBAR_MT_workspace_menu", text="", icon="OPTIONS")
b = r.box()
b.scale_x = 1.5
b.operator("wbs.sel_workspace", text=bpy.context.window.workspace.name, icon="ADD", emboss=True, depress=True).workspace = bpy.context.window.workspace.name
r.separator()
r.separator()
if [x for x in bpy.data.workspaces if x.name not in get_scene_workspaces()]:
c = r.column()
c.scale_x = 1.3
c.menu("WBS_MT_missing_workspaces", text="", icon="WORKSPACE")
r.separator()
c.scale_x = sx
c.menu("SW_MT_missing_workspaces", text="", icon="THREE_DOTS")
c = r.column()
c.scale_x = 1.3
c.scale_x = sx
c.operator("workspace.add", text="", icon="ADD")
col = layout.column()
col.scale_y = 1.24
col.operator("wbs.switch", text="", emboss=False, icon="RADIOBUT_OFF" if use_global else "RADIOBUT_ON")
if len(bpy.data.scenes) > 1:
c = r.column()
c.scale_x = sx
c.menu("SW_MT_copy_from", text="", icon="DUPLICATE")
if active_not_here:
r.separator()
r = r.box()
r = r.row(align=True)
b = r.box()
b.scale_x = sx
b.operator("sw.link_workspace", text=f"{bpy.context.window.workspace.name} (not linked)", icon="ADD", emboss=True, depress=True).workspace = bpy.context.window.workspace.name
col = r.box()
col.scale_x = sx
col.menu("TOPBAR_MT_workspace_menu", text="", icon="OPTIONS")
# End Edited
else:
layout.operator("screen.back_to_previous", icon='SCREEN_BACK', text="Back to Previous")