Skip to content

Commit

Permalink
Open source
Browse files Browse the repository at this point in the history
  • Loading branch information
tattn committed Oct 14, 2023
1 parent 4c20c81 commit e6ac7a8
Show file tree
Hide file tree
Showing 14 changed files with 455 additions and 3 deletions.
2 changes: 1 addition & 1 deletion app/xcode/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ let package = Package(
]),
.target(name: "VCamLocalization", resources: [.process("VCamResources")]),
.target(name: "VCamMedia", dependencies: ["VCamEntity", "VCamAppExtension", "VCamLogger"]),
.target(name: "VCamBridge", dependencies: ["VCamUIFoundation"]),
.target(name: "VCamBridge", dependencies: ["VCamEntity", "VCamUIFoundation", "VCamLocalization"]),
.target(name: "VCamTracking", dependencies: ["VCamCamera"]),
.target(name: "VCamCamera", dependencies: ["VCamMedia", "VCamData", "VCamLogger"]),

Expand Down
75 changes: 75 additions & 0 deletions app/xcode/Sources/VCamBridge/ExternalStateBinding.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// ExternalStateBinding.swift
//
//
// Created by Tatsuya Tanaka on 2023/02/13.
//

import SwiftUI

public struct ExternalState<Value: Hashable> {
let id: UUID
let get: () -> Value
let set: (Value) -> Void
}

extension ExternalState {
init(id: UUID, binding: @autoclosure @escaping () -> Binding<Value>) {
self.id = id
self.get = { binding().wrappedValue }
self.set = { binding().wrappedValue = $0 }
}
}

private final class Reloader: ObservableObject {}
private var reloaders: [UUID: Reloader] = [:]

/// Bind the state outside of SwiftUI and rebuild the UI as needed
@propertyWrapper @dynamicMemberLookup public struct ExternalStateBinding<Value: Hashable>: DynamicProperty {
public init(id: UUID, get: @escaping () -> Value, set: @escaping (Value) -> Void) {
self.id = id
self.get = get
self.set = set

if let reloader = reloaders[id] {
self.reloader = reloader
} else {
reloader = Reloader()
reloaders[id] = reloader
}
}

public init(_ state: ExternalState<Value>) {
self.init(id: state.id, get: state.get, set: state.set)
}

private let id: UUID
private let get: () -> Value
private let set: (Value) -> Void

@ObservedObject private var reloader: Reloader
@State private var lastValue: Value?

public var wrappedValue: Value {
get { get() }
nonmutating set {
set(newValue)
if lastValue != newValue {
reloader.objectWillChange.send()
}
}
}

public var projectedValue: Binding<Value> {
.init(get: get, set: { wrappedValue = $0 })
}

public subscript<Subject>(dynamicMember keyPath: ReferenceWritableKeyPath<Value, Subject>) -> ExternalStateBinding<Subject> {
get {
.init(id: id, get: { wrappedValue[keyPath: keyPath] }, set: { wrappedValue[keyPath: keyPath] = $0 })
}
set {
wrappedValue[keyPath: keyPath] = newValue.wrappedValue
}
}
}
49 changes: 49 additions & 0 deletions app/xcode/Sources/VCamBridge/FilterParameterPreset.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// FilterParameterPreset.swift
//
//
// Created by Tatsuya Tanaka on 2022/03/22.
//

import Foundation
import VCamLocalization

public struct FilterParameterPreset: CaseIterable, Hashable, Identifiable, CustomStringConvertible {
public static var allCases: [FilterParameterPreset] {
let params = UniBridge.shared.allDisplayParameterPresets.components(separatedBy: ",")
return params.compactMap {
let values = $0.components(separatedBy: "@")
guard values.count == 2 else { return nil }
return FilterParameterPreset(id: values[0], description: values[1])
}
}

public static let newPreset = Self.init(id: "", description: L10n.newPreset.text)

public let id: String
public var description: String
}

extension FilterParameterPreset {
public init(string: String) {
let values = string.components(separatedBy: "@")
if values.count == 2 {
self = FilterParameterPreset(id: values[0], description: values[1])
} else {
self = .newPreset
}
}
}

private let currentFilterParameterPresetId = UUID()

