Browse Source

settings(qemu): add drives from add device menu

osy 3 years ago
parent
commit
4a98c4cbda

+ 29 - 0
Platform/Shared/VMSettingsAddDeviceMenuView.swift

@@ -18,6 +18,22 @@ import SwiftUI
 
 struct VMSettingsAddDeviceMenuView: View {
     @ObservedObject var config: UTMQemuConfiguration
+    @Binding var isCreateDriveShown: Bool
+    @Binding var isImportDriveShown: Bool
+    
+    init(config: UTMQemuConfiguration, isCreateDriveShown: Binding<Bool>? = nil, isImportDriveShown: Binding<Bool>? = nil) {
+        self.config = config
+        if let isCreateDriveShown = isCreateDriveShown {
+            _isCreateDriveShown = isCreateDriveShown
+        } else {
+            _isCreateDriveShown = .constant(false)
+        }
+        if let isImportDriveShown = isImportDriveShown {
+            _isImportDriveShown = isImportDriveShown
+        } else {
+            _isImportDriveShown = .constant(false)
+        }
+    }
     
     var body: some View {
         Menu {
@@ -41,6 +57,19 @@ struct VMSettingsAddDeviceMenuView: View {
             } label: {
                 Label("Sound", systemImage: "speaker.wave.2")
             }.disabled(config.system.architecture.soundDeviceType.allRawValues.isEmpty)
+            #if os(iOS)
+            Divider()
+            Button {
+                isImportDriveShown.toggle()
+            } label: {
+                Label("Import Drive", systemImage: "externaldrive")
+            }
+            Button {
+                isCreateDriveShown.toggle()
+            } label: {
+                Label("New Drive", systemImage: "externaldrive.badge.plus")
+            }
+            #endif
         } label: {
             Label("New…", systemImage: "plus")
         }.help("Add a new device.")

+ 0 - 181
Platform/iOS/VMConfigDrivesView.swift

@@ -1,181 +0,0 @@
-//
-// Copyright © 2020 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
-
-// MARK: - Drives list
-
-struct VMConfigDrivesView: View {
-    @ObservedObject var config: UTMQemuConfiguration
-    @State private var createDriveVisible: Bool = false
-    @State private var attemptDelete: IndexSet?
-    @State private var importDrivePresented: Bool = false
-    @State private var triggerRefresh: Bool = false
-    @EnvironmentObject private var data: UTMData
-    
-    var body: some View {
-        Group {
-            if config.drives.count == 0 {
-                Text("No drives added.").font(.headline)
-            } else {
-                Form {
-                    List {
-                        ForEach($config.drives) { $drive in
-                            NavigationLink(
-                                destination: VMConfigDriveDetailsView(config: $drive, onDelete: nil), label: {
-                                    VStack(alignment: .leading) {
-                                        if drive.isExternal {
-                                            Text("Removable Drive")
-                                        } else if drive.imageName == QEMUPackageFileName.efiVariables.rawValue {
-                                            Text("EFI Variables")
-                                        } else if let imageName = drive.imageName {
-                                            Text(imageName)
-                                                .lineLimit(1)
-                                        } else {
-                                            Text("(new)")
-                                        }
-                                        HStack {
-                                            Text(drive.imageType.prettyValue).font(.caption)
-                                            if drive.imageType == .disk || drive.imageType == .cd {
-                                                Text("-")
-                                                Text(drive.interface.prettyValue).font(.caption)
-                                            }
-                                        }
-                                    }
-                                })
-                        }.onDelete { offsets in
-                            attemptDelete = offsets
-                        }
-                        .onMove(perform: moveDrives)
-                    }
-                }.toolbar {
-                    ToolbarItem(placement: .status) {
-                        Text("Note: Boot order is as listed.")
-                    }
-                }.onChange(of: triggerRefresh) { _ in
-                    // HACK: we need edits of drive to trigger a redraw
-                }
-            }
-        }
-        .navigationBarItems(trailing:
-            HStack {
-                EditButton().padding(.trailing, 10)
-                Button(action: { importDrivePresented.toggle() }, label: {
-                    Label("Import Drive", systemImage: "square.and.arrow.down").labelStyle(.iconOnly)
-                }).padding(.trailing, 10)
-                Button(action: { createDriveVisible.toggle() }, label: {
-                    Label("New Drive", systemImage: "plus").labelStyle(.iconOnly)
-                })
-            }
-        )
-        .fileImporter(isPresented: $importDrivePresented, allowedContentTypes: [.item], onCompletion: importDrive)
-        .sheet(isPresented: $createDriveVisible) {
-            CreateDrive(newDrive: UTMQemuConfigurationDrive(forArchitecture: config.system.architecture, target: config.system.target), onDismiss: newDrive)
-        }
-        .actionSheet(item: $attemptDelete) { offsets in
-            ActionSheet(title: Text("Confirm Delete"), message: Text("Are you sure you want to permanently delete this disk image?"), buttons: [.cancel(), .destructive(Text("Delete")) {
-                deleteDrives(offsets: offsets)
-            }])
-        }
-    }
-    
-    private func importDrive(result: Result<URL, Error>) {
-        data.busyWorkAsync {
-            switch result {
-            case .success(let url):
-                await MainActor.run {
-                    var drive = UTMQemuConfigurationDrive(forArchitecture: config.system.architecture, target: config.system.target, isExternal: true)
-                    drive.imageURL = url
-                    config.drives.append(drive)
-                }
-                break
-            case .failure(let err):
-                throw err
-            }
-        }
-    }
-    
-    private func newDrive(drive: UTMQemuConfigurationDrive) {
-        config.drives.append(drive)
-    }
-    
-    private func deleteDrives(offsets: IndexSet) {
-        config.drives.remove(atOffsets: offsets)
-    }
-    
-    private func moveDrives(source: IndexSet, destination: Int) {
-        config.drives.move(fromOffsets: source, toOffset: destination)
-    }
-}
-
-// MARK: - Create Drive
-
-private struct CreateDrive: View {
-    @State var newDrive: UTMQemuConfigurationDrive
-    let onDismiss: (UTMQemuConfigurationDrive) -> Void
-    @Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode>
-    
-    var body: some View {
-        NavigationView {
-            VMConfigDriveCreateView(config: $newDrive)
-                .toolbar {
-                    ToolbarItem(placement: .cancellationAction) {
-                        Button("Cancel", action: cancel)
-                    }
-                    ToolbarItem(placement: .confirmationAction) {
-                        Button("Done", action: done)
-                    }
-                }
-        }.navigationViewStyle(.stack)
-    }
-    
-    private func cancel() {
-        presentationMode.wrappedValue.dismiss()
-    }
-    
-    private func done() {
-        presentationMode.wrappedValue.dismiss()
-        onDismiss(newDrive)
-    }
-}
-
-// MARK: - Preview
-
-struct VMConfigDrivesView_Previews: PreviewProvider {
-    @StateObject static private var config = UTMQemuConfiguration()
-    
-    static var previews: some View {
-        Group {
-            VMConfigDrivesView(config: config)
-            CreateDrive(newDrive: UTMQemuConfigurationDrive()) { _ in
-                
-            }
-        }.onAppear {
-            if config.drives.count == 0 {
-                var drive = UTMQemuConfigurationDrive(forArchitecture: .x86_64, target: QEMUTarget_x86_64.pc)
-                drive.imageName = "test.img"
-                drive.imageType = .disk
-                drive.interface = .ide
-                config.drives.append(drive)
-                drive = UTMQemuConfigurationDrive(forArchitecture: .x86_64, target: QEMUTarget_x86_64.pc)
-                drive.imageName = "bios.bin"
-                drive.imageType = .bios
-                drive.interface = .none
-                config.drives.append(drive)
-            }
-        }
-    }
-}

+ 125 - 0
Platform/iOS/VMDrivesSettingsView.swift

@@ -0,0 +1,125 @@
+//
+// Copyright © 2020 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
+
+// MARK: - Drives list
+
+struct VMDrivesSettingsView: View {
+    @ObservedObject var config: UTMQemuConfiguration
+    @Binding var isCreateDriveShown: Bool
+    @State private var attemptDelete: IndexSet?
+    @EnvironmentObject private var data: UTMData
+    
+    var body: some View {
+        ForEach($config.drives) { $drive in
+            NavigationLink(
+                destination: VMConfigDriveDetailsView(config: $drive, onDelete: nil), label: {
+                    Label(label(for: drive), systemImage: "externaldrive")
+                })
+        }.onDelete { offsets in
+            attemptDelete = offsets
+        }
+        .onMove(perform: moveDrives)
+        .sheet(isPresented: $isCreateDriveShown) {
+            CreateDrive(newDrive: UTMQemuConfigurationDrive(forArchitecture: config.system.architecture, target: config.system.target), onDismiss: newDrive)
+        }
+        .actionSheet(item: $attemptDelete) { offsets in
+            ActionSheet(title: Text("Confirm Delete"), message: Text("Are you sure you want to permanently delete this disk image?"), buttons: [.cancel(), .destructive(Text("Delete")) {
+                deleteDrives(offsets: offsets)
+            }])
+        }
+    }
+    
+    private func label(for drive: UTMQemuConfigurationDrive) -> String {
+        if drive.interface == .none && drive.imageName == QEMUPackageFileName.efiVariables.rawValue {
+            return NSLocalizedString("EFI Variables", comment: "VMDrivesSettingsView")
+        } else {
+            return NSLocalizedString("\(drive.interface.prettyValue) Drive", comment: "VMDrivesSettingsView")
+        }
+    }
+    
+    private func newDrive(drive: UTMQemuConfigurationDrive) {
+        config.drives.append(drive)
+    }
+    
+    private func deleteDrives(offsets: IndexSet) {
+        config.drives.remove(atOffsets: offsets)
+    }
+    
+    private func moveDrives(source: IndexSet, destination: Int) {
+        config.drives.move(fromOffsets: source, toOffset: destination)
+    }
+}
+
+// MARK: - Create Drive
+
+private struct CreateDrive: View {
+    @State var newDrive: UTMQemuConfigurationDrive
+    let onDismiss: (UTMQemuConfigurationDrive) -> Void
+    @Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode>
+    
+    var body: some View {
+        NavigationView {
+            VMConfigDriveCreateView(config: $newDrive)
+                .toolbar {
+                    ToolbarItem(placement: .cancellationAction) {
+                        Button("Cancel", action: cancel)
+                    }
+                    ToolbarItem(placement: .confirmationAction) {
+                        Button("Done", action: done)
+                    }
+                }
+        }.navigationViewStyle(.stack)
+    }
+    
+    private func cancel() {
+        presentationMode.wrappedValue.dismiss()
+    }
+    
+    private func done() {
+        presentationMode.wrappedValue.dismiss()
+        onDismiss(newDrive)
+    }
+}
+
+// MARK: - Preview
+
+struct VMConfigDrivesView_Previews: PreviewProvider {
+    @StateObject static private var config = UTMQemuConfiguration()
+    
+    static var previews: some View {
+        Group {
+            VMDrivesSettingsView(config: config, isCreateDriveShown: .constant(false))
+            CreateDrive(newDrive: UTMQemuConfigurationDrive()) { _ in
+                
+            }
+        }.onAppear {
+            if config.drives.count == 0 {
+                var drive = UTMQemuConfigurationDrive(forArchitecture: .x86_64, target: QEMUTarget_x86_64.pc)
+                drive.imageName = "test.img"
+                drive.imageType = .disk
+                drive.interface = .ide
+                config.drives.append(drive)
+                drive = UTMQemuConfigurationDrive(forArchitecture: .x86_64, target: QEMUTarget_x86_64.pc)
+                drive.imageName = "bios.bin"
+                drive.imageType = .bios
+                drive.interface = .none
+                config.drives.append(drive)
+            }
+        }
+    }
+}

+ 26 - 9
Platform/iOS/VMSettingsView.swift

@@ -21,6 +21,8 @@ struct VMSettingsView: View {
     @ObservedObject var config: UTMQemuConfiguration
     
     @State private var isResetConfig: Bool = false
+    @State private var isCreateDriveShown: Bool = false
+    @State private var isImportDriveShown: Bool = false
     
     @EnvironmentObject private var data: UTMData
     @Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode>
@@ -55,23 +57,17 @@ struct VMSettingsView: View {
                             Label("QEMU", systemImage: "shippingbox")
                                 .labelStyle(.roundRectIcon)
                         })
-                    NavigationLink(
-                        destination: VMConfigDrivesView(config: config).navigationTitle("Drives"),
-                        label: {
-                            Label("Drives", systemImage: "internaldrive")
-                                .labelStyle(.roundRectIcon)
-                        })
                     NavigationLink(
                         destination: VMConfigInputView(config: $config.input).navigationTitle("Input"),
                         label: {
                             Label("Input", systemImage: "keyboard")
-                                .labelStyle(RoundRectIconLabelStyle(color: .green))
+                                .labelStyle(.roundRectIcon)
                         })
                     NavigationLink(
                         destination: VMConfigSharingView(config: $config.sharing).navigationTitle("Sharing"),
                         label: {
                             Label("Sharing", systemImage: "person.crop.circle")
-                                .labelStyle(RoundRectIconLabelStyle(color: .yellow))
+                                .labelStyle(.roundRectIcon)
                         })
                     Section(header: Text("Devices")) {
                         ForEach($config.displays) { $display in
@@ -107,12 +103,16 @@ struct VMSettingsView: View {
                             config.sound.remove(atOffsets: offsets)
                         }
                     }
+                    Section(header: Text("Drives")) {
+                        VMDrivesSettingsView(config: config, isCreateDriveShown: $isCreateDriveShown)
+                            .labelStyle(RoundRectIconLabelStyle(color: .yellow))
+                    }
                 }
             }
             .navigationTitle("Settings")
             .navigationViewStyle(.stack)
             .navigationBarItems(leading: HStack {
-                VMSettingsAddDeviceMenuView(config: config)
+                VMSettingsAddDeviceMenuView(config: config, isCreateDriveShown: $isCreateDriveShown, isImportDriveShown: $isImportDriveShown)
                 EditButton()
             }, trailing: HStack {
                 Button(action: cancel) {
@@ -122,6 +122,7 @@ struct VMSettingsView: View {
                     Text("Save")
                 }
             })
+            .fileImporter(isPresented: $isImportDriveShown, allowedContentTypes: [.item], onCompletion: importDrive)
         }.disabled(data.busy)
         .overlay(BusyOverlay())
     }
