@webspatial/platform-visionos 0.1.22 → 1.0.0
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.
- package/package.json +1 -1
- package/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json +1 -0
- package/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/icon-default.png +0 -0
- package/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json +1 -0
- package/web-spatial/Assets.xcassets/arrow_left.imageset/Contents.json +21 -0
- package/web-spatial/Assets.xcassets/arrow_left.imageset/arrow_left.png +0 -0
- package/web-spatial/Assets.xcassets/arrow_right.imageset/Contents.json +21 -0
- package/web-spatial/Assets.xcassets/arrow_right.imageset/arrow_right.png +0 -0
- package/web-spatial/Assets.xcassets/back.imageset/Contents.json +21 -0
- package/web-spatial/Assets.xcassets/back.imageset/back.png +0 -0
- package/web-spatial/Assets.xcassets/browser.imageset/Contents.json +21 -0
- package/web-spatial/Assets.xcassets/browser.imageset/browser.png +0 -0
- package/web-spatial/Assets.xcassets/close.imageset/Contents.json +21 -0
- package/web-spatial/Assets.xcassets/close.imageset/close.png +0 -0
- package/web-spatial/Assets.xcassets/copy.imageset/Contents.json +21 -0
- package/web-spatial/Assets.xcassets/copy.imageset/copy.png +0 -0
- package/web-spatial/Assets.xcassets/home.imageset/Contents.json +21 -0
- package/web-spatial/Assets.xcassets/home.imageset/home.png +0 -0
- package/web-spatial/Assets.xcassets/link.imageset/Contents.json +21 -0
- package/web-spatial/Assets.xcassets/link.imageset/link.png +0 -0
- package/web-spatial/Assets.xcassets/logo.imageset/Contents.json +1 -0
- package/web-spatial/Assets.xcassets/logo.imageset/icon-default.png +0 -0
- package/web-spatial/Assets.xcassets/more.imageset/Contents.json +21 -0
- package/web-spatial/Assets.xcassets/more.imageset/more.png +0 -0
- package/web-spatial/Assets.xcassets/nav.imageset/nav.png +0 -0
- package/web-spatial/Assets.xcassets/refresh.imageset/Contents.json +21 -0
- package/web-spatial/Assets.xcassets/refresh.imageset/refresh.png +0 -0
- package/web-spatial/libs/Utils/ColorExtension.swift +32 -0
- package/web-spatial/libs/Utils/CommandManager.swift +1 -0
- package/web-spatial/libs/Utils/WindowContainerMgr.swift +21 -3
- package/web-spatial/libs/webView/backend/NativeWebView.swift +33 -1
- package/web-spatial/libs/webView/manifest.swift +1 -1
- package/web-spatial/views/LoadingView.swift +0 -1
- package/web-spatial/views/PlainWindowContainerView.swift +50 -2
- package/web-spatial/views/SpatialWebViewUI.swift +124 -138
- package/web-spatial/views/ui/NavView.swift +119 -72
- package/web-spatial.xcodeproj/project.pbxproj +4 -4
- package/web-spatial/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/icon.png +0 -0
- package/web-spatial/nav.png +0 -0
package/package.json
CHANGED
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"images": [
|
|
3
|
+
{
|
|
4
|
+
"filename": "arrow_left.png",
|
|
5
|
+
"idiom": "universal",
|
|
6
|
+
"scale": "1x"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"idiom": "universal",
|
|
10
|
+
"scale": "2x"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"idiom": "universal",
|
|
14
|
+
"scale": "3x"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"info": {
|
|
18
|
+
"author": "xcode",
|
|
19
|
+
"version": 1
|
|
20
|
+
}
|
|
21
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"images": [
|
|
3
|
+
{
|
|
4
|
+
"filename": "arrow_right.png",
|
|
5
|
+
"idiom": "universal",
|
|
6
|
+
"scale": "1x"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"idiom": "universal",
|
|
10
|
+
"scale": "2x"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"idiom": "universal",
|
|
14
|
+
"scale": "3x"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"info": {
|
|
18
|
+
"author": "xcode",
|
|
19
|
+
"version": 1
|
|
20
|
+
}
|
|
21
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"images": [
|
|
3
|
+
{
|
|
4
|
+
"filename": "back.png",
|
|
5
|
+
"idiom": "universal",
|
|
6
|
+
"scale": "1x"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"idiom": "universal",
|
|
10
|
+
"scale": "2x"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"idiom": "universal",
|
|
14
|
+
"scale": "3x"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"info": {
|
|
18
|
+
"author": "xcode",
|
|
19
|
+
"version": 1
|
|
20
|
+
}
|
|
21
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"images": [
|
|
3
|
+
{
|
|
4
|
+
"filename": "browser.png",
|
|
5
|
+
"idiom": "universal",
|
|
6
|
+
"scale": "1x"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"idiom": "universal",
|
|
10
|
+
"scale": "2x"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"idiom": "universal",
|
|
14
|
+
"scale": "3x"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"info": {
|
|
18
|
+
"author": "xcode",
|
|
19
|
+
"version": 1
|
|
20
|
+
}
|
|
21
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"images": [
|
|
3
|
+
{
|
|
4
|
+
"filename": "close.png",
|
|
5
|
+
"idiom": "universal",
|
|
6
|
+
"scale": "1x"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"idiom": "universal",
|
|
10
|
+
"scale": "2x"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"idiom": "universal",
|
|
14
|
+
"scale": "3x"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"info": {
|
|
18
|
+
"author": "xcode",
|
|
19
|
+
"version": 1
|
|
20
|
+
}
|
|
21
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"images": [
|
|
3
|
+
{
|
|
4
|
+
"filename": "copy.png",
|
|
5
|
+
"idiom": "universal",
|
|
6
|
+
"scale": "1x"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"idiom": "universal",
|
|
10
|
+
"scale": "2x"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"idiom": "universal",
|
|
14
|
+
"scale": "3x"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"info": {
|
|
18
|
+
"author": "xcode",
|
|
19
|
+
"version": 1
|
|
20
|
+
}
|
|
21
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"images": [
|
|
3
|
+
{
|
|
4
|
+
"filename": "home.png",
|
|
5
|
+
"idiom": "universal",
|
|
6
|
+
"scale": "1x"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"idiom": "universal",
|
|
10
|
+
"scale": "2x"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"idiom": "universal",
|
|
14
|
+
"scale": "3x"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"info": {
|
|
18
|
+
"author": "xcode",
|
|
19
|
+
"version": 1
|
|
20
|
+
}
|
|
21
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"images": [
|
|
3
|
+
{
|
|
4
|
+
"filename": "link.png",
|
|
5
|
+
"idiom": "universal",
|
|
6
|
+
"scale": "1x"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"idiom": "universal",
|
|
10
|
+
"scale": "2x"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"idiom": "universal",
|
|
14
|
+
"scale": "3x"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"info": {
|
|
18
|
+
"author": "xcode",
|
|
19
|
+
"version": 1
|
|
20
|
+
}
|
|
21
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"images": [
|
|
3
|
+
{
|
|
4
|
+
"filename": "more.png",
|
|
5
|
+
"idiom": "universal",
|
|
6
|
+
"scale": "1x"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"idiom": "universal",
|
|
10
|
+
"scale": "2x"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"idiom": "universal",
|
|
14
|
+
"scale": "3x"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"info": {
|
|
18
|
+
"author": "xcode",
|
|
19
|
+
"version": 1
|
|
20
|
+
}
|
|
21
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"images": [
|
|
3
|
+
{
|
|
4
|
+
"filename": "refresh.png",
|
|
5
|
+
"idiom": "universal",
|
|
6
|
+
"scale": "1x"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"idiom": "universal",
|
|
10
|
+
"scale": "2x"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"idiom": "universal",
|
|
14
|
+
"scale": "3x"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"info": {
|
|
18
|
+
"author": "xcode",
|
|
19
|
+
"version": 1
|
|
20
|
+
}
|
|
21
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
|
|
3
|
+
extension Color {
|
|
4
|
+
init(hex: String) {
|
|
5
|
+
let hex = hex.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
6
|
+
.replacingOccurrences(of: "#", with: "")
|
|
7
|
+
|
|
8
|
+
guard hex.count == 6 || hex.count == 8 else {
|
|
9
|
+
self.init(.sRGB, red: 0, green: 0, blue: 0, opacity: 1)
|
|
10
|
+
return
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let scanner = Scanner(string: hex)
|
|
14
|
+
var hexNumber: UInt64 = 0
|
|
15
|
+
scanner.scanHexInt64(&hexNumber)
|
|
16
|
+
|
|
17
|
+
let r, g, b, a: Double
|
|
18
|
+
if hex.count == 8 {
|
|
19
|
+
r = Double((hexNumber & 0xFF00_0000) >> 24) / 255
|
|
20
|
+
g = Double((hexNumber & 0x00FF_0000) >> 16) / 255
|
|
21
|
+
b = Double((hexNumber & 0x0000_FF00) >> 8) / 255
|
|
22
|
+
a = Double(hexNumber & 0x0000_00FF) / 255
|
|
23
|
+
} else {
|
|
24
|
+
a = 1.0
|
|
25
|
+
r = Double((hexNumber & 0xFF0000) >> 16) / 255
|
|
26
|
+
g = Double((hexNumber & 0x00FF00) >> 8) / 255
|
|
27
|
+
b = Double(hexNumber & 0x0000FF) / 255
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
self.init(.sRGB, red: r, green: g, blue: b, opacity: a)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -617,6 +617,7 @@ class CommandManager {
|
|
|
617
617
|
// Update scene state
|
|
618
618
|
var cfg = WindowContainerPlainDefaultValues()
|
|
619
619
|
cfg.defaultSize = CGSize(width: resolution.width, height: resolution.height)
|
|
620
|
+
// TODO: need set resizeRange?
|
|
620
621
|
WindowContainerMgr.Instance.updateWindowContainerPlainDefaultValues(cfg)
|
|
621
622
|
return
|
|
622
623
|
}
|
|
@@ -16,6 +16,10 @@ struct WindowContainerData: Decodable, Hashable, Encodable {
|
|
|
16
16
|
let windowContainerID: String
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
struct WindowContainerResizability: Decodable, Encodable {
|
|
20
|
+
let resizeRange: ResizeRange?
|
|
21
|
+
}
|
|
22
|
+
|
|
19
23
|
enum LoadingMethod: String, Decodable, Encodable, Hashable {
|
|
20
24
|
case show
|
|
21
25
|
case hide
|
|
@@ -29,6 +33,7 @@ struct LoadingWindowContainerData: Decodable, Hashable, Encodable {
|
|
|
29
33
|
struct WindowContainerPlainDefaultValues {
|
|
30
34
|
var defaultSize: CGSize?
|
|
31
35
|
var windowResizability: WindowResizability?
|
|
36
|
+
var resizeRange: ResizeRange?
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
// support WindowContainerOptions => WindowContainerPlainDefaultValues
|
|
@@ -38,19 +43,28 @@ extension WindowContainerPlainDefaultValues {
|
|
|
38
43
|
width: options.defaultSize?.width ?? DefaultPlainWindowContainerSize.width,
|
|
39
44
|
height: options.defaultSize?.height ?? DefaultPlainWindowContainerSize.height
|
|
40
45
|
)
|
|
41
|
-
windowResizability = getWindowResizability(
|
|
46
|
+
windowResizability = getWindowResizability(nil)
|
|
47
|
+
resizeRange = options.resizability
|
|
42
48
|
}
|
|
43
49
|
}
|
|
44
50
|
|
|
51
|
+
struct ResizeRange: Codable {
|
|
52
|
+
var minWidth: Double?
|
|
53
|
+
var minHeight: Double?
|
|
54
|
+
var maxWidth: Double?
|
|
55
|
+
var maxHeight: Double?
|
|
56
|
+
}
|
|
57
|
+
|
|
45
58
|
// incomming JSB data
|
|
46
59
|
struct WindowContainerOptions: Codable {
|
|
47
60
|
// windowContainer
|
|
48
61
|
let defaultSize: Size?
|
|
49
|
-
let resizability: String?
|
|
50
62
|
struct Size: Codable {
|
|
51
63
|
var width: Double
|
|
52
64
|
var height: Double
|
|
53
65
|
}
|
|
66
|
+
|
|
67
|
+
let resizability: ResizeRange?
|
|
54
68
|
}
|
|
55
69
|
|
|
56
70
|
func getWindowResizability(_ windowResizability: String?) -> WindowResizability {
|
|
@@ -79,7 +93,8 @@ class WindowContainerMgr: ObservableObject {
|
|
|
79
93
|
|
|
80
94
|
private var wgSetting: WindowContainerPlainDefaultValues = .init(
|
|
81
95
|
defaultSize: CGSize(width: 1080, height: 720 + (pwaManager.display != .fullscreen ? NavView.navHeight : 0)),
|
|
82
|
-
windowResizability: .automatic
|
|
96
|
+
windowResizability: .automatic,
|
|
97
|
+
resizeRange: nil
|
|
83
98
|
)
|
|
84
99
|
|
|
85
100
|
func getValue() -> WindowContainerPlainDefaultValues {
|
|
@@ -100,5 +115,8 @@ class WindowContainerMgr: ObservableObject {
|
|
|
100
115
|
if let newResizability = data.windowResizability {
|
|
101
116
|
wgSetting.windowResizability = newResizability
|
|
102
117
|
}
|
|
118
|
+
if let newResizeRange = data.resizeRange {
|
|
119
|
+
wgSetting.resizeRange = newResizeRange
|
|
120
|
+
}
|
|
103
121
|
}
|
|
104
122
|
}
|
|
@@ -125,7 +125,6 @@ class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler, WKUID
|
|
|
125
125
|
// backward/forward
|
|
126
126
|
webViewRef?.didNavBackForward()
|
|
127
127
|
}
|
|
128
|
-
webViewRef?.navInfo.url = resource
|
|
129
128
|
decisionHandler(.allow)
|
|
130
129
|
} else {
|
|
131
130
|
decisionHandler(.cancel)
|
|
@@ -211,6 +210,34 @@ class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler, WKUID
|
|
|
211
210
|
wg.updateFrame = !(wg.updateFrame)
|
|
212
211
|
}
|
|
213
212
|
}
|
|
213
|
+
|
|
214
|
+
private var isObserving = false
|
|
215
|
+
func startObserving(webView: WKWebView) {
|
|
216
|
+
guard !isObserving else { return }
|
|
217
|
+
webView.addObserver(self, forKeyPath: #keyPath(WKWebView.url), options: .new, context: nil)
|
|
218
|
+
isObserving = true
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
func stopObserving(webView: WKWebView) {
|
|
222
|
+
guard isObserving else { return }
|
|
223
|
+
webView.removeObserver(self, forKeyPath: #keyPath(WKWebView.url))
|
|
224
|
+
isObserving = false
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
override func observeValue(
|
|
228
|
+
forKeyPath keyPath: String?,
|
|
229
|
+
of object: Any?,
|
|
230
|
+
change: [NSKeyValueChangeKey: Any]?,
|
|
231
|
+
context: UnsafeMutableRawPointer?
|
|
232
|
+
) {
|
|
233
|
+
if keyPath == #keyPath(WKWebView.url),
|
|
234
|
+
let url = (object as? WKWebView)?.url?.absoluteString
|
|
235
|
+
{
|
|
236
|
+
DispatchQueue.main.async {
|
|
237
|
+
self.webViewRef?.navInfo.url = url
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
214
241
|
}
|
|
215
242
|
|
|
216
243
|
struct WebViewNative: UIViewRepresentable {
|
|
@@ -250,6 +277,7 @@ struct WebViewNative: UIViewRepresentable {
|
|
|
250
277
|
myConfig.setURLSchemeHandler(webViewHolder.webViewCoordinator, forURLScheme: "file")
|
|
251
278
|
}
|
|
252
279
|
webViewHolder.appleWebView = WKWebView(frame: .zero, configuration: myConfig)
|
|
280
|
+
webViewHolder.webViewCoordinator!.startObserving(webView: webViewHolder.appleWebView!)
|
|
253
281
|
let configUA = myConfig.applicationNameForUserAgent as? String ?? ""
|
|
254
282
|
|
|
255
283
|
// change webview ua
|
|
@@ -284,6 +312,10 @@ struct WebViewNative: UIViewRepresentable {
|
|
|
284
312
|
func updateUIView(_ webView: WKWebView, context: Context) {
|
|
285
313
|
initialLoad()
|
|
286
314
|
}
|
|
315
|
+
|
|
316
|
+
static func dismantleUIView(_ uiView: WKWebView, coordinator: Coordinator) {
|
|
317
|
+
coordinator.stopObserving(webView: uiView)
|
|
318
|
+
}
|
|
287
319
|
}
|
|
288
320
|
|
|
289
321
|
// extend webview to support file://
|
|
@@ -9,7 +9,38 @@ struct PlainWindowContainerView: View {
|
|
|
9
9
|
@State private var timer: Timer?
|
|
10
10
|
|
|
11
11
|
private func setSize(size: CGSize) {
|
|
12
|
-
sceneDelegate.window?.windowScene
|
|
12
|
+
sceneDelegate.window?.windowScene?
|
|
13
|
+
.requestGeometryUpdate(
|
|
14
|
+
.Vision(
|
|
15
|
+
size: size
|
|
16
|
+
)
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private func setResizibility(resizingRestrictions: UIWindowScene.ResizingRestrictions) {
|
|
21
|
+
sceneDelegate.window?.windowScene?
|
|
22
|
+
.requestGeometryUpdate(
|
|
23
|
+
.Vision(
|
|
24
|
+
resizingRestrictions: resizingRestrictions
|
|
25
|
+
)
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private func setResizeRange(resizeRange: ResizeRange) {
|
|
30
|
+
sceneDelegate.window?.windowScene?
|
|
31
|
+
.requestGeometryUpdate(
|
|
32
|
+
.Vision(
|
|
33
|
+
minimumSize: CGSize(
|
|
34
|
+
width: resizeRange.minWidth ?? 0,
|
|
35
|
+
height: resizeRange
|
|
36
|
+
.minHeight ?? 0
|
|
37
|
+
),
|
|
38
|
+
maximumSize: CGSize(
|
|
39
|
+
width: resizeRange.maxWidth ?? .infinity,
|
|
40
|
+
height: resizeRange.maxHeight ?? .infinity
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
)
|
|
13
44
|
}
|
|
14
45
|
|
|
15
46
|
var body: some View {
|
|
@@ -39,7 +70,6 @@ struct PlainWindowContainerView: View {
|
|
|
39
70
|
// Avoid showing webview until its loading completes
|
|
40
71
|
let wc = e.getComponent(SpatialWindowComponent.self)
|
|
41
72
|
let didFinishFirstLoad = wc != nil ? wc!.didFinishFirstLoad : false
|
|
42
|
-
|
|
43
73
|
SpatialWebViewUI().environment(e)
|
|
44
74
|
.frame(width: width, height: height).padding3D(.front, -100_000)
|
|
45
75
|
.rotation3DEffect(Rotation3D(simd_quatf(ix: e.modelEntity.orientation.vector.x, iy: e.modelEntity.orientation.vector.y, iz: e.modelEntity.orientation.vector.z, r: e.modelEntity.orientation.vector.w)))
|
|
@@ -47,12 +77,30 @@ struct PlainWindowContainerView: View {
|
|
|
47
77
|
.offset(z: z)
|
|
48
78
|
.opacity(didFinishFirstLoad ? 1.0 : 0.0)
|
|
49
79
|
.animation(.linear(duration: 0.2), value: didFinishFirstLoad)
|
|
80
|
+
.ornament(attachmentAnchor: .scene(.top), contentAlignment: .center) {
|
|
81
|
+
if pwaManager.display != .fullscreen {
|
|
82
|
+
ZStack {
|
|
83
|
+
NavView(swc: wc, navInfo: wc!.navInfo).offset(y: -15)
|
|
84
|
+
}.frame(height: 100)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
50
87
|
}
|
|
51
88
|
}
|
|
52
89
|
}
|
|
53
90
|
.onReceive(windowContainerContent.setSize) { newSize in
|
|
54
91
|
setSize(size: newSize)
|
|
55
92
|
}
|
|
93
|
+
.onAppear {
|
|
94
|
+
let wd = WindowContainerMgr.Instance.getValue()
|
|
95
|
+
if let range = wd.resizeRange {
|
|
96
|
+
self.setResizeRange(resizeRange: range)
|
|
97
|
+
if (range.minWidth != nil || range.minWidth != nil) && range.minWidth == range.maxWidth && range.minHeight == range.maxHeight {
|
|
98
|
+
self.setResizibility(resizingRestrictions: .none)
|
|
99
|
+
} else {
|
|
100
|
+
self.setResizibility(resizingRestrictions: .freeform)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
56
104
|
.onChange(of: proxy3D.size) {
|
|
57
105
|
// WkWebview has an issue where it doesn't resize while the swift window is resized
|
|
58
106
|
// Treid to call didMoveToWindow to force redraw to occur but that seemed to cause rendering artifacts so that solution was rejected
|
|
@@ -28,115 +28,33 @@ struct SpatialWebViewUI: View {
|
|
|
28
28
|
let parentYOffset = Float(wv.scrollOffset.y)
|
|
29
29
|
|
|
30
30
|
let childEntities = ent.getEntities()
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
ZStack {
|
|
40
|
-
ForEach(Array(childEntities.keys), id: \.self) { key in
|
|
41
|
-
if let e = childEntities[key] {
|
|
42
|
-
let _ = e.forceUpdate ? 0 : 0
|
|
43
|
-
if let childWindowcomponent = e.getComponent(SpatialWindowComponent.self) {
|
|
44
|
-
if e.coordinateSpace == .DOM {
|
|
45
|
-
let view = childWindowcomponent
|
|
46
|
-
let x = CGFloat(e.modelEntity.position.x)
|
|
47
|
-
let y = CGFloat(e.modelEntity.position.y - (view.scrollWithParent ? parentYOffset : 0))
|
|
48
|
-
let z = CGFloat(e.modelEntity.position.z) + (e.zIndex * zOrderBias)
|
|
49
|
-
let width = CGFloat(view.resolutionX)
|
|
50
|
-
let height = CGFloat(view.resolutionY)
|
|
51
|
-
let anchor = view.rotationAnchor
|
|
52
|
-
|
|
53
|
-
// Matrix = MTranslate X MRotate X MScale
|
|
54
|
-
SpatialWebViewUI().environment(e)
|
|
55
|
-
.frame(width: width, height: height)
|
|
56
|
-
// use .offset(smallVal) to workaround for glassEffect not working and small width/height spatialDiv not working
|
|
57
|
-
.offset(z: 0.0001)
|
|
58
|
-
.scaleEffect(
|
|
59
|
-
x: CGFloat(e.modelEntity.scale.x),
|
|
60
|
-
y: CGFloat(e.modelEntity.scale.y),
|
|
61
|
-
z: CGFloat(e.modelEntity.scale.z),
|
|
62
|
-
anchor: anchor
|
|
63
|
-
)
|
|
64
|
-
.rotation3DEffect(
|
|
65
|
-
Rotation3D(simd_quatf(
|
|
66
|
-
ix: e.modelEntity.orientation.vector.x,
|
|
67
|
-
iy: e.modelEntity.orientation.vector.y,
|
|
68
|
-
iz: e.modelEntity.orientation.vector.z,
|
|
69
|
-
r: e.modelEntity.orientation.vector.w
|
|
70
|
-
)),
|
|
71
|
-
anchor: anchor
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
.position(x: x, y: y)
|
|
75
|
-
.offset(z: z)
|
|
76
|
-
.gesture(
|
|
77
|
-
DragGesture()
|
|
78
|
-
.onChanged { gesture in
|
|
79
|
-
let scrollEnabled = view.isScrollEnabled()
|
|
80
|
-
if !scrollEnabled {
|
|
81
|
-
// Check if there is a nearest scroll-enabled SpatialWindowComponent in the current SpatialEntity
|
|
82
|
-
// and scroll it if it exists
|
|
83
|
-
if let targetScrollWV = wv.findNearestScrollEnabledSpatialWindowComponent() {
|
|
84
|
-
if !view.dragStarted {
|
|
85
|
-
view.dragStarted = true
|
|
86
|
-
view.dragStart = (gesture.translation.height)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// TODO: this should have velocity
|
|
90
|
-
let delta = view.dragStart - gesture.translation.height
|
|
91
|
-
view.dragStart = gesture.translation.height
|
|
92
|
-
targetScrollWV.updateScrollOffset(delta: delta)
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
.onEnded { _ in
|
|
97
|
-
let scrollEnabled = view.isScrollEnabled()
|
|
98
|
-
|
|
99
|
-
if !scrollEnabled {
|
|
100
|
-
if let targetScrollWV = wv.findNearestScrollEnabledSpatialWindowComponent() {
|
|
101
|
-
view.dragStarted = false
|
|
102
|
-
view.dragStart = 0
|
|
103
|
-
|
|
104
|
-
targetScrollWV.stopScrolling()
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
)
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Model3D content
|
|
115
|
-
ForEach(Array(childEntities.keys), id: \.self) { key in
|
|
116
|
-
if let e = childEntities[key] {
|
|
117
|
-
let _ = e.forceUpdate ? 0 : 0
|
|
118
|
-
SpatialModel3DView(parentYOffset: parentYOffset)
|
|
119
|
-
.environment(e)
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// SpatialView content
|
|
124
|
-
ForEach(Array(childEntities.keys), id: \.self) { key in
|
|
125
|
-
if let e = childEntities[key] {
|
|
31
|
+
// Display child entities of the webview
|
|
32
|
+
ZStack {
|
|
33
|
+
OptionalClip(clipEnabled: ent.coordinateSpace != .ROOT && wv.isScrollEnabled()) {
|
|
34
|
+
ZStack {
|
|
35
|
+
ForEach(Array(childEntities.keys), id: \.self) { key in
|
|
36
|
+
if let e = childEntities[key] {
|
|
37
|
+
let _ = e.forceUpdate ? 0 : 0
|
|
38
|
+
if let childWindowcomponent = e.getComponent(SpatialWindowComponent.self) {
|
|
126
39
|
if e.coordinateSpace == .DOM {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
40
|
+
let view = childWindowcomponent
|
|
41
|
+
let x = CGFloat(e.modelEntity.position.x)
|
|
42
|
+
let y = CGFloat(e.modelEntity.position.y - (view.scrollWithParent ? parentYOffset : 0))
|
|
43
|
+
let z = CGFloat(e.modelEntity.position.z) + (e.zIndex * zOrderBias)
|
|
44
|
+
let width = CGFloat(view.resolutionX)
|
|
45
|
+
let height = CGFloat(view.resolutionY)
|
|
46
|
+
let anchor = view.rotationAnchor
|
|
47
|
+
|
|
48
|
+
// Matrix = MTranslate X MRotate X MScale
|
|
49
|
+
SpatialWebViewUI().environment(e)
|
|
50
|
+
.frame(width: width, height: height)
|
|
51
|
+
// use .offset(smallVal) to workaround for glassEffect not working and small width/height spatialDiv not working
|
|
52
|
+
.offset(z: 0.0001)
|
|
53
|
+
.scaleEffect(
|
|
137
54
|
x: CGFloat(e.modelEntity.scale.x),
|
|
138
55
|
y: CGFloat(e.modelEntity.scale.y),
|
|
139
|
-
z: CGFloat(e.modelEntity.scale.z)
|
|
56
|
+
z: CGFloat(e.modelEntity.scale.z),
|
|
57
|
+
anchor: anchor
|
|
140
58
|
)
|
|
141
59
|
.rotation3DEffect(
|
|
142
60
|
Rotation3D(simd_quatf(
|
|
@@ -144,50 +62,118 @@ struct SpatialWebViewUI: View {
|
|
|
144
62
|
iy: e.modelEntity.orientation.vector.y,
|
|
145
63
|
iz: e.modelEntity.orientation.vector.z,
|
|
146
64
|
r: e.modelEntity.orientation.vector.w
|
|
147
|
-
))
|
|
148
|
-
|
|
65
|
+
)),
|
|
66
|
+
anchor: anchor
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
.position(x: x, y: y)
|
|
149
70
|
.offset(z: z)
|
|
150
|
-
|
|
71
|
+
.gesture(
|
|
72
|
+
DragGesture()
|
|
73
|
+
.onChanged { gesture in
|
|
74
|
+
let scrollEnabled = view.isScrollEnabled()
|
|
75
|
+
if !scrollEnabled {
|
|
76
|
+
// Check if there is a nearest scroll-enabled SpatialWindowComponent in the current SpatialEntity
|
|
77
|
+
// and scroll it if it exists
|
|
78
|
+
if let targetScrollWV = wv.findNearestScrollEnabledSpatialWindowComponent() {
|
|
79
|
+
if !view.dragStarted {
|
|
80
|
+
view.dragStarted = true
|
|
81
|
+
view.dragStart = (gesture.translation.height)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// TODO: this should have velocity
|
|
85
|
+
let delta = view.dragStart - gesture.translation.height
|
|
86
|
+
view.dragStart = gesture.translation.height
|
|
87
|
+
targetScrollWV.updateScrollOffset(delta: delta)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
.onEnded { _ in
|
|
92
|
+
let scrollEnabled = view.isScrollEnabled()
|
|
93
|
+
if !scrollEnabled {
|
|
94
|
+
if let targetScrollWV = wv.findNearestScrollEnabledSpatialWindowComponent() {
|
|
95
|
+
view.dragStarted = false
|
|
96
|
+
view.dragStart = 0
|
|
97
|
+
targetScrollWV.stopScrolling()
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
)
|
|
151
102
|
}
|
|
152
103
|
}
|
|
153
104
|
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
105
|
+
}
|
|
156
106
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if let url = wv.getURL() {
|
|
164
|
-
wv.navigateToURL(url: url)
|
|
165
|
-
} else {
|
|
166
|
-
logger.warning("Unable to reload URL")
|
|
167
|
-
}
|
|
107
|
+
// Model3D content
|
|
108
|
+
ForEach(Array(childEntities.keys), id: \.self) { key in
|
|
109
|
+
if let e = childEntities[key] {
|
|
110
|
+
let _ = e.forceUpdate ? 0 : 0
|
|
111
|
+
SpatialModel3DView(parentYOffset: parentYOffset)
|
|
112
|
+
.environment(e)
|
|
168
113
|
}
|
|
169
|
-
.foregroundColor(.white)
|
|
170
114
|
}
|
|
171
|
-
.frame(maxWidth: .infinity, maxHeight: .infinity).glassBackgroundEffect()
|
|
172
115
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
116
|
+
// SpatialView content
|
|
117
|
+
ForEach(Array(childEntities.keys), id: \.self) { key in
|
|
118
|
+
if let e = childEntities[key] {
|
|
119
|
+
if e.coordinateSpace == .DOM {
|
|
120
|
+
if let viewComponent = e.getComponent(SpatialViewComponent.self) {
|
|
121
|
+
let _ = e.forceUpdate ? 0 : 0
|
|
122
|
+
let x = CGFloat(e.modelEntity.position.x)
|
|
123
|
+
let y = CGFloat(e.modelEntity.position.y - parentYOffset)
|
|
124
|
+
let z = CGFloat(e.modelEntity.position.z)
|
|
125
|
+
|
|
126
|
+
let width = CGFloat(viewComponent.resolutionX)
|
|
127
|
+
let height = CGFloat(viewComponent.resolutionY)
|
|
128
|
+
|
|
129
|
+
SpatialViewUI().environment(e).frame(width: width, height: height).scaleEffect(
|
|
130
|
+
x: CGFloat(e.modelEntity.scale.x),
|
|
131
|
+
y: CGFloat(e.modelEntity.scale.y),
|
|
132
|
+
z: CGFloat(e.modelEntity.scale.z)
|
|
133
|
+
)
|
|
134
|
+
.rotation3DEffect(
|
|
135
|
+
Rotation3D(simd_quatf(
|
|
136
|
+
ix: e.modelEntity.orientation.vector.x,
|
|
137
|
+
iy: e.modelEntity.orientation.vector.y,
|
|
138
|
+
iz: e.modelEntity.orientation.vector.z,
|
|
139
|
+
r: e.modelEntity.orientation.vector.w
|
|
140
|
+
))
|
|
141
|
+
).position(x: x, y: y)
|
|
142
|
+
.offset(z: z)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}.frame(maxWidth: .infinity, maxHeight: .infinity).frame(maxDepth: 0, alignment: .back).offset(z: 0)
|
|
148
|
+
}
|
|
179
149
|
|
|
180
|
-
|
|
150
|
+
// Display the main webview
|
|
151
|
+
if wv.didFailLoad {
|
|
152
|
+
VStack {
|
|
153
|
+
Text("Failed to load webpage. Is the server running?")
|
|
154
|
+
.foregroundColor(.white)
|
|
155
|
+
Button("Reload") {
|
|
156
|
+
if let url = wv.getURL() {
|
|
157
|
+
wv.navigateToURL(url: url)
|
|
158
|
+
} else {
|
|
159
|
+
logger.warning("Unable to reload URL")
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
.foregroundColor(.white)
|
|
181
163
|
}
|
|
164
|
+
.frame(maxWidth: .infinity, maxHeight: .infinity).glassBackgroundEffect()
|
|
165
|
+
|
|
166
|
+
} else {
|
|
167
|
+
wv.getView()
|
|
168
|
+
.materialWithBorderCorner(
|
|
169
|
+
wv.backgroundMaterial,
|
|
170
|
+
wv.cornerRadius
|
|
171
|
+
)
|
|
172
|
+
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
182
173
|
}
|
|
183
|
-
.opacity(wv.opacity)
|
|
184
|
-
.hidden(!ent.visible)
|
|
185
|
-
// .ornament(attachmentAnchor: .scene(.top), contentAlignment: .top) {
|
|
186
|
-
// if wv.isRootWebview() && pwaManager.display != .fullscreen {
|
|
187
|
-
// NavView(swc: wv, navInfo: wv.navInfo)
|
|
188
|
-
// }
|
|
189
|
-
// }
|
|
190
174
|
}
|
|
175
|
+
.opacity(wv.opacity)
|
|
176
|
+
.hidden(!ent.visible)
|
|
191
177
|
}
|
|
192
178
|
}
|
|
193
179
|
}
|
|
@@ -2,16 +2,20 @@ import SwiftUI
|
|
|
2
2
|
import WebKit
|
|
3
3
|
|
|
4
4
|
struct NavView: View {
|
|
5
|
-
static let navHeight: CGFloat =
|
|
5
|
+
static let navHeight: CGFloat = 60
|
|
6
|
+
static let minWidth: CGFloat = 400
|
|
6
7
|
@State var swc: SpatialWindowComponent?
|
|
7
8
|
@StateObject var navInfo: NavInfo
|
|
8
|
-
@State var
|
|
9
|
-
@State private var showNav: Bool = false
|
|
9
|
+
@State var navWidth: CGFloat = 0
|
|
10
10
|
@State private var showCopyTip = false
|
|
11
|
-
@State private var
|
|
12
|
-
@State private var contentHeight: CGFloat = 68
|
|
11
|
+
@State private var contentHeight: CGFloat = 60
|
|
13
12
|
@State private var texWidth: CGFloat = 0
|
|
14
13
|
@State private var firstGetSize: Bool = true
|
|
14
|
+
@State private var timer: Timer?
|
|
15
|
+
@State private var timeRemaining = 5
|
|
16
|
+
@State private var showEnter: Double = 1
|
|
17
|
+
@State private var showNav: Double = 0
|
|
18
|
+
@State private var showUrl: Double = 0
|
|
15
19
|
@Namespace var hoverNamespace
|
|
16
20
|
var navHoverGroup: HoverEffectGroup {
|
|
17
21
|
HoverEffectGroup(hoverNamespace)
|
|
@@ -20,83 +24,108 @@ struct NavView: View {
|
|
|
20
24
|
var body: some View {
|
|
21
25
|
ZStack {
|
|
22
26
|
ZStack {
|
|
23
|
-
HStack {
|
|
24
|
-
Image("logo").resizable().frame(width:
|
|
27
|
+
HStack(spacing: 10) {
|
|
28
|
+
Image("logo").resizable().frame(width: 32, height: 32)
|
|
25
29
|
.cornerRadius(100)
|
|
26
|
-
|
|
30
|
+
if pwaManager.display == .minimal {
|
|
31
|
+
NavButton(action: { swc?.goBack() }, children: Image("arrow_left"), clearBackGround: true).disabled(!(swc?.canGoBack ?? false))
|
|
32
|
+
NavButton(action: { swc?.goForward() }, children: Image("arrow_right"), clearBackGround: true).disabled(!(swc?.canGoBack ?? false))
|
|
33
|
+
NavButton(action: { swc?.reload() }, children: Image("refresh"), clearBackGround: true)
|
|
34
|
+
NavDivider()
|
|
35
|
+
}
|
|
36
|
+
NavButton(action: { print("click"); withAnimation(.easeInOut(duration: 0.5)) { showNav = 1.0; showEnter = 0 } }, children: Image("more"), clearBackGround: true)
|
|
27
37
|
}
|
|
28
38
|
.padding(12)
|
|
29
|
-
.background(
|
|
39
|
+
.background(Color(hex: "#161616E5"))
|
|
30
40
|
.hoverEffect(in: navHoverGroup) { effect, isActive, _ in
|
|
31
|
-
effect.opacity((isActive &&
|
|
41
|
+
effect.opacity((isActive && showNav == 0 && showUrl == 0) ? 1 : 0)
|
|
32
42
|
}
|
|
33
|
-
Image("nav").resizable().frame(width:
|
|
34
|
-
effect.opacity((isActive &&
|
|
43
|
+
Image("nav").resizable().frame(width: 32, height: 32).hoverEffect(.highlight).hoverEffect(in: navHoverGroup) { effect, isActive, _ in
|
|
44
|
+
effect.opacity((isActive && showNav == 0 && showUrl == 0) ? 0 : 1)
|
|
35
45
|
}
|
|
36
46
|
}
|
|
37
47
|
.frame(height: contentHeight)
|
|
38
48
|
.cornerRadius(100)
|
|
39
|
-
.
|
|
40
|
-
effect.clipShape(.capsule.size(
|
|
41
|
-
width: isActive ? proxy.size.width : proxy.size.height,
|
|
42
|
-
height: proxy.size.height,
|
|
43
|
-
anchor: .center
|
|
44
|
-
))
|
|
45
|
-
}
|
|
46
|
-
.opacity(showNav ? 0 : 1)
|
|
47
|
-
// if showNav {
|
|
49
|
+
.opacity(showEnter)
|
|
48
50
|
HStack(spacing: 14) {
|
|
49
51
|
if pwaManager.display == .minimal {
|
|
50
|
-
NavButton(action: { swc?.goBack() }, children: Image(
|
|
51
|
-
NavButton(action: { swc?.goForward() }, children: Image(
|
|
52
|
+
NavButton(action: { swc?.goBack() }, children: Image("arrow_left")).disabled(!(swc?.canGoBack ?? false))
|
|
53
|
+
NavButton(action: { swc?.goForward() }, children: Image("arrow_right")).disabled(!(swc?.canGoBack ?? false))
|
|
54
|
+
NavButton(action: { swc?.reload() }, children: Image("refresh"))
|
|
55
|
+
NavDivider()
|
|
52
56
|
}
|
|
53
|
-
NavButton(action: { swc?.
|
|
54
|
-
NavButton(action: {
|
|
55
|
-
NavButton(action: {
|
|
56
|
-
NavButton(action: { showNav = false }, children: Image(systemName: "chevron.up"))
|
|
57
|
+
NavButton(action: { swc?.navigateToURL(url: URL(string: pwaManager.start_url)!) }, children: Image("home"))
|
|
58
|
+
NavButton(action: { withAnimation(.easeInOut(duration: 0.5)) { showUrl = 1; showNav = 0 } }, children: Image("link"))
|
|
59
|
+
NavButton(action: { showNav = 0; showEnter = 1 }, children: Image("back"))
|
|
57
60
|
}
|
|
58
61
|
.padding(12)
|
|
59
|
-
.
|
|
60
|
-
.
|
|
61
|
-
.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return AnyView(EmptyView())
|
|
74
|
-
})
|
|
75
|
-
.frame(width: .maximum(300, texWidth))
|
|
76
|
-
NavButton(action: {
|
|
77
|
-
UIPasteboard.general.string = swc?.getURL()?.absoluteString ?? ""
|
|
78
|
-
showCopyTip = true
|
|
79
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
|
80
|
-
showCopyTip = false
|
|
81
|
-
}
|
|
82
|
-
}, children: Image(systemName: "square.on.square"))
|
|
83
|
-
NavButton(action: { showUrl = false }, children: Image(systemName: "xmark"))
|
|
84
|
-
}
|
|
62
|
+
.background(Color(hex: "#161616E5"))
|
|
63
|
+
.cornerRadius(100)
|
|
64
|
+
.opacity(showNav)
|
|
65
|
+
.onChange(of: showNav) { _, newValue in
|
|
66
|
+
if newValue == 1 {
|
|
67
|
+
startTimer()
|
|
68
|
+
} else {
|
|
69
|
+
stopTimer()
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
HStack(spacing: 6) {
|
|
73
|
+
Text(navInfo.url.count > 0 ? navInfo.url : (swc?.getURL()?.absoluteString ?? ""))
|
|
74
|
+
.lineLimit(1)
|
|
75
|
+
.textSelection(.enabled)
|
|
85
76
|
.padding(12)
|
|
86
|
-
.
|
|
87
|
-
.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
77
|
+
.frame(minWidth: 200)
|
|
78
|
+
.frame(maxWidth: 500)
|
|
79
|
+
.frame(height: 44)
|
|
80
|
+
.background(.black)
|
|
81
|
+
.cornerRadius(100)
|
|
82
|
+
NavButton(action: {
|
|
83
|
+
UIPasteboard.general.string = swc?.getURL()?.absoluteString ?? ""
|
|
84
|
+
showCopyTip = true
|
|
85
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
|
86
|
+
showCopyTip = false
|
|
91
87
|
}
|
|
92
|
-
|
|
88
|
+
withAnimation(.easeInOut(duration: 0.5)) { showUrl = 0; showNav = 1 }
|
|
89
|
+
}, children: Image("copy"))
|
|
90
|
+
NavButton(action: {
|
|
91
|
+
print("open browser")
|
|
92
|
+
UIApplication.shared.open(URL(string: navInfo.url.count > 0 ? navInfo.url : (swc?.getURL()?.absoluteString ?? ""))!, options: [:], completionHandler: nil)
|
|
93
|
+
withAnimation(.easeInOut(duration: 0.5)) { showUrl = 0; showNav = 1 }
|
|
94
|
+
}, children: Image("browser"))
|
|
95
|
+
NavButton(action: { withAnimation(.easeInOut(duration: 0.5)) { showUrl = 0; showNav = 1 } }, children: Image("close"))
|
|
93
96
|
}
|
|
94
|
-
|
|
97
|
+
.popover(isPresented: $showCopyTip) {
|
|
98
|
+
Text("copied!")
|
|
99
|
+
.padding()
|
|
100
|
+
.background(Color(hex: "#161616E5"))
|
|
101
|
+
.cornerRadius(10)
|
|
102
|
+
}
|
|
103
|
+
.padding(8)
|
|
104
|
+
.background(Color(hex: "#161616E5"))
|
|
105
|
+
.cornerRadius(100)
|
|
106
|
+
.opacity(showUrl)
|
|
95
107
|
}
|
|
108
|
+
}
|
|
96
109
|
|
|
97
|
-
|
|
110
|
+
private func startTimer() {
|
|
111
|
+
timeRemaining = 5
|
|
112
|
+
timer = Timer.scheduledTimer(
|
|
113
|
+
withTimeInterval: 1,
|
|
114
|
+
repeats: true
|
|
115
|
+
) { _ in
|
|
116
|
+
timeRemaining -= 1
|
|
98
117
|
|
|
99
|
-
|
|
118
|
+
if timeRemaining <= 0 {
|
|
119
|
+
showNav = 0
|
|
120
|
+
showEnter = 1
|
|
121
|
+
stopTimer()
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private func stopTimer() {
|
|
127
|
+
timer?.invalidate()
|
|
128
|
+
timer = nil
|
|
100
129
|
}
|
|
101
130
|
}
|
|
102
131
|
|
|
@@ -112,27 +141,45 @@ struct NavButton: View {
|
|
|
112
141
|
var children: Image
|
|
113
142
|
var size: CGFloat = 44
|
|
114
143
|
var padding: CGFloat = 10
|
|
115
|
-
var clearBackGround: Bool =
|
|
144
|
+
var clearBackGround: Bool = true
|
|
116
145
|
var body: some View {
|
|
117
146
|
if clearBackGround {
|
|
118
147
|
Button(action: action, label: {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
148
|
+
ZStack {
|
|
149
|
+
Circle()
|
|
150
|
+
.fill(Color.white.opacity(0))
|
|
151
|
+
.frame(width: size, height: size)
|
|
152
|
+
.overlay(
|
|
153
|
+
children.resizable().frame(width: size - padding * 2, height: size - padding * 2)
|
|
154
|
+
)
|
|
155
|
+
.contentShape(Circle())
|
|
156
|
+
}
|
|
157
|
+
}).buttonStyle(NavButtonStyle()).frame(width: size, height: size).hoverEffect(.highlight)
|
|
126
158
|
} else {
|
|
127
159
|
Button(action: action, label: {
|
|
128
160
|
Circle()
|
|
129
161
|
.fill(Color.white.opacity(0))
|
|
130
162
|
.frame(width: size, height: size)
|
|
131
163
|
.overlay(
|
|
132
|
-
children.frame(width: size, height: size
|
|
164
|
+
children.resizable().frame(width: size - padding * 2, height: size - padding * 2)
|
|
133
165
|
)
|
|
134
|
-
|
|
166
|
+
.contentShape(Circle())
|
|
167
|
+
}).hoverEffect(.highlight)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
struct NavDivider: View {
|
|
173
|
+
var width: CGFloat = 1
|
|
174
|
+
var height: CGFloat = 16
|
|
175
|
+
var paddingLR: CGFloat = 4
|
|
176
|
+
var paddingTB: CGFloat = 12
|
|
177
|
+
var body: some View {
|
|
178
|
+
VStack {
|
|
179
|
+
Rectangle().fill(Color(hex: "#FFFFFF3D")).frame(width: width, height: height)
|
|
135
180
|
}
|
|
181
|
+
.padding([.top, .bottom], paddingTB)
|
|
182
|
+
.padding([.leading, .trailing], paddingLR)
|
|
136
183
|
}
|
|
137
184
|
}
|
|
138
185
|
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
2B2F1D992BEDA8EF006897EE /* VolumetricWindowContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B2F1D982BEDA8EF006897EE /* VolumetricWindowContainerView.swift */; };
|
|
22
22
|
2B2F1D9B2BEDA975006897EE /* PlainWindowContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B2F1D9A2BEDA975006897EE /* PlainWindowContainerView.swift */; };
|
|
23
23
|
2B37E8E12D002C4D0096749A /* MaterialWithBorderCornerModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B37E8E02D002C2B0096749A /* MaterialWithBorderCornerModifier.swift */; };
|
|
24
|
+
2B48B3F12DF9866700826889 /* ColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B48B3F02DF9866100826889 /* ColorExtension.swift */; };
|
|
24
25
|
2B67BBAC2D151C1A00BBC689 /* manifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B67BBAB2D151C1500BBC689 /* manifest.swift */; };
|
|
25
|
-
2B6BA5DF2DF0607400D40337 /* nav.png in Resources */ = {isa = PBXBuildFile; fileRef = 2B6BA5DE2DF0607400D40337 /* nav.png */; };
|
|
26
26
|
2B85209C2BFD5FDB0038FE29 /* UpdateSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B85209B2BFD5FDB0038FE29 /* UpdateSystem.swift */; };
|
|
27
27
|
2B9909932C3605A9004826D1 /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B9909922C3605A9004826D1 /* Window.swift */; };
|
|
28
28
|
2BA06D712D4380C60020B505 /* LoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BA06D702D4380C30020B505 /* LoadingView.swift */; };
|
|
@@ -76,8 +76,8 @@
|
|
|
76
76
|
2B2F1D982BEDA8EF006897EE /* VolumetricWindowContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VolumetricWindowContainerView.swift; sourceTree = "<group>"; };
|
|
77
77
|
2B2F1D9A2BEDA975006897EE /* PlainWindowContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlainWindowContainerView.swift; sourceTree = "<group>"; };
|
|
78
78
|
2B37E8E02D002C2B0096749A /* MaterialWithBorderCornerModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaterialWithBorderCornerModifier.swift; sourceTree = "<group>"; };
|
|
79
|
+
2B48B3F02DF9866100826889 /* ColorExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorExtension.swift; sourceTree = "<group>"; };
|
|
79
80
|
2B67BBAB2D151C1500BBC689 /* manifest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = manifest.swift; sourceTree = "<group>"; };
|
|
80
|
-
2B6BA5DE2DF0607400D40337 /* nav.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = nav.png; sourceTree = "<group>"; };
|
|
81
81
|
2B85209B2BFD5FDB0038FE29 /* UpdateSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateSystem.swift; sourceTree = "<group>"; };
|
|
82
82
|
2B9909922C3605A9004826D1 /* Window.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = "<group>"; };
|
|
83
83
|
2BA06D702D4380C30020B505 /* LoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingView.swift; sourceTree = "<group>"; };
|
|
@@ -125,6 +125,7 @@
|
|
|
125
125
|
2B0B1C0E2C494E4100E644F9 /* Utils */ = {
|
|
126
126
|
isa = PBXGroup;
|
|
127
127
|
children = (
|
|
128
|
+
2B48B3F02DF9866100826889 /* ColorExtension.swift */,
|
|
128
129
|
2B0FF4F32DD2711800C3F20A /* PerfClock.swift */,
|
|
129
130
|
2BD510552D54A2FF0001E5E6 /* SceneManager.swift */,
|
|
130
131
|
2B0B43092CBE21540003CEF3 /* CommandManager.swift */,
|
|
@@ -160,7 +161,6 @@
|
|
|
160
161
|
2B2F1D8A2BED4CD0006897EE /* libs */,
|
|
161
162
|
2B2F1D6A2BEBFAAA006897EE /* web_spatialApp.swift */,
|
|
162
163
|
2B2F1D702BEBFAAC006897EE /* Assets.xcassets */,
|
|
163
|
-
2B6BA5DE2DF0607400D40337 /* nav.png */,
|
|
164
164
|
2B2F1D752BEBFAAC006897EE /* Info.plist */,
|
|
165
165
|
2B2F1D722BEBFAAC006897EE /* Preview Content */,
|
|
166
166
|
);
|
|
@@ -354,7 +354,6 @@
|
|
|
354
354
|
files = (
|
|
355
355
|
2BDBF9B62C4ED9F600D269D7 /* static-web in Resources */,
|
|
356
356
|
2B2F1D742BEBFAAC006897EE /* Preview Assets.xcassets in Resources */,
|
|
357
|
-
2B6BA5DF2DF0607400D40337 /* nav.png in Resources */,
|
|
358
357
|
2B2F1D712BEBFAAC006897EE /* Assets.xcassets in Resources */,
|
|
359
358
|
);
|
|
360
359
|
runOnlyForDeploymentPostprocessing = 0;
|
|
@@ -392,6 +391,7 @@
|
|
|
392
391
|
2BDBED5F2D3F885A0065443F /* SpatialModel3DComponent.swift in Sources */,
|
|
393
392
|
2B2F1D6B2BEBFAAA006897EE /* web_spatialApp.swift in Sources */,
|
|
394
393
|
2B0B1C102C494E5400E644F9 /* Logger.swift in Sources */,
|
|
394
|
+
2B48B3F12DF9866700826889 /* ColorExtension.swift in Sources */,
|
|
395
395
|
2B37E8E12D002C4D0096749A /* MaterialWithBorderCornerModifier.swift in Sources */,
|
|
396
396
|
2BC261222D38F1DA00BCA977 /* NavView.swift in Sources */,
|
|
397
397
|
2BAC1BE22CDC49040022E29B /* SpatialViewUI.swift in Sources */,
|
|
Binary file
|
package/web-spatial/nav.png
DELETED
|
Binary file
|