public extension ExternalState {
static var currentFilterParameterPreset: ExternalState<FilterParameterPreset> {
.init(id: currentFilterParameterPresetId) {
FilterParameterPreset(string: UniBridge.shared.currentDisplayParameter.wrappedValue)
} set: {
UniBridge.shared.currentDisplayParameter.wrappedValue = "\($0.id)@\($0.description)"
UniBridge.shared.applyDisplayParameter()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import Foundation
import VCamEntity
import VCamBridge

@propertyWrapper public struct UniAction<Arguments>: Equatable {
public init(action: @escaping (Arguments) -> Void) {
Expand Down
19 changes: 19 additions & 0 deletions app/xcode/Sources/VCamBridge/UniBridge+ExternalStateBinding.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// UniBridge+ExternalStateBinding.swift
//
//
// Created by Tatsuya Tanaka on 2023/4/14.
//

import Foundation

private let baseUUID = UUID()

public extension ExternalStateBinding {
init(_ type: UniBridge.BoolType) where Value == Bool {
var uuid = baseUUID.uuid
uuid.0 = UInt8(type.rawValue)
let mapper = UniBridge.shared.boolMapper
self.init(id: UUID(uuid: uuid), get: { mapper.get(type) }, set: mapper.set(type))
}
}
12 changes: 12 additions & 0 deletions app/xcode/Sources/VCamData/UserDefaultsKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public extension UserDefaults.Key {
typealias Key = UserDefaults.Key
static var skipThisVersion: Key<Version> { .init("vc_skip_version", default: "0.0.0") }
static var previousVersion: Key<String> { .init("vc_prev_version", default: "") }
static var useHMirror: Key<Bool> { .init("vc_use_hmirror", default: false) }
static var useAutoConvertVRM1: Key<Bool> { .init("vc_use_auto_convert_vrm1", default: false) }
static var useVowelEstimation: Key<Bool> { .init("vc_use_vowel_estimation", default: false) }
static var useEyeTracking: Key<Bool> { .init("vc_use_eye_tracking", default: true) }
static var useEmotion: Key<Bool> { .init("vc_use_emotion", default: false) }
Expand All @@ -25,10 +27,20 @@ public extension UserDefaults.Key {
static var trackingMethodFace: Key<TrackingMethod.Face> { .init("vc_tracking_method_face", default: .default) }
static var trackingMethodHand: Key<TrackingMethod.Hand> { .init("vc_tracking_method_hand", default: .default) }
static var trackingMethodFinger: Key<TrackingMethod.Finger> { .init("vc_tracking_method_finger", default: .default) }
static var moveXIntensity: Key<Double> { .init("vc_move_x_intensity", default: 1) }
static var eyeTrackingOffsetY: Key<Double> { .init("vc_eye_tracking_offset_y", default: -0.2) }
static var eyeTrackingIntensityX: Key<Double> { .init("vc_eye_tracking_intensity_x", default: 6) }
static var eyeTrackingIntensityY: Key<Double> { .init("vc_eye_tracking_intensity_y", default: 3) }
static var fingerTrackingOpenIntensity: Key<Double> { .init("vc_ftracking_open_intensity", default: 1) }
static var fingerTrackingCloseIntensity: Key<Double> { .init("vc_ftracking_close_intensity", default: 1) }
static var screenshotWaitTime: Key<Double> { .init("vc_screenshot_wait", default: 0) }
static var screenshotDestination: Key<Data> { .init("vc_screenshot_dest", default: .init()) }
static var recordingVideoFormat: Key<String> { .init("vc_rec_videoformat", default: "mp4") }
static var recordSystemSound: Key<Bool> { .init("vc_rec_systemsound", default: false) }
static var recordMicSyncOffset: Key<Int> { .init("vc_rec_mic_sync_offset", default: -160) }
static var integrationVCamMocap: Key<Bool> { .init("vc_intg_vcammocap", default: false) }
static var integrationFacialMocapIp: Key<String> { .init("vc_intg_facialmocap_ip", default: "192.168.0.1") }
static var integrationMocopi: Key<Bool> { .init("vc_intg_mocopi", default: false) }

static var macOSMicModeEnabled: Key<Bool> { .init("vc_macos_micmode_enabled", default: false) }
}
1 change: 1 addition & 0 deletions app/xcode/Sources/VCamUI/MacWindowManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public final class MacWindowManager {
private var openWindows: [String: NSWindow] = [:]

public var openSettings = {}
public var openSettingsVirtualCamera = {}

public func open<T: MacWindow>(_ windowView: T) {
let id = self.id(T.self)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import AppKit
import VCamEntity
import VCamBridge
import VCamLocalization
import struct SwiftUI.Image

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import AppKit
import VCamEntity
import VCamBridge
import VCamLocalization
import struct SwiftUI.Image

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import Foundation
import VCamEntity
import VCamBridge
import VCamLocalization
import struct SwiftUI.Image

Expand Down
6 changes: 6 additions & 0 deletions app/xcode/Sources/VCamUI/UIComponent/SelectEditField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,9 @@ where T.AllCases: RandomAccessCollection {
}
}
}

extension SelectEditField: Equatable {
public static func == (lhs: Self, rhs: Self) -> Bool {
lhs.value == rhs.value && lhs.label == rhs.label
}
}
66 changes: 66 additions & 0 deletions app/xcode/Sources/VCamUI/VCamMainView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//
// VCamMainView.swift
//
//
// Created by Tatsuya Tanaka on 2022/02/20.
//

import SwiftUI
import VCamEntity
import VCamCamera
import VCamTracking

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

@UniState(.message, name: "message") private var message

@State private var isCameraExtensionDisallow = false

public var body: some View {
VStack(alignment: .leading) {
if isCameraExtensionDisallow {
Button {
MacWindowManager.shared.openSettingsVirtualCamera()
} label: {
Image(systemName: "exclamationmark.triangle")
Text(L10n.cameraExtensionAwaitingUserApproval.key, bundle: .localize)
}
.font(.footnote)
.frame(maxWidth: .infinity, alignment: .trailing)
}

HStack {
SelectAllTextField(placeholder: L10n.message.text, text: $message)
FlatButton {
Tracking.shared.resetCalibration()
} label: {
Text(L10n.calibrate.key, bundle: .localize)
.font(.callout)
}
.flatButtonStyle(.label)
.help(L10n.helpCalibrate.text)
}

VCamShortcutGridView()
}
.task {
if let property = try? await CameraExtension().extensionProperties() {
isCameraExtensionDisallow = property.isAwaitingUserApproval
}
}
}
}

#Preview {
VCamMainView()
.padding(4)
}

#Preview {
VCamShortcutGridView(shortcutManager: VCamShortcutManager(shortcuts: [
.create(),
.create(),
]))
.padding(4)
}
Loading

0 comments on commit e6ac7a8

Please sign in to comment.