Skip to content

Commit

Permalink
Open source
Browse files Browse the repository at this point in the history
  • Loading branch information
tattn committed Oct 19, 2023
1 parent c57949f commit a779f32
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 28 deletions.
6 changes: 6 additions & 0 deletions app/xcode/Sources/VCamBridge/ExternalStateBinding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ public struct ExternalState<Value: Hashable> {
let id: UUID
let get: () -> Value
let set: (Value) -> Void

public init(id: UUID, get: @escaping () -> Value, set: @escaping (Value) -> Void) {
self.id = id
self.get = get
self.set = set
}
}

extension ExternalState {
Expand Down
22 changes: 12 additions & 10 deletions app/xcode/Sources/VCamBridge/UniBridge+ExternalStateBinding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,40 +9,42 @@ import Foundation

private let baseUUID = UUID().uuid

private func uuid(_ keyPath: WritableKeyPath<uuid_t, UInt8>, typeValue: Int32) -> UUID {
var uuid = baseUUID
uuid[keyPath: keyPath] = UInt8(typeValue)
return UUID(uuid: uuid)
public extension UUID {
static func externalState<T: RawRepresentable>(_ keyPath: WritableKeyPath<uuid_t, UInt8>, type: T) -> UUID where T.RawValue == Int32 {
var uuid = baseUUID
uuid[keyPath: keyPath] = UInt8(type.rawValue)
return UUID(uuid: uuid)
}
}

public extension ExternalStateBinding {
init(_ type: UniBridge.BoolType) where Value == Bool {
let mapper = UniBridge.shared.boolMapper
self.init(id: uuid(\.0, typeValue: type.rawValue), get: { mapper.get(type) }, set: mapper.set(type))
self.init(id: .externalState(\.0, type: type), get: { mapper.get(type) }, set: mapper.set(type))
}

init(_ type: UniBridge.FloatType) where Value == CGFloat {
let mapper = UniBridge.shared.floatMapper
self.init(id: uuid(\.1, typeValue: type.rawValue), get: { mapper.get(type) }, set: mapper.set(type))
self.init(id: .externalState(\.1, type: type), get: { mapper.get(type) }, set: mapper.set(type))
}

init(_ type: UniBridge.IntType) where Value == Int32 {
let mapper = UniBridge.shared.intMapper
self.init(id: uuid(\.2, typeValue: type.rawValue), get: { mapper.get(type) }, set: mapper.set(type))
self.init(id: .externalState(\.2, type: type), get: { mapper.get(type) }, set: mapper.set(type))
}

init(_ type: UniBridge.StringType) where Value == String {
let mapper = UniBridge.shared.stringMapper
self.init(id: uuid(\.3, typeValue: type.rawValue), get: { mapper.get(type) }, set: mapper.set(type))
self.init(id: .externalState(\.3, type: type), get: { mapper.get(type) }, set: mapper.set(type))
}

init<Element>(_ type: UniBridge.ArrayType, as: Array<Element>.Type) where Value == Array<Element> {
let mapper = UniBridge.shared.arrayMapper
self.init(id: uuid(\.4, typeValue: type.rawValue), get: { mapper.binding(type, size: type.arraySize).wrappedValue }, set: mapper.set(type))
self.init(id: .externalState(\.4, type: type), get: { mapper.binding(type, size: type.arraySize).wrappedValue }, set: mapper.set(type))
}

init(_ type: UniBridge.StructType, as: Value.Type = Value.self) where Value: ValueBindingStructType {
let mapper = UniBridge.shared.structMapper
self.init(id: uuid(\.5, typeValue: type.rawValue), get: { mapper.binding(type).wrappedValue }, set: { mapper.binding(type).wrappedValue = $0 })
self.init(id: .externalState(\.5, type: type), get: { mapper.binding(type).wrappedValue }, set: { mapper.binding(type).wrappedValue = $0 })
}
}
26 changes: 26 additions & 0 deletions app/xcode/Sources/VCamUI/Extensions/ExternalStateBinding+UI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// ExternalStateBinding+UI.swift
//
//
// Created by Tatsuya Tanaka on 2023/10/20.
//

import Foundation
import VCamEntity
import VCamBridge

extension ExternalState {
public static var typedScreenResolution: ExternalState<ScreenResolution> {
.init(id: .externalState(\.4, type: UniBridge.ArrayType.screenResolution), get: {
let size = UniBridge.shared.screenResolution.wrappedValue
guard size.count == 2 else { return .init(width: 1920, height: 1280) } // an empty array after disposal
return ScreenResolution(width: Int(size[0]), height: Int(size[1]))
}, set: {
let isLandscape = MainTexture.shared.isLandscape
UniBridge.shared.screenResolution.wrappedValue = [Int32($0.size.width), Int32($0.size.height)]
if isLandscape != MainTexture.shared.isLandscape {
SceneManager.shared.changeAspectRatio()
}
})
}
}
17 changes: 0 additions & 17 deletions app/xcode/Sources/VCamUI/Extensions/UniBridge+.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,6 @@ extension UniState {
}
}

extension UniState<ScreenResolution>.CustomState {
public static var typedScreenResolution: Self {
let rawValue = UniState<[Int32]>(.screenResolution, name: "screenResolution", as: [Int32].self)
return .init {
let size = rawValue.wrappedValue
guard size.count == 2 else { return .init(width: 1920, height: 1280) } // an empty array after disposal
return ScreenResolution(width: Int(size[0]), height: Int(size[1]))
} set: {
let isLandscape = MainTexture.shared.isLandscape
rawValue.wrappedValue = [Int32($0.size.width), Int32($0.size.height)]
if isLandscape != MainTexture.shared.isLandscape {
SceneManager.shared.changeAspectRatio()
}
}
}
}

extension UniBridge {
public var canvasCGSize: CGSize {
let size = canvasSize
Expand Down
63 changes: 63 additions & 0 deletions app/xcode/Sources/VCamUI/Settings/VCamSettingGeneralView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// VCamSettingGeneralView.swift
//
//
// Created by Tatsuya Tanaka on 2023/02/14.
//

import SwiftUI
import VCamLocalization
import VCamBridge

public struct VCamSettingGeneralView: View {
public init() {}

@AppStorage(key: .useHMirror) var useHMirror
@AppStorage(key: .useAutoConvertVRM1) var useAutoConvertVRM1
@AppStorage(key: .locale) var locale

@ExternalStateBinding(.useAutoMode) private var useAutoMode: Bool
@ExternalStateBinding(.useCombineMesh) private var useCombineMesh: Bool
@ExternalStateBinding(.useAddToMacOSMenuBar) private var useAddToMacOSMenuBar: Bool

public var body: some View {
GroupBox {
Form {
Toggle(isOn: $useAutoMode) {
Text(L10n.playIdleMotions.key, bundle: .localize)
}
Toggle(isOn: $useCombineMesh) {
Text(L10n.optimizeMeshes.key, bundle: .localize)
}
.help(L10n.helpMesh.text)
Toggle(isOn: $useAutoConvertVRM1) {
Text(L10n.enableAutoConvertingToVRM1.key, bundle: .localize)
}
Toggle(isOn: $useHMirror) {
Text(L10n.flipScreen.key, bundle: .localize)
}
Toggle(isOn: $useAddToMacOSMenuBar) {
Text(L10n.addToMacOSMenuBar.key, bundle: .localize)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
}
.onChange(of: useAddToMacOSMenuBar) { newValue in
WindowManager.shared.isMacOSMenubarVisible = newValue
}

GroupBox {
Form {
Picker("Language / 言語", selection: $locale.map(get: LanguageList.init(locale:), set: \.rawValue)) {
ForEach(LanguageList.allCases) { lang in
Text(lang.name).tag(lang.rawValue)
}
}
}
}
}
}

#Preview {
VCamSettingGeneralView()
}
71 changes: 71 additions & 0 deletions app/xcode/Sources/VCamUI/Settings/VCamSettingRenderingView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// VCamSettingRenderingView.swift
//
//
// Created by Tatsuya Tanaka on 2023/02/14.
//

import SwiftUI
import VCamEntity
import VCamBridge
import VCamLocalization

public struct VCamSettingRenderingView: View {
public init() {}

@ExternalStateBinding(.typedScreenResolution) private var typedScreenResolution
@ExternalStateBinding(.qualityLevel) private var qualityLevel
@ExternalStateBinding(.fps) private var fps
@ExternalStateBinding(.useVSync) private var useVSync

public var body: some View {
VStack {
GroupBox {
Form {
Picker(selection: $typedScreenResolution) {
ForEach(ScreenResolution.allCases) {
Text($0.description)
.tag($0)
}
} label: {
Text(L10n.screenResolution.key, bundle: .localize)
}
Picker(selection: $qualityLevel) {
ForEach(QualityLevel.allCases) {
Text($0.localizedName, bundle: .localize)
.tag($0.rawValue)
}
} label: {
Text(L10n.renderingQuality.key, bundle: .localize)
}

ValueEditField(L10n.fpsScreen.key, value: $fps, type: .slider(10...60))
.disabled(useVSync)
}
}
}
}
}

private extension ScreenResolution {
var description: String {
"\(size.width) x \(size.height) \(shortDescription) \(videoType)"
}

private var videoType: String {
isLandscape ? L10n.asHorizontalVideo.text : L10n.asVerticalVideo.text
}

private var shortDescription: String {
switch self {
case .resolution2160p: return "[4K]"
case .resolution1080p, .resolutionVertical1080p: return "[1080p]"
case .resolution720p: return "[720p]"
case .resolution540p: return "[540p]"
}
}
}

#Preview {
VCamSettingRenderingView()
}
2 changes: 1 addition & 1 deletion app/xcode/Sources/VCamUI/VCamRecordingView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import VCamBridge
public struct VCamRecordingView: View {
public init() {}

@UniState(.typedScreenResolution) private var typedScreenResolution
@ExternalStateBinding(.typedScreenResolution) private var typedScreenResolution
@ObservedObject private var recorder = VideoRecorder.shared
@State private var restWaitTime: CGFloat = 0
@State private var screenshotDestinationString = ""
Expand Down

0 comments on commit a779f32

Please sign in to comment.