Skip to content

Commit f3e188b

Browse files
Merge pull request #286 from specklesystems/bilal/cnx-2013-implement-workspace-dropdown
Bilal/cnx 2013 implement workspace dropdown
2 parents fcc8527 + 346b021 commit f3e188b

File tree

6 files changed

+140
-48
lines changed

6 files changed

+140
-48
lines changed

bpy_speckle/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@
8989
unregister as unregister_speckle_state,
9090
)
9191

92+
93+
from .connector.ui.workspace_selection_dialog import (
94+
SPECKLE_OT_workspace_selection_dialog,
95+
SPECKLE_UL_workspaces_list,
96+
)
97+
9298
# Utils
9399
from .connector.ui.account_selection_dialog import (
94100
SPECKLE_OT_account_selection_dialog,
@@ -104,7 +110,7 @@ def invoke_window_manager_properties():
104110
WindowManager.speckle_workspaces = bpy.props.CollectionProperty(
105111
type=speckle_workspace
106112
)
107-
WindowManager.selected_workspace_id = bpy.props.StringProperty()
113+
WindowManager.selected_workspace = bpy.props.PointerProperty(type=speckle_workspace)
108114
WindowManager.can_create_project_in_workspace = bpy.props.BoolProperty()
109115
# Projects
110116
WindowManager.speckle_projects = bpy.props.CollectionProperty(type=speckle_project)
@@ -163,6 +169,8 @@ def invoke_window_manager_properties():
163169
SPECKLE_OT_create_project,
164170
SPECKLE_OT_create_model,
165171
speckle_account,
172+
SPECKLE_UL_workspaces_list,
173+
SPECKLE_OT_workspace_selection_dialog,
166174
SPECKLE_OT_account_selection_dialog,
167175
SPECKLE_UL_accounts_list,
168176
)

bpy_speckle/connector/blender_operators/create_project.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ def execute(self, context: Context) -> set[str]:
2626
wm.selected_account_id,
2727
self.project_name,
2828
None
29-
if wm.selected_workspace_id == "personal"
30-
else wm.selected_workspace_id,
29+
if wm.selected_workspace.id == "personal"
30+
else wm.selected_workspace.id,
3131
)
3232
wm.selected_project_id = project_id
3333
wm.selected_project_name = project_name

bpy_speckle/connector/ui/account_selection_dialog.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
speckle_account,
77
speckle_workspace,
88
get_workspaces,
9-
get_default_workspace_id,
9+
get_active_workspace,
1010
get_account_from_id,
1111
)
1212
from ..utils.project_manager import get_projects_for_account
@@ -123,15 +123,15 @@ def update_workspaces_list(context: Context) -> None:
123123
workspace: speckle_workspace = wm.speckle_workspaces.add()
124124
workspace.id = id
125125
workspace.name = name
126-
wm.selected_workspace_id = get_default_workspace_id(wm.selected_account_id)
126+
wm.selected_workspace.id = get_active_workspace(wm.selected_account_id)["id"]
127127
print("Updated Workspaces List!")
128128

129129

130130
def update_projects_list(context: Context) -> None:
131131
wm = context.window_manager
132132
wm.speckle_projects.clear()
133133
projects: List[Tuple[str, str, str, str, bool]] = get_projects_for_account(
134-
wm.selected_account_id, workspace_id=wm.selected_workspace_id
134+
wm.selected_account_id, workspace_id=wm.selected_workspace.id
135135
)
136136
for name, role, updated, id, can_receive in projects:
137137
project: speckle_project = wm.speckle_projects.add()

bpy_speckle/connector/ui/project_selection_dialog.py

Lines changed: 12 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,14 @@
77
get_workspaces,
88
speckle_workspace,
99
can_create_project_in_workspace,
10+
get_active_workspace,
1011
get_default_account_id,
1112
get_account_from_id,
1213
)
1314
from ..utils.project_manager import get_projects_for_account
1415
from ..utils.property_groups import speckle_project
1516

1617

17-
def get_workspaces_callback(self, context):
18-
"""
19-
Callback to dynamically fetch workspace enum items.
20-
"""
21-
wm = context.window_manager
22-
return [
23-
(workspace.id, workspace.name, "", "WORKSPACE", i)
24-
for i, workspace in enumerate(wm.speckle_workspaces)
25-
]
26-
27-
2818
class SPECKLE_UL_projects_list(bpy.types.UIList):
2919
"""
3020
UIList for displaying a list of Speckle projects
@@ -74,16 +64,16 @@ def update_projects_list(self, context: Context) -> None:
7464
"""
7565
wm = context.window_manager
7666

