UTM/Platform/Shared/VMContextMenuModifier.swift

156 lines
6.3 KiB
Swift

//
// 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
struct VMContextMenuModifier: ViewModifier {
@ObservedObject var vm: VMData
@EnvironmentObject private var data: UTMData
@State private var showSharePopup = false
@State private var confirmAction: ConfirmAction?
@State private var shareItem: VMShareItemModifier.ShareItem?
func body(content: Content) -> some View {
content.contextMenu {
#if os(macOS)
Button {
NSWorkspace.shared.activateFileViewerSelecting([vm.pathUrl])
} label: {
Label("Show in Finder", systemImage: "folder")
}.help("Reveal where the VM is stored.")
Divider()
#endif
Button {
data.close(vm: vm) // close window
data.edit(vm: vm)
} label: {
Label("Edit", systemImage: "slider.horizontal.3")
}.disabled(vm.hasSuspendState || !vm.isModifyAllowed)
.help("Modify settings for this VM.")
if vm.hasSuspendState || !vm.isStopped {
Button {
confirmAction = .confirmStopVM
} label: {
Label("Stop", systemImage: "stop.fill")
}.help("Stop the running VM.")
} else if !vm.isModifyAllowed { // paused
Button {
data.run(vm: vm)
} label: {
Label("Resume", systemImage: "playpause.fill")
}.help("Resume running VM.")
} else {
Divider()
Button {
data.run(vm: vm)
} label: {
Label("Run", systemImage: "play.fill")
}.help("Run the VM in the foreground.")
#if os(macOS) && arch(arm64)
if #available(macOS 13, *), let appleConfig = vm.config as? UTMAppleConfiguration, appleConfig.system.boot.operatingSystem == .macOS {
Button {
data.run(vm: vm, options: .bootRecovery)
} label: {
Label("Run Recovery", systemImage: "play.fill")
}.help("Boot into recovery mode.")
}
#endif
if let _ = vm.wrapped as? UTMQemuVirtualMachine {
Button {
data.run(vm: vm, options: .bootDisposibleMode)
} label: {
Label("Run without saving changes", systemImage: "play")
}.help("Run the VM in the foreground, without saving data changes to disk.")
}
#if os(iOS)
if let qemuConfig = vm.config as? UTMQemuConfiguration {
Button {
qemuConfig.qemu.isGuestToolsInstallRequested = true
} label: {
Label("Install Windows Guest Tools…", systemImage: "wrench.and.screwdriver")
}.help("Download and mount the guest tools for Windows.")
.disabled(qemuConfig.qemu.isGuestToolsInstallRequested)
}
#endif
Divider()
}
Button {
shareItem = .utmCopy(vm)
showSharePopup.toggle()
} label: {
Label("Share…", systemImage: "square.and.arrow.up")
}.help("Share a copy of this VM and all its data.")
#if os(macOS)
if !vm.isShortcut {
Button {
confirmAction = .confirmMoveVM
} label: {
Label("Move…", systemImage: "arrow.down.doc")
}.disabled(!vm.isModifyAllowed)
.help("Move this VM from internal storage to elsewhere.")
}
#endif
Button {
confirmAction = .confirmCloneVM
} label: {
Label("Clone…", systemImage: "doc.on.doc")
}.help("Duplicate this VM along with all its data.")
Button {
data.busyWorkAsync {
try await data.template(vm: vm)
}
} label: {
Label("New from template…", systemImage: "doc.on.clipboard")
}.help("Create a new VM with the same configuration as this one but without any data.")
Divider()
if vm.isShortcut {
DestructiveButton {
confirmAction = .confirmDeleteShortcut
} label: {
Label("Remove", systemImage: "trash")
}.disabled(!vm.isModifyAllowed)
.help("Delete this shortcut. The underlying data will not be deleted.")
} else {
DestructiveButton {
confirmAction = .confirmDeleteVM
} label: {
Label("Delete", systemImage: "trash")
}.disabled(!vm.isModifyAllowed)
.help("Delete this VM and all its data.")
}
}
.modifier(VMShareItemModifier(isPresented: $showSharePopup, shareItem: shareItem))
.modifier(VMConfirmActionModifier(vm: vm, confirmAction: $confirmAction) {
if confirmAction == .confirmMoveVM {
shareItem = .utmMove(vm)
showSharePopup.toggle()
}
})
.onChange(of: (vm.config as? UTMQemuConfiguration)?.qemu.isGuestToolsInstallRequested) { newValue in
if newValue == true {
data.busyWorkAsync {
try await data.mountSupportTools(for: vm.wrapped as! UTMQemuVirtualMachine)
}
}
}
}
}