From 00a1bb55aab31f8a52daf70ee91bcb1528ad5b43 Mon Sep 17 00:00:00 2001 From: Tigran Hambardzumyan Date: Wed, 24 Apr 2024 23:41:09 +0400 Subject: [PATCH] Adding drag and drop feature for VMRemovableDrivesView --- Platform/Shared/VMRemovableDrivesView.swift | 38 ++++++++++++++++++--- Services/UTMQemuVirtualMachine.swift | 6 ++-- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/Platform/Shared/VMRemovableDrivesView.swift b/Platform/Shared/VMRemovableDrivesView.swift index 0682533bc..ee8750f55 100644 --- a/Platform/Shared/VMRemovableDrivesView.swift +++ b/Platform/Shared/VMRemovableDrivesView.swift @@ -15,6 +15,7 @@ // import SwiftUI +import UniformTypeIdentifiers struct VMRemovableDrivesView: View { @ObservedObject var vm: VMData @@ -25,7 +26,10 @@ struct VMRemovableDrivesView: View { /// Explanation see "SwiftUI FileImporter modal bug" in the `body` @State private var workaroundFileImporterBug: Bool = false @State private var currentDrive: UTMQemuConfigurationDrive? - + + private static let shareDirectoryUTType = UTType.folder + private static let diskImageUTType = UTType.data + private var qemuVM: (any UTMSpiceVirtualMachine)! { vm.wrapped as? any UTMSpiceVirtualMachine } @@ -73,8 +77,21 @@ struct VMRemovableDrivesView: View { } else { Button("Browse…", action: { shareDirectoryFileImportPresented.toggle() }) } - }.fileImporter(isPresented: $shareDirectoryFileImportPresented, allowedContentTypes: [.folder], onCompletion: selectShareDirectory) - .disabled(mode == .virtfs && vm.state != .stopped) + }.fileImporter(isPresented: $shareDirectoryFileImportPresented, allowedContentTypes: [Self.shareDirectoryUTType], onCompletion: selectShareDirectory) + .disabled(mode == .virtfs && vm.state != .stopped) + .onDrop(of: [Self.shareDirectoryUTType], isTargeted: nil) { providers in + guard let item = providers.first, item.hasItemConformingToTypeIdentifier(Self.shareDirectoryUTType.identifier) else { return false } + + item.loadItem(forTypeIdentifier: Self.shareDirectoryUTType.identifier) { url, error in + if let url = url as? URL { + selectShareDirectory(result: .success(url)) + } + if let error = error { + selectShareDirectory(result: .failure(error)) + } + } + return true + } } ForEach(config.drives.filter { $0.isExternal }) { drive in HStack { @@ -128,12 +145,25 @@ struct VMRemovableDrivesView: View { .lineLimit(1) .truncationMode(.tail) .foregroundColor(.secondary) - }.fileImporter(isPresented: $diskImageFileImportPresented, allowedContentTypes: [.data]) { result in + }.fileImporter(isPresented: $diskImageFileImportPresented, allowedContentTypes: [Self.diskImageUTType]) { result in if let currentDrive = self.currentDrive { selectRemovableImage(forDrive: currentDrive, result: result) self.currentDrive = nil } } + .onDrop(of: [Self.diskImageUTType], isTargeted: nil) { providers in + guard let item = providers.first, item.hasItemConformingToTypeIdentifier(Self.diskImageUTType.identifier) else { return false } + + item.loadItem(forTypeIdentifier: Self.diskImageUTType.identifier) { url, error in + if let url = url as? URL{ + selectRemovableImage(forDrive: drive, result: .success(url)) + } + if let error { + selectRemovableImage(forDrive: drive, result: .failure(error)) + } + } + return true + } } } } diff --git a/Services/UTMQemuVirtualMachine.swift b/Services/UTMQemuVirtualMachine.swift index 122acac91..7d8c824e1 100644 --- a/Services/UTMQemuVirtualMachine.swift +++ b/Services/UTMQemuVirtualMachine.swift @@ -792,10 +792,10 @@ extension UTMQemuVirtualMachine { try await eject(drive, isForced: true) let file = try UTMRegistryEntry.File(url: url, isReadOnly: drive.isReadOnly) await registryEntry.setExternalDrive(file, forId: drive.id) - try await changeMedium(drive, with: tempBookmark, url: url, isSecurityScoped: false, isAccessOnly: isAccessOnly) + try await changeMedium(drive, with: tempBookmark, isSecurityScoped: false, isAccessOnly: isAccessOnly) } - private func changeMedium(_ drive: UTMQemuConfigurationDrive, with bookmark: Data, url: URL?, isSecurityScoped: Bool, isAccessOnly: Bool) async throws { + private func changeMedium(_ drive: UTMQemuConfigurationDrive, with bookmark: Data, isSecurityScoped: Bool, isAccessOnly: Bool) async throws { let system = await system ?? UTMProcess() let (success, bookmark, path) = await system.accessData(withBookmark: bookmark, securityScoped: isSecurityScoped) guard let bookmark = bookmark, let path = path, success else { @@ -818,7 +818,7 @@ extension UTMQemuVirtualMachine { let id = drive.id if let bookmark = await registryEntry.externalDrives[id]?.remoteBookmark { // an image bookmark was saved while QEMU was running - try await changeMedium(drive, with: bookmark, url: nil, isSecurityScoped: true, isAccessOnly: !isMounting) + try await changeMedium(drive, with: bookmark, isSecurityScoped: true, isAccessOnly: !isMounting) } else if let localBookmark = await registryEntry.externalDrives[id]?.bookmark { // an image bookmark was saved while QEMU was NOT running let url = try URL(resolvingPersistentBookmarkData: localBookmark)