157 lines
4.2 KiB
Swift
157 lines
4.2 KiB
Swift
import MusicAssistantCore
|
|
import SwiftUI
|
|
|
|
struct MusicalParametersEditorView: View {
|
|
@Binding var project: SongProject
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
ManualAutoIntegerRow(
|
|
title: "BPM",
|
|
value: manualAutoIntegerBinding(\.bpm)
|
|
)
|
|
ManualAutoTextRow(
|
|
title: "Key",
|
|
value: manualAutoTextBinding(\.key),
|
|
placeholder: "D"
|
|
)
|
|
ManualAutoTextRow(
|
|
title: "Scale",
|
|
value: manualAutoTextBinding(\.scale),
|
|
placeholder: "minor"
|
|
)
|
|
ManualAutoTextRow(
|
|
title: "Maqam",
|
|
value: manualAutoTextBinding(\.maqam),
|
|
placeholder: "Hijaz"
|
|
)
|
|
}
|
|
}
|
|
|
|
private func manualAutoIntegerBinding(
|
|
_ keyPath: WritableKeyPath<SongProject, ManualAutoValue<Int>?>
|
|
) -> Binding<ManualAutoValue<Int>> {
|
|
Binding {
|
|
project[keyPath: keyPath] ?? ManualAutoValue(mode: .auto)
|
|
} set: { newValue in
|
|
project[keyPath: keyPath] = newValue
|
|
}
|
|
}
|
|
|
|
private func manualAutoTextBinding(
|
|
_ keyPath: WritableKeyPath<SongProject, ManualAutoValue<String>?>
|
|
) -> Binding<ManualAutoValue<String>> {
|
|
Binding {
|
|
project[keyPath: keyPath] ?? ManualAutoValue(mode: .auto)
|
|
} set: { newValue in
|
|
project[keyPath: keyPath] = newValue
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct ManualAutoIntegerRow: View {
|
|
let title: String
|
|
@Binding var value: ManualAutoValue<Int>
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
ManualAutoPicker(title: title, mode: modeBinding)
|
|
|
|
if value.mode == .manual {
|
|
TextField(title, text: integerTextBinding)
|
|
}
|
|
}
|
|
}
|
|
|
|
private var modeBinding: Binding<ControlMode> {
|
|
Binding {
|
|
value.mode
|
|
} set: { newMode in
|
|
value = ManualAutoValue(
|
|
mode: newMode,
|
|
value: newMode == .manual ? value.value : nil
|
|
)
|
|
}
|
|
}
|
|
|
|
private var integerTextBinding: Binding<String> {
|
|
Binding {
|
|
value.value.map(String.init) ?? ""
|
|
} set: { newValue in
|
|
value.value = Int(newValue.trimmingCharacters(in: .whitespacesAndNewlines))
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct ManualAutoTextRow: View {
|
|
let title: String
|
|
@Binding var value: ManualAutoValue<String>
|
|
let placeholder: String
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
ManualAutoPicker(title: title, mode: modeBinding)
|
|
|
|
if value.mode == .manual {
|
|
TextField(placeholder, text: textBinding)
|
|
}
|
|
}
|
|
}
|
|
|
|
private var modeBinding: Binding<ControlMode> {
|
|
Binding {
|
|
value.mode
|
|
} set: { newMode in
|
|
value = ManualAutoValue(
|
|
mode: newMode,
|
|
value: newMode == .manual ? value.value : nil
|
|
)
|
|
}
|
|
}
|
|
|
|
private var textBinding: Binding<String> {
|
|
Binding {
|
|
value.value ?? ""
|
|
} set: { newValue in
|
|
let trimmedValue = newValue.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
value.value = trimmedValue.isEmpty ? nil : trimmedValue
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct ManualAutoPicker: View {
|
|
let title: String
|
|
@Binding var mode: ControlMode
|
|
|
|
var body: some View {
|
|
Picker(title, selection: $mode) {
|
|
Text("Auto").tag(ControlMode.auto)
|
|
Text("Manual").tag(ControlMode.manual)
|
|
}
|
|
.pickerStyle(.segmented)
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
MusicalParametersEditorPreview()
|
|
}
|
|
|
|
private struct MusicalParametersEditorPreview: View {
|
|
@State private var project = SongProject(
|
|
title: "Parameters",
|
|
idea: "Preview",
|
|
bpm: ManualAutoValue(mode: .manual, value: 96),
|
|
key: ManualAutoValue(mode: .auto),
|
|
scale: ManualAutoValue(mode: .manual, value: "minor"),
|
|
maqam: ManualAutoValue(mode: .auto)
|
|
)
|
|
|
|
var body: some View {
|
|
Form {
|
|
MusicalParametersEditorView(project: $project)
|
|
}
|
|
.formStyle(.grouped)
|
|
.frame(width: 340)
|
|
}
|
|
}
|