@@ -139,6 +140,22 @@ struct VMSettingsView: View {
             try data.discardChanges(for: self.vm)
         }
     }
+    
+    private func importDrive(result: Result<URL, Error>) {
+        data.busyWorkAsync {
+            switch result {
+            case .success(let url):
+                await MainActor.run {
+                    var drive = UTMQemuConfigurationDrive(forArchitecture: config.system.architecture, target: config.system.target, isExternal: true)
+                    drive.imageURL = url
+                    config.drives.append(drive)
+                }
+                break
+            case .failure(let err):
+                throw err
+            }
+        }
+    }
 }
 
 struct RoundRectIconLabelStyle: LabelStyle {

+ 6 - 6
UTM.xcodeproj/project.pbxproj

@@ -773,7 +773,7 @@
 		CE2D957D24AD4F990059923A /* VMConfigNetworkPortForwardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2D954D24AD4F980059923A /* VMConfigNetworkPortForwardView.swift */; };
 		CE2D958324AD4F990059923A /* VMConfigNetworkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2D955024AD4F980059923A /* VMConfigNetworkView.swift */; };
 		CE2D958424AD4F990059923A /* VMConfigNetworkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2D955024AD4F980059923A /* VMConfigNetworkView.swift */; };
-		CE2D958524AD4F990059923A /* VMConfigDrivesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2D955124AD4F980059923A /* VMConfigDrivesView.swift */; };
+		CE2D958524AD4F990059923A /* VMDrivesSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2D955124AD4F980059923A /* VMDrivesSettingsView.swift */; };
 		CE2D958724AD4F990059923A /* VMConfigPortForwardForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2D955224AD4F980059923A /* VMConfigPortForwardForm.swift */; };
 		CE2D958824AD4F990059923A /* VMConfigPortForwardForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2D955224AD4F980059923A /* VMConfigPortForwardForm.swift */; };
 		CE2D958924AD4F990059923A /* VMConfigSystemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2D955324AD4F980059923A /* VMConfigSystemView.swift */; };
