Skip to content

Commit 00a1bb5

Browse files
committed
Adding drag and drop feature for VMRemovableDrivesView
1 parent 6025783 commit 00a1bb5

File tree

2 files changed

+37
-7
lines changed

2 files changed

+37
-7
lines changed

Platform/Shared/VMRemovableDrivesView.swift

+34-4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//
1616

1717
import SwiftUI
18+
import UniformTypeIdentifiers
1819

1920
struct VMRemovableDrivesView: View {
2021
@ObservedObject var vm: VMData
@@ -25,7 +26,10 @@ struct VMRemovableDrivesView: View {
2526
/// Explanation see "SwiftUI FileImporter modal bug" in the `body`
2627
@State private var workaroundFileImporterBug: Bool = false
2728
@State private var currentDrive: UTMQemuConfigurationDrive?
28-
29+
30+
private static let shareDirectoryUTType = UTType.folder
31+
private static let diskImageUTType = UTType.data
32+
2933
private var qemuVM: (any UTMSpiceVirtualMachine)! {
3034
vm.wrapped as? any UTMSpiceVirtualMachine
3135
}
@@ -73,8 +77,21 @@ struct VMRemovableDrivesView: View {
7377
} else {
7478
Button("Browse…", action: { shareDirectoryFileImportPresented.toggle() })
7579
}
76-
}.fileImporter(isPresented: $shareDirectoryFileImportPresented, allowedContentTypes: [.folder], onCompletion: selectShareDirectory)
77-
.disabled(mode == .virtfs && vm.state != .stopped)
80+
}.fileImporter(isPresented: $shareDirectoryFileImportPresented, allowedContentTypes: [Self.shareDirectoryUTType], onCompletion: selectShareDirectory)
81+
.disabled(mode == .virtfs && vm.state != .stopped)
82+
.onDrop(of: [Self.shareDirectoryUTType], isTargeted: nil) { providers in
83+
guard let item = providers.first, item.hasItemConformingToTypeIdentifier(Self.shareDirectoryUTType.identifier) else { return false }
84+
85+
item.loadItem(forTypeIdentifier: Self.shareDirectoryUTType.identifier) { url, error in
86+
if let url = url as? URL {
87+
selectShareDirectory(result: .success(url))
88+
}
89+
if let error = error {
90+
selectShareDirectory(result: .failure(error))
91+
}
92+
}
93+
return true
94+
}
7895
}
7996
ForEach(config.drives.filter { $0.isExternal }) { drive in
8097
HStack {
@@ -128,12 +145,25 @@ struct VMRemovableDrivesView: View {
128145
.lineLimit(1)
129146
.truncationMode(.tail)
130147
.foregroundColor(.secondary)
131-
}.fileImporter(isPresented: $diskImageFileImportPresented, allowedContentTypes: [.data]) { result in
148+
}.fileImporter(isPresented: $diskImageFileImportPresented, allowedContentTypes: [Self.diskImageUTType]) { result in
132149
if let currentDrive = self.currentDrive {
133150
selectRemovableImage(forDrive: currentDrive, result: result)
134151
self.currentDrive = nil
135152
}
136153
}
154+
.onDrop(of: [Self.diskImageUTType], isTargeted: nil) { providers in
155+
guard let item = providers.first, item.hasItemConformingToTypeIdentifier(Self.diskImageUTType.identifier) else { return false }
156+
157+
item.loadItem(forTypeIdentifier: Self.diskImageUTType.identifier) { url, error in
158+
if let url = url as? URL{
159+
selectRemovableImage(forDrive: drive, result: .success(url))
160+
}
161+
if let error {
162+
selectRemovableImage(forDrive: drive, result: .failure(error))
163+
}
164+
}
165+
return true
166+
}
137167
}
138168
}
139169
}

Services/UTMQemuVirtualMachine.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -792,10 +792,10 @@ extension UTMQemuVirtualMachine {
792792
try await eject(drive, isForced: true)
793793
let file = try UTMRegistryEntry.File(url: url, isReadOnly: drive.isReadOnly)
794794
await registryEntry.setExternalDrive(file, forId: drive.id)
795-
try await changeMedium(drive, with: tempBookmark, url: url, isSecurityScoped: false, isAccessOnly: isAccessOnly)
795+
try await changeMedium(drive, with: tempBookmark, isSecurityScoped: false, isAccessOnly: isAccessOnly)
796796
}
797797

798-
private func changeMedium(_ drive: UTMQemuConfigurationDrive, with bookmark: Data, url: URL?, isSecurityScoped: Bool, isAccessOnly: Bool) async throws {
798+
private func changeMedium(_ drive: UTMQemuConfigurationDrive, with bookmark: Data, isSecurityScoped: Bool, isAccessOnly: Bool) async throws {
799799
let system = await system ?? UTMProcess()
800800
let (success, bookmark, path) = await system.accessData(withBookmark: bookmark, securityScoped: isSecurityScoped)
801801
guard let bookmark = bookmark, let path = path, success else {
@@ -818,7 +818,7 @@ extension UTMQemuVirtualMachine {
818818
let id = drive.id
819819
if let bookmark = await registryEntry.externalDrives[id]?.remoteBookmark {
820820
// an image bookmark was saved while QEMU was running
821-
try await changeMedium(drive, with: bookmark, url: nil, isSecurityScoped: true, isAccessOnly: !isMounting)
821+
try await changeMedium(drive, with: bookmark, isSecurityScoped: true, isAccessOnly: !isMounting)
822822
} else if let localBookmark = await registryEntry.externalDrives[id]?.bookmark {
823823
// an image bookmark was saved while QEMU was NOT running
824824
let url = try URL(resolvingPersistentBookmarkData: localBookmark)

0 commit comments

Comments
 (0)