import SwiftUI // MARK: - CommandMenu struct XcodeCommands: Commands { // CommandMenus don't participate in the environment hierarchy, so we need to shuffle AppState along to the individual Commands manually. let appState: AppState var body: some Commands { CommandMenu("Xcode") { Group { InstallCommand() Divider() SelectCommand() LaunchCommand() RevealCommand() CopyPathCommand() } .environmentObject(appState) } } } // MARK: - Buttons // These are used for both context menus and commands struct InstallButton: View { @EnvironmentObject var appState: AppState let xcode: Xcode? var body: some View { Button(action: uninstallOrInstall) { if let xcode = xcode { Text(xcode.installed == true ? "Uninstall" : "Install") } else { Text("Install") } } } private func uninstallOrInstall() { guard let xcode = xcode else { return } if xcode.installed { appState.xcodeBeingConfirmedForUninstallation = xcode } else { appState.install(id: xcode.id) } } } struct SelectButton: View { @EnvironmentObject var appState: AppState let xcode: Xcode? var body: some View { Button(action: select) { Text("Select") } } private func select() { guard let xcode = xcode else { return } appState.select(id: xcode.id) } } struct LaunchButton: View { @EnvironmentObject var appState: AppState let xcode: Xcode? var body: some View { Button(action: launch) { Text("Launch") } } private func launch() { guard let xcode = xcode else { return } appState.launch(id: xcode.id) } } struct RevealButton: View { @EnvironmentObject var appState: AppState let xcode: Xcode? var body: some View { Button(action: reveal) { Text("Reveal in Finder") } } private func reveal() { guard let xcode = xcode else { return } appState.reveal(id: xcode.id) } } struct CopyPathButton: View { @EnvironmentObject var appState: AppState let xcode: Xcode? var body: some View { Button(action: copyPath) { Text("Copy Path") } } private func copyPath() { guard let xcode = xcode else { return } appState.copyPath(id: xcode.id) } } // MARK: - Commands struct InstallCommand: View { @EnvironmentObject var appState: AppState @FocusedValue(\.selectedXcode) private var selectedXcode: SelectedXcode? var body: some View { InstallButton(xcode: selectedXcode.unwrapped) .keyboardShortcut(selectedXcode.unwrapped?.installed == true ? "u" : "i", modifiers: [.command, .option]) .disabled(selectedXcode.unwrapped == nil) } } struct SelectCommand: View { @EnvironmentObject var appState: AppState @FocusedValue(\.selectedXcode) private var selectedXcode: SelectedXcode? var body: some View { SelectButton(xcode: selectedXcode.unwrapped) .keyboardShortcut("s", modifiers: [.command, .option]) .disabled(selectedXcode.unwrapped?.installed != true) } } struct LaunchCommand: View { @EnvironmentObject var appState: AppState @FocusedValue(\.selectedXcode) private var selectedXcode: SelectedXcode? var body: some View { LaunchButton(xcode: selectedXcode.unwrapped) .keyboardShortcut(KeyboardShortcut(.downArrow, modifiers: .command)) .disabled(selectedXcode.unwrapped?.installed != true) } } struct RevealCommand: View { @EnvironmentObject var appState: AppState @FocusedValue(\.selectedXcode) private var selectedXcode: SelectedXcode? var body: some View { RevealButton(xcode: selectedXcode.unwrapped) .keyboardShortcut("r", modifiers: [.command, .option]) .disabled(selectedXcode.unwrapped?.installed != true) } } struct CopyPathCommand: View { @EnvironmentObject var appState: AppState @FocusedValue(\.selectedXcode) private var selectedXcode: SelectedXcode? var body: some View { CopyPathButton(xcode: selectedXcode.unwrapped) .keyboardShortcut("c", modifiers: [.command, .option]) .disabled(selectedXcode.unwrapped?.installed != true) } }