77-
wm.selected_workspace_id = self.workspaces
67+
7868
wm.can_create_project_in_workspace = can_create_project_in_workspace(
79-
wm.selected_account_id, self.workspaces
69+
wm.selected_account_id, wm.selected_workspace.id
8070
)
8171
wm.speckle_projects.clear()
8272

8373
# get projects for the selected account, using search if provided
8474
search = self.search_query if self.search_query.strip() else None
8575
projects: List[Tuple[str, str, str, str, bool]] = get_projects_for_account(
86-
wm.selected_account_id, search=search, workspace_id=self.workspaces
76+
wm.selected_account_id, search=search, workspace_id=wm.selected_workspace.id
8777
)
8878

8979
for name, role, updated, id, can_receive in projects:
@@ -103,13 +93,6 @@ def update_projects_list(self, context: Context) -> None:
10393
update=update_projects_list,
10494
)
10595

106-
workspaces: bpy.props.EnumProperty( # type: ignore
107-
name="Workspace",
108-
description="Selected workspace to filter projects by",
109-
items=get_workspaces_callback,
110-
update=update_projects_list,
111-
)
112-
11396
project_index: bpy.props.IntProperty(name="Project Index", default=0) # type: ignore
11497

11598
def execute(self, context: Context) -> set[str]:
@@ -138,25 +121,16 @@ def invoke(self, context: Context, event: Event) -> set[str]:
138121

139122
# Clear existing projects
140123
wm.speckle_projects.clear()
141-
wm.speckle_workspaces.clear()
142124

143125
if wm.selected_account_id == "":
144126
wm.selected_account_id = get_default_account_id()
145127

146-
# Fetch workspaces from server
147-
for id, name in get_workspaces(wm.selected_account_id):
148-
workspace: speckle_workspace = wm.speckle_workspaces.add()
149-
workspace.id = id
150-
workspace.name = name
151-
selected_workspace_id = self.workspaces
152-
wm.selected_workspace_id = selected_workspace_id
153-
wm.can_create_project_in_workspace = can_create_project_in_workspace(
154-
wm.selected_account_id, selected_workspace_id
155-
)
128+
wm.selected_workspace.id = get_active_workspace(wm.selected_account_id)["id"]
129+
wm.selected_workspace.name = get_active_workspace(wm.selected_account_id)["name"]
156130

157131
# Fetch projects from server
158132
projects: List[Tuple[str, str, str, str, bool]] = get_projects_for_account(
159-
wm.selected_account_id, workspace_id=selected_workspace_id
133+
wm.selected_account_id, wm.selected_workspace.id
160134
)
161135

162136
for name, role, updated, id, can_receive in projects:
@@ -190,8 +164,11 @@ def draw(self, context: Context) -> None:
190164
)
191165
# Workspace selection
192166
row = layout.row()
193-
if wm.selected_workspace_id != "NO_WORKSPACES":
194-
row.prop(self, "workspaces", text="")
167+
row.operator(
168+
"speckle.workspace_selection_dialog",
169+
icon="WORKSPACE",
170+
text=wm.selected_workspace.name,
171+
)
195172

