From 556438152ad09c1d9016c837997b2e03e5911e00 Mon Sep 17 00:00:00 2001 From: Michael Fey Date: Tue, 10 Mar 2026 19:38:39 -0400 Subject: [PATCH] Add launch action to VM installer completion screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added a dedicated installer completion view with a primary launch action Replace the inline done-state progress UI with InstallProgressDoneView and add a "Let’s Go!" button that launches the newly created VM and dismisses the installer. Use a hidden default-action button to preserve the Return key shortcut without letting macOS override the visible button’s custom text color. --- .../Steps/InstallProgressDoneView.swift | 70 +++++++++++++++++++ .../InstallProgressDisplayView.swift | 8 +-- .../Installer/VMInstallationWizard.swift | 3 +- .../VirtualMachineSessionUIManager.swift | 1 + 4 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 VirtualUI/Source/Installer/Steps/InstallProgressDoneView.swift diff --git a/VirtualUI/Source/Installer/Steps/InstallProgressDoneView.swift b/VirtualUI/Source/Installer/Steps/InstallProgressDoneView.swift new file mode 100644 index 00000000..4f0aeddd --- /dev/null +++ b/VirtualUI/Source/Installer/Steps/InstallProgressDoneView.swift @@ -0,0 +1,70 @@ +// +// InstallProgressDoneView.swift +// VirtualBuddy +// +// Created by Michael Fey on 3/10/26. +// + + +import SwiftUI +import VirtualCore +import BuddyKit + +struct InstallProgressDoneView: View { + @EnvironmentObject private var viewModel: VMInstallationViewModel + @EnvironmentObject private var library: VMLibraryController + @EnvironmentObject private var sessionManager: VirtualMachineSessionUIManager + @Environment(\.dismiss) private var dismiss + + var body: some View { + VStack(spacing: 0) { + Spacer(minLength: 0) + + VStack(spacing: 18) { + VirtualBuddyMonoIcon(style: .success) + + Text(viewModel.data.systemType.installFinishedMessage) + .font(.subheadline) + + if let machine = viewModel.machine { + launchButton(for: machine) + .padding(.top, 10) + .padding(.bottom, 6) + } + } + .monospacedDigit() + .multilineTextAlignment(.center) + .foregroundStyle(.green) + .tint(.green) + + Spacer(minLength: 56) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + + private func launchButton(for machine: VBVirtualMachine) -> some View { + ZStack { + Button("Let\u{2019}s Go!") { + launch(machine) + } + .foregroundStyle(Color(white: 0.03)) + .controlSize(.large) + .accessibilityHint("Launches the new virtual machine") + + // This is a hidden button that exists purely to accept the keyboardShortcut(.defaultAction) for this window. Setting a default action for a button overrides the foreground styling on the button leaving white text on a lighter green background, which is difficult to read. + Button("") { + launch(machine) + } + .keyboardShortcut(.defaultAction) + .labelsHidden() + .frame(width: 0, height: 0) + .opacity(0) + .accessibilityHidden(true) + } + } + + private func launch(_ machine: VBVirtualMachine) { + sessionManager.launch(machine, library: library, options: nil) + dismiss() + } +} diff --git a/VirtualUI/Source/Installer/Steps/Restore Image Selection/Components/InstallProgressDisplayView.swift b/VirtualUI/Source/Installer/Steps/Restore Image Selection/Components/InstallProgressDisplayView.swift index 06d3d4b2..2fc003e4 100644 --- a/VirtualUI/Source/Installer/Steps/Restore Image Selection/Components/InstallProgressDisplayView.swift +++ b/VirtualUI/Source/Installer/Steps/Restore Image Selection/Components/InstallProgressDisplayView.swift @@ -4,6 +4,9 @@ import BuddyKit struct InstallProgressDisplayView: View { @EnvironmentObject private var viewModel: VMInstallationViewModel + @EnvironmentObject private var library: VMLibraryController + @EnvironmentObject private var sessionManager: VirtualMachineSessionUIManager + @Environment(\.dismiss) private var dismiss var body: some View { VirtualDisplayView { @@ -14,10 +17,7 @@ struct InstallProgressDisplayView: View { case .install: InstallProgressStepView() case .done: - VirtualBuddyMonoProgressView( - status: Text(viewModel.data.systemType.installFinishedMessage), - style: .success - ) + InstallProgressDoneView() default: EmptyView() } diff --git a/VirtualUI/Source/Installer/VMInstallationWizard.swift b/VirtualUI/Source/Installer/VMInstallationWizard.swift index 1acd9e1e..012d5459 100644 --- a/VirtualUI/Source/Installer/VMInstallationWizard.swift +++ b/VirtualUI/Source/Installer/VMInstallationWizard.swift @@ -106,7 +106,6 @@ public struct VMInstallationWizard: View { Button("Done") { closeWindow() } - .keyboardShortcut(.defaultAction) } } @@ -299,6 +298,8 @@ extension VMInstallationWizard { static func preview(step: VMInstallationStep) -> some View { VMInstallationWizard(library: .preview, initialStep: step) .frame(width: 900) + .environmentObject(VMLibraryController.preview) + .environmentObject(VirtualMachineSessionUIManager.shared) } @ViewBuilder diff --git a/VirtualUI/Source/Session/VirtualMachineSessionUIManager.swift b/VirtualUI/Source/Session/VirtualMachineSessionUIManager.swift index 0988e215..8dcd1e49 100644 --- a/VirtualUI/Source/Session/VirtualMachineSessionUIManager.swift +++ b/VirtualUI/Source/Session/VirtualMachineSessionUIManager.swift @@ -89,6 +89,7 @@ public final class VirtualMachineSessionUIManager: ObservableObject { openWindow(animationBehavior: .documentWindow) { VMInstallationWizard(library: library, restoringAt: restoreURL) .environmentObject(library) + .environmentObject(self) } }