@webspatial/platform-visionos 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/Packages/RealityKitContent/.build/workspace-state.json +7 -0
  3. package/Packages/RealityKitContent/.swiftpm/xcode/xcuserdata/bytedance.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
  4. package/Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json +11 -0
  5. package/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/SceneMetadataList.json +112 -0
  6. package/Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/Settings.rcprojectdata +17 -0
  7. package/Packages/RealityKitContent/Package.swift +27 -0
  8. package/Packages/RealityKitContent/README.md +3 -0
  9. package/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Immersive.usda +50 -0
  10. package/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Materials/GridMaterial.usda +216 -0
  11. package/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Scene.usda +59 -0
  12. package/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift +4 -0
  13. package/package.json +27 -0
  14. package/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json +12 -0
  15. package/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json +6 -0
  16. package/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Contents.json +17 -0
  17. package/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json +12 -0
  18. package/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json +6 -0
  19. package/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json +12 -0
  20. package/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json +6 -0
  21. package/web-spatial/Assets.xcassets/Contents.json +6 -0
  22. package/web-spatial/Info.plist +33 -0
  23. package/web-spatial/Preview Content/Preview Assets.xcassets/Contents.json +6 -0
  24. package/web-spatial/libs/EventEmitter.swift +32 -0
  25. package/web-spatial/libs/SpatialComponent.swift +31 -0
  26. package/web-spatial/libs/SpatialEntity.swift +179 -0
  27. package/web-spatial/libs/SpatialInputComponent.swift +26 -0
  28. package/web-spatial/libs/SpatialMeshResource.swift +19 -0
  29. package/web-spatial/libs/SpatialModel3DComponent.swift +51 -0
  30. package/web-spatial/libs/SpatialModelComponent.swift +32 -0
  31. package/web-spatial/libs/SpatialObject.swift +144 -0
  32. package/web-spatial/libs/SpatialPhysicallyBasedMaterial.swift +19 -0
  33. package/web-spatial/libs/SpatialViewComponent.swift +15 -0
  34. package/web-spatial/libs/SpatialWindowComponent.swift +443 -0
  35. package/web-spatial/libs/SpatialWindowContainer.swift +149 -0
  36. package/web-spatial/libs/Utils/CommandManager.swift +800 -0
  37. package/web-spatial/libs/Utils/Logger.swift +36 -0
  38. package/web-spatial/libs/Utils/SceneManager.swift +108 -0
  39. package/web-spatial/libs/Utils/WindowContainerMgr.swift +117 -0
  40. package/web-spatial/libs/json/JsonParser.swift +52 -0
  41. package/web-spatial/libs/uiKitDelegate/Window.swift +34 -0
  42. package/web-spatial/libs/webView/UpdateSystem.swift +33 -0
  43. package/web-spatial/libs/webView/backend/NativeWebView.swift +319 -0
  44. package/web-spatial/libs/webView/manifest.swift +92 -0
  45. package/web-spatial/static-web/index.html +9 -0
  46. package/web-spatial/views/HideViewModifier.swift +17 -0
  47. package/web-spatial/views/ImmersiveView.swift +24 -0
  48. package/web-spatial/views/LoadingView.swift +29 -0
  49. package/web-spatial/views/MaterialWithBorderCornerModifier.swift +82 -0
  50. package/web-spatial/views/OpenDismissHandlerUI.swift +52 -0
  51. package/web-spatial/views/PlainWindowContainerView.swift +84 -0
  52. package/web-spatial/views/SpatialModel3DView.swift +193 -0
  53. package/web-spatial/views/SpatialViewUI.swift +168 -0
  54. package/web-spatial/views/SpatialWebViewUI.swift +193 -0
  55. package/web-spatial/views/VolumetricWindowContainerView.swift +38 -0
  56. package/web-spatial/views/ui/NavView.swift +125 -0
  57. package/web-spatial/web_spatialApp.swift +158 -0
  58. package/web-spatial.xcodeproj/project.pbxproj +686 -0
  59. package/web-spatial.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  60. package/web-spatial.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  61. package/web-spatial.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +5 -0
  62. package/web-spatial.xcodeproj/project.xcworkspace/xcuserdata/bytedance.xcuserdatad/WorkspaceSettings.xcsettings +14 -0
  63. package/web-spatial.xcodeproj/xcshareddata/xcschemes/web-spatial.xcscheme +115 -0
  64. package/web-spatialTests/web_spatialTests.swift +34 -0