196173
# Search field
197174
row = layout.row(align=True)
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import bpy
2+
from bpy.types import Context, UILayout, Event, PropertyGroup
3+
from typing import List, Tuple
4+
from ..utils.account_manager import get_workspaces, speckle_workspace
5+
from ..utils.project_manager import get_projects_for_account
6+
from ..utils.account_manager import can_create_project_in_workspace
7+
8+
9+
class SPECKLE_UL_workspaces_list(bpy.types.UIList):
10+
"""
11+
UIList for workspaces
12+
"""
13+
14+
def draw_item(
15+
self,
16+
context: Context,
17+
layout: UILayout,
18+
data: PropertyGroup,
19+
item: PropertyGroup,
20+
icon: str,
21+
active_data: PropertyGroup,
22+
active_propname: str,
23+
) -> None:
24+
if self.layout_type in {"DEFAULT", "COMPACT"}:
25+
row = layout.row(align=True)
26+
row.label(text=item.name)
27+
28+
elif self.layout_type == "GRID":
29+
layout.alignment = "CENTER"
30+
layout.label(text=item.name)
31+
32+
33+
class SPECKLE_OT_workspace_selection_dialog(bpy.types.Operator):
34+
"""
35+
Operator for selecting a workspace
36+
"""
37+
38+
bl_idname = "speckle.workspace_selection_dialog"
39+
bl_label = "Select Workspace"
40+
bl_description = "Select a workspace to load projects from"
41+
42+
workspace_index: bpy.props.IntProperty(name="Workspace Index", default=0) # type: ignore
43+
44+
def invoke(self, context: Context, event: Event) -> set[str]:
45+
wm = context.window_manager
46+
wm.speckle_workspaces.clear()
47+
workspaces: List[Tuple[str, str]] = get_workspaces(wm.selected_account_id)
48+
current_workspace_index = 0
49+
for i, (id, name) in enumerate(workspaces):
50+
workspace: speckle_workspace = wm.speckle_workspaces.add()
51+
workspace.id = id
52+
workspace.name = name
53+
if id == wm.selected_workspace.id:
54+
current_workspace_index = i
55+
self.workspace_index = current_workspace_index
56+
return context.window_manager.invoke_props_dialog(self)
57+
58+
def draw(self, context: Context) -> None:
59+
layout: UILayout = self.layout
60+
wm = context.window_manager
61+
layout.label(text=f"Selected Workspace: {wm.selected_workspace.name}")
62+
layout.template_list(
63+
"SPECKLE_UL_workspaces_list",
64+
"",
65+
context.window_manager,
66+
"speckle_workspaces",
67+
self,
68+
"workspace_index",
69+
)
70+
71+
def execute(self, context: Context) -> set[str]:
72+
wm = context.window_manager
73+
if 0 <= self.workspace_index < len(wm.speckle_workspaces):
74+
selected_workspace = wm.speckle_workspaces[self.workspace_index]
75+
wm.selected_workspace.id = selected_workspace.id
76+
wm.selected_workspace.name = selected_workspace.name
77+
update_projects_list(context)
78+
context.area.tag_redraw()
79+
return {"FINISHED"}
80+
81+
82+
def update_projects_list(context):
83+
"""Update projects list when workspace changes"""
84+
85+
wm = context.window_manager
86+
wm.speckle_projects.clear()
87+
88+
# get projects for the selected account and workspace
89+
projects = get_projects_for_account(
90+
wm.selected_account_id, wm.selected_workspace.id
91+
)
92+
93+
for name, role, updated, id, can_receive in projects:
94+
project = wm.speckle_projects.add()
95+
project.name = name
96+
project.role = role
97+
project.updated = updated
98+
project.id = id
99+
project.can_receive = can_receive
100+
101+
# Update can_create_project_in_workspace flag
102+
wm.can_create_project_in_workspace = can_create_project_in_workspace(
103+
wm.selected_account_id, wm.selected_workspace.id
104+
)
105+
print(f"Workspace changed to: {wm.selected_workspace.id}")
106+
print("Projects list updated")
107+
108+
context.area.tag_redraw()

bpy_speckle/connector/utils/account_manager.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import bpy
22
from specklepy.core.api.credentials import get_local_accounts
3-
from typing import List, Tuple, Optional
3+
from typing import List, Tuple, Optional, Dict
44
from specklepy.core.api.credentials import Account
55
from specklepy.core.api.client import SpeckleClient
66
from specklepy.core.api.wrapper import StreamWrapper
@@ -99,18 +99,17 @@ def get_server_url_by_account_id(account_id: str) -> Optional[str]:
9999
return None
100100

101101

102-
def get_default_workspace_id(account_id: str) -> Optional[str]:
102+
def get_active_workspace(account_id: str) -> Optional[Dict[str, str]]:
103103
"""
104104
retrieves the ID of the default workspace for a given account ID
105105
"""
106106
account = next((acc for acc in get_local_accounts() if acc.id == account_id), None)
107107
client = SpeckleClient(host=account.serverInfo.url)
108108
client.authenticate_with_account(account)
109-
return (
110-
client.active_user.get_active_workspace().id
111-
if client.active_user.get_active_workspace()
112-
else "personal"
113-
)
109+
active_workspace = client.active_user.get_active_workspace()
110+
if active_workspace:
111+
return {"id": active_workspace.id, "name": active_workspace.name}
112+
return {"id": "personal", "name": "Personal Projects"}
114113

115114

116115
def get_account_from_id(account_id: str) -> Optional[Account]:

0 commit comments

Comments
 (0)