@@ -886,7 +886,7 @@
 		CEA45E7B263519B5002FA97D /* qapi-events-machine-target.c in Sources */ = {isa = PBXBuildFile; fileRef = CE23C13D23FCEC08001177D6 /* qapi-events-machine-target.c */; };
 		CEA45E7C263519B5002FA97D /* qapi-types-misc.c in Sources */ = {isa = PBXBuildFile; fileRef = CE23C12823FCEC07001177D6 /* qapi-types-misc.c */; };
 		CEA45E7D263519B5002FA97D /* qapi-commands-qom.c in Sources */ = {isa = PBXBuildFile; fileRef = CE23C15523FCEC0A001177D6 /* qapi-commands-qom.c */; };
-		CEA45E7E263519B5002FA97D /* VMConfigDrivesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2D955124AD4F980059923A /* VMConfigDrivesView.swift */; };
+		CEA45E7E263519B5002FA97D /* VMDrivesSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2D955124AD4F980059923A /* VMDrivesSettingsView.swift */; };
 		CEA45E7F263519B5002FA97D /* qapi-visit-rocker.c in Sources */ = {isa = PBXBuildFile; fileRef = CE23C0F723FCEC05001177D6 /* qapi-visit-rocker.c */; };
 		CEA45E80263519B5002FA97D /* qapi-visit-net.c in Sources */ = {isa = PBXBuildFile; fileRef = CE23C08A23FCEC00001177D6 /* qapi-visit-net.c */; };
 		CEA45E84263519B5002FA97D /* qapi-events-net.c in Sources */ = {isa = PBXBuildFile; fileRef = CE23C10123FCEC05001177D6 /* qapi-events-net.c */; };
