// // Copyright © 2023 osy. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // import SwiftUI @available(macOS 12, *) struct VMConfigQEMUArgumentsView: View { @Binding var config: UTMQemuConfigurationQEMU let architecture: QEMUArchitecture let fixedArguments: [QEMUArgument] private let fixedUuids: Set @State private var selected: Set @State private var selectedArgument = QEMUArgument("") @FocusState private var focused: UUID? @State private var showExportArgs: Bool = false private var customUuids: Set { Set(config.additionalArguments.map({ $0.id })) } private var exportShareItem: VMShareItemModifier.ShareItem { var argString = "qemu-system-\(architecture.rawValue)" for arg in fixedArguments { if arg.string.contains(" ") { argString += " \"\(arg.string)\"" } else { argString += " \(arg.string)" } } for arg in config.additionalArguments { argString += " \(arg.string)" } return .qemuCommand(argString) } init(config: Binding, architecture: QEMUArchitecture, fixedArguments: [QEMUArgument]) { self._config = config self.architecture = architecture self.fixedArguments = fixedArguments self.fixedUuids = Set(fixedArguments.map({ $0.id })) self._selected = State>(initialValue: .init()) } var body: some View { VStack { Table(of: QEMUArgument.self, selection: $selected) { TableColumn("Arguments") { arg in let customSelected = selected.intersection(customUuids) if fixedUuids.contains(arg.id) || customSelected.count > 1 || !customSelected.contains(arg.id) { Text(arg.string) .foregroundColor(fixedUuids.contains(arg.id) ? .secondary : .primary) .textSelection(.enabled) } else { TextField("", text: $selectedArgument.string) .focused($focused, equals: arg.id) .onSubmit(of: .text) { if let index = config.additionalArguments.firstIndex(of: arg) { config.additionalArguments[index] = selectedArgument } } } } } rows: { ForEach(fixedArguments) { arg in TableRow(arg) } ForEach(config.additionalArguments) { arg in TableRow(arg) } }.onChange(of: selected) { newValue in // save changes to last selected argument if let index = config.additionalArguments.firstIndex(where: { $0.id == selectedArgument.id }) { config.additionalArguments[index] = selectedArgument selectedArgument = .init("") } // get new selected argument if let selectedId = selected.intersection(customUuids).first { if let arg = config.additionalArguments.first(where: { $0.id == selectedId }) { selectedArgument = arg } } } Spacer() HStack { Button { showExportArgs.toggle() } label: { Text("Export QEMU Command…") }.help("Export all arguments as a text file. This is only for debugging purposes as UTM's built-in QEMU differs from upstream QEMU in supported arguments.") Spacer() let customSelected = selected.intersection(customUuids) if !customSelected.isEmpty { if customSelected.count > 1 || customSelected.first != config.additionalArguments.first?.id { Button { for i in 1.. 1 || customSelected.first != config.additionalArguments.last?.id { Button { for i in (0..