home: indicate busy status in card view

Show a spinner when VM is loading and hide start button when VM is started.
This will give a better visual cue of which VM is started. Additionally,
add a double-click handler to easily open/start a VM.

Resolves #3885
This commit is contained in:
osy 2022-05-02 11:05:53 -07:00
parent 29120ac29f
commit db40dc50af
3 changed files with 88 additions and 7 deletions

View File

@ -48,16 +48,25 @@ struct VMCardView: View {
}.lineLimit(1)
.truncationMode(.tail)
Spacer()
Button {
data.run(vm: vm)
} label: {
Label("Run", systemImage: "play.circle")
.font(.largeTitle)
.foregroundColor(buttonColor)
.labelStyle(.iconOnly)
if vm.state == .vmStopped {
Button {
data.run(vm: vm)
} label: {
Label("Run", systemImage: "play.circle")
.font(.largeTitle)
.foregroundColor(buttonColor)
.labelStyle(.iconOnly)
}
} else if vm.isBusy {
BigWhiteSpinner()
}
}.padding([.top, .bottom], 10)
.buttonStyle(.plain)
#if os(macOS)
.onDoubleClick {
data.run(vm: vm)
}
#endif
}
}

View File

@ -0,0 +1,68 @@
//
// Copyright © 2022 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
// from https://gist.github.com/joelekstrom/91dad79ebdba409556dce663d28e8297
extension View {
/// Adds a double click handler this view (macOS only)
///
/// Example
/// ```
/// Text("Hello")
/// .onDoubleClick { print("Double click detected") }
/// ```
/// - Parameters:
/// - handler: Block invoked when a double click is detected
func onDoubleClick(handler: @escaping () -> Void) -> some View {
modifier(DoubleClickHandler(handler: handler))
}
}
struct DoubleClickHandler: ViewModifier {
let handler: () -> Void
func body(content: Content) -> some View {
content.overlay(DoubleClickListeningViewRepresentable(handler: handler))
}
}
struct DoubleClickListeningViewRepresentable: NSViewRepresentable {
let handler: () -> Void
func makeNSView(context: Context) -> DoubleClickListeningView {
DoubleClickListeningView(handler: handler)
}
func updateNSView(_ nsView: DoubleClickListeningView, context: Context) {}
}
class DoubleClickListeningView: NSView {
let handler: () -> Void
init(handler: @escaping () -> Void) {
self.handler = handler
super.init(frame: .zero)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func mouseDown(with event: NSEvent) {
super.mouseDown(with: event)
if event.clickCount == 2 {
handler()
}
}
}

View File

@ -1119,6 +1119,7 @@
CEA905C92603DA0D00801E7C /* VMDisplayMetalViewController+USB.m in Sources */ = {isa = PBXBuildFile; fileRef = CEA905C82603DA0D00801E7C /* VMDisplayMetalViewController+USB.m */; };
CEA905CD2603DBFB00801E7C /* VMUSBDevicesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEA905CC2603DBFB00801E7C /* VMUSBDevicesView.xib */; };
CEA905D82603DC5300801E7C /* VMUSBDevicesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA905D72603DC5300801E7C /* VMUSBDevicesViewController.swift */; };
CEB20EEA282053320033EFB5 /* DoubleClickHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB20EE9282053320033EFB5 /* DoubleClickHandler.swift */; };
CEB63A7624F4654400CAF323 /* Main.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB63A7524F4654400CAF323 /* Main.swift */; };
CEB63A7724F4654400CAF323 /* Main.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB63A7524F4654400CAF323 /* Main.swift */; };
CEB63A7A24F469E300CAF323 /* UTMJailbreak.m in Sources */ = {isa = PBXBuildFile; fileRef = CEB63A7924F469E300CAF323 /* UTMJailbreak.m */; };
@ -2100,6 +2101,7 @@
CEA905C82603DA0D00801E7C /* VMDisplayMetalViewController+USB.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "VMDisplayMetalViewController+USB.m"; sourceTree = "<group>"; };
CEA905CC2603DBFB00801E7C /* VMUSBDevicesView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = VMUSBDevicesView.xib; sourceTree = "<group>"; };
CEA905D72603DC5300801E7C /* VMUSBDevicesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMUSBDevicesViewController.swift; sourceTree = "<group>"; };
CEB20EE9282053320033EFB5 /* DoubleClickHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoubleClickHandler.swift; sourceTree = "<group>"; };
CEB63A7524F4654400CAF323 /* Main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Main.swift; sourceTree = "<group>"; };
CEB63A7824F468BA00CAF323 /* UTMJailbreak.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UTMJailbreak.h; sourceTree = "<group>"; };
CEB63A7924F469E300CAF323 /* UTMJailbreak.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UTMJailbreak.m; sourceTree = "<group>"; };
@ -2583,6 +2585,7 @@
children = (
CE1BD9FA24F4825C0022A468 /* Display */,
CEECE13B25E47D9500A2AAB8 /* AppDelegate.swift */,
CEB20EE9282053320033EFB5 /* DoubleClickHandler.swift */,
CEEC811A24E48EC600ACB0B3 /* SettingsView.swift */,
CEBBF1A624B5730F00C15049 /* UTMDataExtension.swift */,
CE93759524BB7E9F0074066F /* UTMTabViewController.swift */,
@ -4086,6 +4089,7 @@
CE0B6CF624AD568400FE012D /* UTMQemuConfiguration+Drives.m in Sources */,
CE0B6D4324AD584C00FE012D /* qapi-types.c in Sources */,
CE0B6D0824AD56C300FE012D /* qapi-visit-core.c in Sources */,
CEB20EEA282053320033EFB5 /* DoubleClickHandler.swift in Sources */,
CE0B6D2024AD57FC00FE012D /* qapi-commands-dump.c in Sources */,
CE0B6D5224AD584C00FE012D /* qapi-visit-sockets.c in Sources */,
CE0B6D7F24AD584D00FE012D /* qapi-events-qom.c in Sources */,