@@ -1974,7 +1974,7 @@
 		CE2D954D24AD4F980059923A /* VMConfigNetworkPortForwardView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VMConfigNetworkPortForwardView.swift; sourceTree = "<group>"; };
 		CE2D954F24AD4F980059923A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		CE2D955024AD4F980059923A /* VMConfigNetworkView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VMConfigNetworkView.swift; sourceTree = "<group>"; };
-		CE2D955124AD4F980059923A /* VMConfigDrivesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VMConfigDrivesView.swift; sourceTree = "<group>"; };
+		CE2D955124AD4F980059923A /* VMDrivesSettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VMDrivesSettingsView.swift; sourceTree = "<group>"; };
 		CE2D955224AD4F980059923A /* VMConfigPortForwardForm.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VMConfigPortForwardForm.swift; sourceTree = "<group>"; };
 		CE2D955324AD4F980059923A /* VMConfigSystemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VMConfigSystemView.swift; sourceTree = "<group>"; };
 		CE2D955524AD4F980059923A /* UTMApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UTMApp.swift; sourceTree = "<group>"; };
@@ -2606,8 +2606,8 @@
 				CE8813D224CD230300532628 /* ActivityView.swift */,
 				CED814EE24C7EB760042F0F1 /* ImagePicker.swift */,
 				CEBBF1A424B56A2900C15049 /* UTMDataExtension.swift */,