@@ -0,0 +1,443 @@
1
+
2
+ //
3
+ // SpatialWindowComponent.swift
4
+ // web-spatial
5
+ //
6
+ // Created by ByteDance on 5/9/24.
7
+ //
8
+
9
+ import Combine
10
+ import Foundation
11
+ import RealityKit
12
+ import SwiftUI
13
+ import WebKit
14
+
15
+ let DefaultPlainWindowContainerSize = CGSize(width: 1280, height: 720)
16
+
17
+ func getDocumentsDirectory() -> URL {
18
+ let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
19
+ let documentsDirectory = paths[0]
20
+ return documentsDirectory
21
+ }
22
+
23
+ struct LoadingStyles {
24
+ var cornerRadius: CornerRadius = .init()
25
+ var windowContainerSize = DefaultPlainWindowContainerSize
26
+ var backgroundMaterial: BackgroundMaterial = .None
27
+ }
28
+
29
+ @Observable
30
+ class SpatialWindowComponent: SpatialComponent {
31
+ override func inspect() -> [String: Any] {
32
+ let childEntitiesInfo = childResources.mapValues { spatialObject in
33
+ spatialObject.inspect()
34
+ }
35
+
36
+ let encoder = JSONEncoder()
37
+ encoder.outputFormatting = .prettyPrinted
38
+
39
+ var inspectInfo: [String: Any] = [
40
+ "scrollWithParent": scrollWithParent,
41
+ "resolutionX": resolutionX,
42
+ "resolutionY": resolutionY,
43
+ "parentWebviewID": parentWebviewID,
44
+ "parentWindowContainerID": parentWindowContainerID,
45
+ "childWindowContainers": childWindowContainers,
46
+ "spawnedNativeWebviewsCount": spawnedNativeWebviews.count,
47
+ "childResources": childEntitiesInfo,
48
+ "cornerRadius": cornerRadius.toJson(),
49
+ "backgroundMaterial": backgroundMaterial.rawValue,
50
+ "isOpaque": webViewNative!.webViewHolder.appleWebView!.isOpaque,
51
+ "isScrollEnabled": isScrollEnabled(),
52
+ ]
53
+
54
+ let baseInspectInfo = super.inspect()
55
+ for (key, value) in baseInspectInfo {
56
+ inspectInfo[key] = value
57
+ }
58
+ return inspectInfo
59
+ }
60
+
61
+ var scrollOffset = CGPoint()
62
+ private var webViewNative: WebViewNative?
63
+ var resolutionX: Double = 0
64
+ var resolutionY: Double = 0
65
+ var scrollWithParent = false
66
+
67
+ var rotationAnchor: UnitPoint3D = .center
68
+
69
+ // Track the first load event of the webview so we don't see a flash of white before the page loads
70
+ var didFinishFirstLoad = false
71
+
72
+ // ID of the webview that created this or empty if its root
73
+ var parentWebviewID: String = ""
74
+ var parentWindowContainerID: String
75
+ var childWindowContainers = [String: WindowContainerData]()
76
+ var spawnedNativeWebviews = [String: WebViewNative]()
77
+
78
+ // Resources that will be destroyed when this webpage is destoryed or if it is navigated away from
79
+ private var childResources = [String: SpatialObject]()
80
+ public func addChildSpatialObject(_ spatialObject: SpatialObject) {
81
+ childResources[spatialObject.id] = spatialObject
82
+ spatialObject
83
+ .on(
84
+ event: SpatialObject.Events.BeforeDestroyed.rawValue,
85
+ listener: onSptatialObjectDestroyed
86
+ )
87
+ }
88
+
89
+ public func removeChildSpatialObject(_ spatialObject: SpatialObject) {
90
+ spatialObject
91
+ .off(
92
+ event: SpatialObject.Events.BeforeDestroyed.rawValue,
93
+ listener: onSptatialObjectDestroyed
94
+ )
95
+ childResources.removeValue(forKey: spatialObject.id)
96
+ }
97
+
98
+ public func getChildSpatialObject(name: String) -> SpatialObject? {
99
+ return childResources[name]
100
+ }
101
+
102
+ public func destroyChild(name: String) {
103
+ childResources[name]?.destroy()
104
+ }
105
+
106
+ private func onSptatialObjectDestroyed(_ object: Any, _ data: Any) {
107
+ let spatialObject = object as! SpatialObject
108
+ removeChildSpatialObject(spatialObject)
109
+ }
110
+
111
+ private func onWindowContainerDestroyed(_ object: Any, _ data: Any) {
112
+ if let spatialObject = object as? SpatialWindowContainer {
113
+ spatialObject
114
+ .off(
115
+ event: SpatialObject.Events.BeforeDestroyed.rawValue,
116
+ listener: onWindowContainerDestroyed
117
+ )
118
+ childWindowContainers.removeValue(forKey: spatialObject.id)
119
+ }
120
+ }
121
+
122
+ /// Determines whether the current webview is a root webview.
123
+ ///
124
+ /// A root webview is created when the Scene is initialized.
125
+ /// If the webview is created by another `SpatialWebview`, it is not considered a root webview.
126
+ /// For example, a `SpatialDiv` is not a root webview.
127
+ ///
128
+ /// - Returns: `true` if the webview is a root webview (i.e., `parentWebviewID` is empty), otherwise `false`.
129
+ public func isRootWebview() -> Bool {
130
+ return parentWebviewID == ""
131
+ }
132
+
133
+ public func setWindowContainer(uuid: String, wgd: WindowContainerData) {
134
+ childWindowContainers[uuid] = wgd
135
+ SpatialWindowContainer.getSpatialWindowContainer(uuid)!.on(
136
+ event: SpatialObject.Events.BeforeDestroyed.rawValue,
137
+ listener: onWindowContainerDestroyed
138
+ )
139
+ }
140
+
141
+ // Drag event handling
142
+ var dragStarted = false
143
+ var dragStart = 0.0
144
+ var dragVelocity = 0.0
145
+
146
+ var gotStyle = false
147
+ var opacity = 1.0
148
+ var cornerRadius: CornerRadius = .init()
149
+
150
+ private var _backgroundMaterial = BackgroundMaterial.None
151
+ var backgroundMaterial: BackgroundMaterial {
152
+ get {
153
+ return _backgroundMaterial
154
+ }
155
+ set(newValue) {
156
+ _backgroundMaterial = newValue
157
+ if isRootWebview() {
158
+ webViewNative?.webViewHolder.appleWebView?.isOpaque = _backgroundMaterial == .None
159
+ } else {
160
+ // it's spatial div
161
+ webViewNative?.webViewHolder.appleWebView?.isOpaque = false
162
+ }
163
+ }
164
+ }
165
+
166
+ var loadingStyles = LoadingStyles()
167
+ var isLoading = true
168
+
169
+ var didFailLoad = false
170
+
171
+ private var cancellables = Set<AnyCancellable>() // save subscriptions
172
+
173
+ init(parentWindowContainerID: String) {
174
+ self.parentWindowContainerID = parentWindowContainerID
175
+ super.init()
176
+ webViewNative = WebViewNative()
177
+ webViewNative?.webViewRef = self
178
+ _ = webViewNative?.createResources()
179
+ registerForceStyle()
180
+ }
181
+
182
+ init(parentWindowContainerID: String, url: URL) {
183
+ self.parentWindowContainerID = parentWindowContainerID
184
+ super.init()
185
+
186
+ webViewNative = WebViewNative(url: url)
187
+ webViewNative?.webViewRef = self
188
+ _ = webViewNative?.createResources()
189
+ registerForceStyle()
190
+ }
191
+
192
+ func initFromURL(url: URL) {
193
+ webViewNative = WebViewNative(url: url)
194
+ webViewNative?.webViewRef = self
195
+ _ = webViewNative?.createResources()
196
+ registerForceStyle()
197
+ }
198
+
199
+ // the url schema handler for forcestyle cannot bind seperately for every webview due to apple limitation. So this is a workaround like eventbus, webview will handle the message that has matched webview.
200
+ func registerForceStyle() {
201
+ webviewGetEarlyStyleData
202
+ .filter { [weak self] event in
203
+ self?.getView()?.webViewHolder.appleWebView == event.webview
204
+ }
205
+ .sink { [weak self] event in
206
+ self?.didGetEarlyStyle(style: event.style)
207
+ }
208
+ .store(in: &cancellables)
209
+ }
210
+
211
+ func goBack() {
212
+ webViewNative?.webViewHolder.appleWebView?.goBack()
213
+ }
214
+
215
+ func goForward() {
216
+ webViewNative?.webViewHolder.appleWebView?.goForward()
217
+ }
218
+
219
+ func reload() {
220
+ webViewNative?.webViewHolder.appleWebView?.reload()
221
+ }
222
+
223
+ var canGoBack: Bool = false
224
+
225
+ var canGoForward: Bool = false
226
+
227
+ func navigateToURL(url: URL) {
228
+ webViewNative!.url = url
229
+ webViewNative!.webViewHolder.needsUpdate = true
230
+ webViewNative!.initialLoad()
231
+ }
232
+
233
+ func isScrollEnabled() -> Bool {
234
+ return webViewNative!.webViewHolder.appleWebView!.scrollView.isScrollEnabled
235
+ }
236
+
237
+ /// Finds the nearest ancestor `SpatialWindowComponent` that has scrolling enabled.
238
+ ///
239
+ /// This method traverses the entity hierarchy upwards, starting from the current `SpatialWindowComponent`,
240
+ /// and checks each ancestor `SpatialWindowComponent` to see if scrolling is enabled. If a suitable
241
+ /// component is found, it is returned; otherwise, `nil` is returned.
242
+ ///
243
+ /// - Returns: The nearest ancestor `SpatialWindowComponent` with scrolling enabled, or `nil` if none is found.
244
+ func findNearestScrollEnabledSpatialWindowComponent() -> SpatialWindowComponent? {
245
+ var current: SpatialWindowComponent? = self
246
+ while current != nil {
247
+ if current!.isScrollEnabled() {
248
+ return current!
249
+ }
250
+ if let parentEntity = current?.entity?.parent {
251
+ current = parentEntity.getComponent(SpatialWindowComponent.self)
252
+ } else {
253
+ current = nil
254
+ }
255
+ }
256
+ return current
257
+ }
258
+
259
+ func updateScrollOffset(delta: CGFloat) {
260
+ webViewNative!.webViewHolder.appleWebView!.scrollView.contentOffset.y += delta
261
+ }
262
+
263
+ func stopScrolling() {
264
+ webViewNative!.webViewHolder.appleWebView!.scrollView.stopScrollingAndZooming()
265
+ }
266
+
267
+ func getView() -> WebViewNative? {
268
+ return webViewNative
269
+ }
270
+
271
+ func setView(wv: WebViewNative) {
272
+ webViewNative = wv
273
+ webViewNative!.webViewRef = self
274
+ }
275
+
276
+ func evaluateJS(js: String) {
277
+ webViewNative!.webViewHolder.appleWebView!.evaluateJavaScript(js)
278
+ }
279
+
280
+ func getURL() -> URL? {
281
+ return webViewNative?.url
282
+ }
283
+
284
+ func setURL(url: URL) {
285
+ webViewNative!.url = url
286
+ }
287
+
288
+ func parseURL(url: String) -> String {
289
+ // Compute target url depending if the url is relative or not
290
+ var targetUrl = url
291
+ if !pwaManager.isLocal {
292
+ if url[...url.index(url.startIndex, offsetBy: 0)] == "/" {
293
+ // Absolute path
294
+ var port = ""
295
+ if let p = webViewNative?.url.port {
296
+ port = ":" + String(p)
297
+ }
298
+ let domain = webViewNative!.url.scheme! + "://" + webViewNative!.url.host()! + port + "/"
299
+ targetUrl = domain + String(url[url.index(url.startIndex, offsetBy: 1)...])
300
+ } else {
301
+ // Full url eg. http://domain.com
302
+ if let parsed = URL(string: url) {
303
+ if parsed.scheme != nil {
304
+ return url
305
+ }
306
+ }
307
+ // Reletive path
308
+ let localDir = NSString(string: webViewNative!.url.absoluteString)
309
+ let relPath = String(localDir.deletingLastPathComponent) + "/" + targetUrl
310
+ return relPath
311
+ }
312
+ } else {
313
+ if !(targetUrl.starts(with: "http://") || targetUrl.starts(with: "https://")) {
314
+ targetUrl = pwaManager.getLocalResourceURL(url: targetUrl)
315
+ }
316
+ }
317
+ return targetUrl
318
+ }
319
+
320
+ func readWindowContainerID(id: String) -> String {
321
+ if id == "current" {
322
+ return parentWindowContainerID
323
+ } else {
324
+ return id
325
+ }
326
+ }
327
+
328
+ deinit {
329
+ webViewNative!.destroy()
330
+ cancellables.removeAll()
331
+ }
332
+
333
+ func completeEvent(requestID: Int, data: String = "{}") {
334
+ webViewNative?.webViewHolder.appleWebView?.evaluateJavaScript("window.__SpatialWebEvent({success: true, requestID:" + String(requestID) + ", data: " + data + "})")
335
+ }
336
+
337
+ func fireComponentEvent(componentId: String, data: String = "{}") {
338
+ webViewNative?.webViewHolder.appleWebView?.evaluateJavaScript("window.__SpatialWebEvent({resourceId:'" + componentId + "', data: " + data + "})")
339
+ }
340
+
341
+ func failEvent(requestID: Int, data: String = "{}") {
342
+ webViewNative?.webViewHolder.appleWebView?.evaluateJavaScript("window.__SpatialWebEvent({success: false, requestID:" + String(requestID) + ", data: " + data + "})")
343
+ }
344
+
345
+ // Request information of webview that request this webview to load
346
+ weak var loadRequestWV: SpatialWindowComponent?
347
+ var loadRequestID = -1
348
+
349
+ // A load request of a child webview was loaded
350
+ func didLoadChild(loadRequestID: Int, resourceID: String) {
351
+ completeEvent(requestID: loadRequestID, data: "{createdID: '" + id + "'}")
352
+ }
353
+
354
+ func didFailLoadPage() {
355
+ didFailLoad = true
356
+ didFinishFirstLoad = true
357
+ }
358
+
359
+ func releaseChildResources() {
360
+ let spatialObjects = childResources.map { $0.value }
361
+ for spatialObject in spatialObjects {
362
+ spatialObject.destroy()
363
+ }
364
+ childResources = [String: SpatialObject]()
365
+ spawnedNativeWebviews = [String: WebViewNative]()
366
+
367
+ let wgkeys = childWindowContainers.map { $0.key }
368
+ for k in wgkeys {
369
+ SpatialWindowContainer.getSpatialWindowContainer(k)!.closeWindowData.send(childWindowContainers[k]!)
370
+ }
371
+ }
372
+
373
+ func didStartLoadPage() {
374
+ if didFinishFirstLoad {
375
+ webViewNative!.webViewHolder.appleWebView!.evaluateJavaScript("window.__WebSpatialUnloaded = true")
376
+ }
377
+
378
+ releaseChildResources()
379
+ let url = webViewNative?.webViewHolder.appleWebView?.url
380
+ webViewNative!.url = url!
381
+
382
+ // Mark that we havn't gotten a style update
383
+ gotStyle = false
384
+ isLoading = true
385
+ loadingStyles = LoadingStyles()
386
+
387
+ // FIXME:
388
+ // This is a workaround to force run UIViewRepresentable.update()
389
+ // SwiftUI not trigger it when go back from example page.
390
+ // Warning of `AttributeGraph: cycle detected through attribute` fired when goes to example page
391
+ webViewNative?.initialLoad()
392
+ }
393
+
394
+ func didSpawnWebView(wv: WebViewNative) {
395
+ let uuid = UUID().uuidString
396
+ wv.webViewHolder.appleWebView!.evaluateJavaScript("window._webSpatialID = '" + uuid + "'")
397
+ spawnedNativeWebviews[uuid] = wv
398
+ }
399
+
400
+ func didCloseWebView() {
401
+ // if need
402
+ if isRootWebview() {
403
+ SceneManager.Instance.closeRoot(self)
404
+ }
405
+ }
406
+
407
+ func didStartReceivePageContent() {}
408
+
409
+ func didGetEarlyStyle(style: PreloadStyleSettings) {
410
+ if let cornerRadius = style.cornerRadius {
411
+ loadingStyles.cornerRadius = cornerRadius
412
+ }
413
+
414
+ if let backgroundMaterial = style.backgroundMaterial {
415
+ loadingStyles.backgroundMaterial = backgroundMaterial
416
+ }
417
+ }
418
+
419
+ func didFinishLoadPage() {
420
+ didFinishFirstLoad = true
421
+ didFailLoad = false
422
+ cornerRadius = loadingStyles.cornerRadius
423
+ backgroundMaterial = loadingStyles.backgroundMaterial
424
+
425
+ isLoading = false
426
+
427
+ // update navinfo
428
+ if let wv = webViewNative?.webViewHolder.appleWebView {
429
+ canGoBack = wv.canGoBack
430
+ canGoForward = wv.canGoForward
431
+ }
432
+ }
433
+
434
+ override func onDestroy() {
435
+ releaseChildResources()
436
+ didCloseWebView()
437
+ }
438
+
439
+ func didNavBackForward() {
440
+ // in JS calling history.go(-1) we should set needUpdate=true
441
+ webViewNative?.webViewHolder.needsUpdate = true
442
+ }
443
+ }
@@ -0,0 +1,149 @@
1
+ //
2
+ // SpatialWindowContainer.swift
3
+ // web-spatial
4
+ //
5
+ // Created by ByteDance on 9/10/24.
6
+ //
7
+
8
+ import Combine
9
+ import Foundation
10
+ import RealityKit
11
+ import typealias RealityKit.Entity
12
+
13
+ @Observable
14
+ class SpatialWindowContainer: SpatialObject {
15
+ // save active plain windowContainer ids
16
+ static var activePlainWindowContainerIds: Set<String> = []
17
+ // get first active plain WindowContainerId
18
+ static var firstActivePlainWindowContainerId: String? {
19
+ return activePlainWindowContainerIds.first
20
+ }
21
+
22
+ // Resources that will be destroyed when this window group is removed
23
+ private var childResources = [String: SpatialObject]()
24
+ public var childContainers = [String: SpatialWindowContainer]()
25
+
26
+ var wgd: WindowContainerData
27
+ static func getSpatialWindowContainer(_ name: String) -> SpatialWindowContainer? {
28
+ return SpatialObject.get(name) as? SpatialWindowContainer
29
+ }
30
+
31
+ static func getOrCreateSpatialWindowContainer(_ name: String, _ data: WindowContainerData) -> SpatialWindowContainer? {
32
+ if let windowContainer = getSpatialWindowContainer(name) {
33
+ return windowContainer
34
+ }
35
+ let newWindowContainer = SpatialWindowContainer(name, data)
36
+ return newWindowContainer
37
+ }
38
+
39
+ init(_ name: String, _ data: WindowContainerData) {
40
+ wgd = data
41
+ if data.windowStyle == "Plain" {
42
+ SpatialWindowContainer.activePlainWindowContainerIds.insert(data.windowContainerID)
43
+ }
44
+
45
+ super.init(name)
46
+ }
47
+
48
+ // Resources
49
+ private var childEntities = [String: SpatialEntity]()
50
+
51
+ public func getEntities() -> [String: SpatialEntity] {
52
+ return childEntities
53
+ }
54
+
55
+ public func addEntity(_ spatialEntity: SpatialEntity) {
56
+ childEntities[spatialEntity.id] = spatialEntity
57
+ }
58
+
59
+ public func removeEntity(_ spatialEntity: SpatialEntity) {
60
+ childEntities.removeValue(forKey: spatialEntity.id)
61
+ }
62
+
63
+ // Global state
64
+ var toggleImmersiveSpace = PassthroughSubject<Bool, Never>()
65
+
66
+ var setSize = PassthroughSubject<CGSize, Never>()
67
+
68
+ var updateFrame = false
69
+ var openWindowData = PassthroughSubject<WindowContainerData, Never>()
70
+ var closeWindowData = PassthroughSubject<WindowContainerData, Never>()
71
+
72
+ var setLoadingWindowData = PassthroughSubject<LoadingWindowContainerData, Never>()
73
+
74
+ override func onDestroy() {
75
+ // Note that destroy wont be called on immersive window container
76
+ // as there should only ever be one in existance
77
+
78
+ childEntities.forEach { $0.value.destroy() }
79
+ childEntities = [:]
80
+
81
+ // Destroy resources
82
+ let spatialObjects = childResources.map { $0.value }
83
+ for spatialObject in spatialObjects {
84
+ spatialObject.destroy()
85
+ }
86
+ childResources = [String: SpatialObject]()
87
+
88
+ // Destroy spatial containers
89
+ let spatialContainers = childContainers.map { $0.value }
90
+ for spatialObject in spatialContainers {
91
+ if spatialObject != self {
92
+ spatialObject.destroy()
93
+ }
94
+ }
95
+ childContainers = [String: SpatialWindowContainer]()
96
+
97
+ // Close the window group when this object is destroyed
98
+ SpatialWindowContainer.getSpatialWindowContainer(id)!.closeWindowData.send(wgd)
99
+ if wgd.windowStyle == "Plain" {
100
+ SpatialWindowContainer.activePlainWindowContainerIds.remove(wgd.windowContainerID)
101
+ }
102
+ }
103
+
104
+ override func inspect() -> [String: Any] {
105
+ let childEntitiesInfo = childEntities.mapValues { entity in
106
+ entity.inspect()
107
+ }
108
+
109
+ let baseInspectInfo = super.inspect()
110
+ var inspectInfo: [String: Any] = ["childEntities": childEntitiesInfo]
111
+ for (key, value) in baseInspectInfo {
112
+ inspectInfo[key] = value
113
+ }
114
+ return inspectInfo
115
+ }
116
+ }
117
+
118
+ extension SpatialWindowContainer {
119
+ private static let RootID = "root"
120
+ static func getRootID() -> String {
121
+ return RootID
122
+ }
123
+
124
+ static func createRootWindowContainer() -> SpatialWindowContainer {
125
+ if let rootWindowContainer = getSpatialWindowContainer(RootID) {
126
+ logger.warning("Root already created! ")
127
+ return rootWindowContainer
128
+ }
129
+ let wgd = WindowContainerData(windowStyle: "Plain", windowContainerID: "root")
130
+ return SpatialWindowContainer(RootID, wgd)
131
+ }
132
+ }
133
+
134
+ extension SpatialWindowContainer {
135
+ static let ImmersiveID = "Immersive"
136
+
137
+ static func getImmersiveWindowContainer() -> SpatialWindowContainer? {
138
+ return getSpatialWindowContainer(ImmersiveID)
139
+ }
140
+
141
+ static func createImmersiveWindowContainer() -> SpatialWindowContainer {
142
+ if let windowContainer = getSpatialWindowContainer(ImmersiveID) {
143
+ logger.warning("Immersive already created! ")
144
+ return windowContainer
145
+ }
146
+ let wgd = WindowContainerData(windowStyle: "ImmersiveSpace", windowContainerID: "ImmersiveSpace")
147
+ return SpatialWindowContainer(ImmersiveID, wgd)
148
+ }
149
+ }