|
@@ -93,7 +93,7 @@ internal class SDL_ClearHostingController<Content: View>: UIHostingController<Co
|
|
|
@MainActor
|
|
@MainActor
|
|
|
@objc(SDL_CurvedContentHosting)
|
|
@objc(SDL_CurvedContentHosting)
|
|
|
internal class SDL_CurvedContentHosting: NSObject {
|
|
internal class SDL_CurvedContentHosting: NSObject {
|
|
|
- private let settings = SDL_CurvedContentSettings()
|
|
|
|
|
|
|
+ private let settings = SDL_CurvedContentSettings.load()
|
|
|
|
|
|
|
|
private let helper = SDL_RealityKitHelper()
|
|
private let helper = SDL_RealityKitHelper()
|
|
|
|
|
|
|
@@ -127,7 +127,7 @@ internal class SDL_CurvedContentHosting: NSObject {
|
|
|
// Spin up an async task to present / dismiss ornaments when there are updates to the scene state.
|
|
// Spin up an async task to present / dismiss ornaments when there are updates to the scene state.
|
|
|
let settings = self.settings
|
|
let settings = self.settings
|
|
|
let sceneStateObservations = Observations { [weak settings] in
|
|
let sceneStateObservations = Observations { [weak settings] in
|
|
|
- guard let settings else { return nil as (SDL_CurvedContentSettings.SceneState, SDL_CurvedContentSettings.InputType, Bool, Bool)? }
|
|
|
|
|
|
|
+ guard let settings else { return nil as (SceneState, InputType, Bool, Bool)? }
|
|
|
return (settings.sceneState, settings.inputType, settings.isSnapped, settings.settingsExpanded)
|
|
return (settings.sceneState, settings.inputType, settings.isSnapped, settings.settingsExpanded)
|
|
|
}
|
|
}
|
|
|
Task { [weak self] in
|
|
Task { [weak self] in
|
|
@@ -195,26 +195,76 @@ internal class SDL_CurvedContentHosting: NSObject {
|
|
|
|
|
|
|
|
// MARK: - Settings Panel
|
|
// MARK: - Settings Panel
|
|
|
|
|
|
|
|
|
|
+/// State of the app user interface, determined by the content view's state.
|
|
|
|
|
+enum SceneState: String, Codable {
|
|
|
|
|
+ /// A state which allows the user to configure the scene. Ornaments should be visible.
|
|
|
|
|
+ case interactive
|
|
|
|
|
+
|
|
|
|
|
+ /// A state which hides all UI except for the game itself. Ornaments should not be visible.
|
|
|
|
|
+ case cinematic
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+enum InputType: String, Codable {
|
|
|
|
|
+ case eyes
|
|
|
|
|
+ case pointer
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+internal class SDL_CurvedContentPersistentSettings: Codable {
|
|
|
|
|
+ var inputType: InputType?
|
|
|
|
|
+ var showHover: Bool?
|
|
|
|
|
+ var isDimmed: Bool?
|
|
|
|
|
+ var curvatureRadius: Float?
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
@Observable
|
|
@Observable
|
|
|
internal class SDL_CurvedContentSettings {
|
|
internal class SDL_CurvedContentSettings {
|
|
|
- /// State of the app user interface, determined by the content view's state.
|
|
|
|
|
- enum SceneState {
|
|
|
|
|
- /// A state which allows the user to configure the scene. Ornaments should be visible.
|
|
|
|
|
- case interactive
|
|
|
|
|
|
|
|
|
|
- /// A state which hides all UI except for the game itself. Ornaments should not be visible.
|
|
|
|
|
- case cinematic
|
|
|
|
|
|
|
+ static func load() -> SDL_CurvedContentSettings {
|
|
|
|
|
+ let settings = SDL_CurvedContentSettings()
|
|
|
|
|
+ if let json = SDL_VisionOS_GetWindowSettings() {
|
|
|
|
|
+ if json != "", let data = json.data(using: .utf8) {
|
|
|
|
|
+ do {
|
|
|
|
|
+ let values = try JSONDecoder().decode(SDL_CurvedContentPersistentSettings.self, from:data)
|
|
|
|
|
+ if let inputType = values.inputType {
|
|
|
|
|
+ settings.inputType = inputType
|
|
|
|
|
+ }
|
|
|
|
|
+ if let showHover = values.showHover {
|
|
|
|
|
+ settings.showHover = showHover
|
|
|
|
|
+ }
|
|
|
|
|
+ if let isDimmed = values.isDimmed {
|
|
|
|
|
+ settings.isDimmed = isDimmed
|
|
|
|
|
+ }
|
|
|
|
|
+ if let curvatureRadius = values.curvatureRadius {
|
|
|
|
|
+ settings.curvatureRadius = curvatureRadius
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ NSLog("Couldn't parse window settings: %@", error.localizedDescription)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return settings
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- enum InputType {
|
|
|
|
|
- case eyes
|
|
|
|
|
- case pointer
|
|
|
|
|
|
|
+ func save() {
|
|
|
|
|
+ let values = SDL_CurvedContentPersistentSettings()
|
|
|
|
|
+ values.inputType = inputType
|
|
|
|
|
+ values.showHover = showHover
|
|
|
|
|
+ values.isDimmed = isDimmed
|
|
|
|
|
+ values.curvatureRadius = curvatureRadius
|
|
|
|
|
+
|
|
|
|
|
+ do {
|
|
|
|
|
+ let data = try JSONEncoder().encode(values)
|
|
|
|
|
+ let json = String(data: data, encoding: String.Encoding.utf8)
|
|
|
|
|
+ SDL_VisionOS_SendWindowSettings(json)
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ NSLog("Couldn't encode window settings: %@", error.localizedDescription)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
var inputType: InputType = .eyes
|
|
var inputType: InputType = .eyes
|
|
|
var showHover: Bool = true
|
|
var showHover: Bool = true
|
|
|
var isDimmed: Bool = false
|
|
var isDimmed: Bool = false
|
|
|
- var curvatureRadius: Float = SDL_VisionOS_GetCurvature()
|
|
|
|
|
|
|
+ var curvatureRadius: Float = 0.0
|
|
|
var sceneState: SceneState = .interactive
|
|
var sceneState: SceneState = .interactive
|
|
|
var isSnapped: Bool = false
|
|
var isSnapped: Bool = false
|
|
|
var settingsExpanded: Bool = false
|
|
var settingsExpanded: Bool = false
|
|
@@ -330,6 +380,9 @@ struct SDL_SettingsPanelView: View {
|
|
|
|
|
|
|
|
Toggle(isOn: $settings.showHover) {
|
|
Toggle(isOn: $settings.showHover) {
|
|
|
}
|
|
}
|
|
|
|
|
+ .onChange(of: settings.showHover) {
|
|
|
|
|
+ settings.save()
|
|
|
|
|
+ }
|
|
|
.labelsHidden()
|
|
.labelsHidden()
|
|
|
.tint(.secondary)
|
|
.tint(.secondary)
|
|
|
|
|
|
|
@@ -341,6 +394,9 @@ struct SDL_SettingsPanelView: View {
|
|
|
|
|
|
|
|
Toggle(isOn: $settings.isDimmed) {
|
|
Toggle(isOn: $settings.isDimmed) {
|
|
|
}
|
|
}
|
|
|
|
|
+ .onChange(of: settings.isDimmed) {
|
|
|
|
|
+ settings.save()
|
|
|
|
|
+ }
|
|
|
.labelsHidden()
|
|
.labelsHidden()
|
|
|
.tint(.secondary)
|
|
.tint(.secondary)
|
|
|
|
|
|
|
@@ -383,7 +439,7 @@ struct SDL_SettingsPanelView: View {
|
|
|
+ (1.0 - curvatureSlider) * Self.maximumCurvatureRadius)
|
|
+ (1.0 - curvatureSlider) * Self.maximumCurvatureRadius)
|
|
|
settings.curvatureRadius = radius
|
|
settings.curvatureRadius = radius
|
|
|
}
|
|
}
|
|
|
- SDL_VisionOS_SendCurvatureChanged(settings.curvatureRadius)
|
|
|
|
|
|
|
+ settings.save()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
CurviestButtonIcon()
|
|
CurviestButtonIcon()
|