-				CE2D955124AD4F980059923A /* VMConfigDrivesView.swift */,
 				CE2D954D24AD4F980059923A /* VMConfigNetworkPortForwardView.swift */,
+				CE2D955124AD4F980059923A /* VMDrivesSettingsView.swift */,
 				CE2D954C24AD4F980059923A /* VMSettingsView.swift */,
 				CEF0307026A2B04300667B63 /* VMWizardView.swift */,
 				CE95877426D74C2A0086BDE8 /* iOS.entitlements */,
@@ -3561,7 +3561,7 @@
 				CE2D92AB24AD46670059923A /* qapi-events-machine-target.c in Sources */,
 				CE2D92AC24AD46670059923A /* qapi-types-misc.c in Sources */,
 				CE2D92AE24AD46670059923A /* qapi-commands-qom.c in Sources */,
-				CE2D958524AD4F990059923A /* VMConfigDrivesView.swift in Sources */,
+				CE2D958524AD4F990059923A /* VMDrivesSettingsView.swift in Sources */,
 				CE2D92AF24AD46670059923A /* qapi-visit-rocker.c in Sources */,
 				CE2D92B224AD46670059923A /* qapi-visit-net.c in Sources */,
 				848D99BC28636AC90055C215 /* UTMConfigurationDrive.swift in Sources */,
@@ -4131,7 +4131,7 @@
 				CEA45E7C263519B5002FA97D /* qapi-types-misc.c in Sources */,
 				841619B72843226B000034B2 /* QEMUConstant.swift in Sources */,
 				CEA45E7D263519B5002FA97D /* qapi-commands-qom.c in Sources */,
-				CEA45E7E263519B5002FA97D /* VMConfigDrivesView.swift in Sources */,
+				CEA45E7E263519B5002FA97D /* VMDrivesSettingsView.swift in Sources */,
 				CEBE820C26A4C8E0007AAB12 /* VMWizardSummaryView.swift in Sources */,
 				CEA45E7F263519B5002FA97D /* qapi-visit-rocker.c in Sources */,
 				CEA45E80263519B5002FA97D /* qapi-visit-net.c in Sources */,