mirror of
https://github.com/XcodesOrg/XcodesApp.git
synced 2026-03-25 08:55:46 +00:00
Merge pull request #461 from XcodesOrg/matt/redesign
Redesign the main window to better suit more data
This commit is contained in:
commit
3c5f86023e
22 changed files with 520 additions and 223 deletions
|
|
@ -114,8 +114,9 @@
|
|||
E689540325BE8C64000EBCEA /* DockProgress in Frameworks */ = {isa = PBXBuildFile; productRef = E689540225BE8C64000EBCEA /* DockProgress */; };
|
||||
E81D7EA02805250100A205FC /* Collection+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E81D7E9F2805250100A205FC /* Collection+.swift */; };
|
||||
E832EAF82B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E832EAF72B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift */; };
|
||||
E84CF8C12B0FEB8300ECA259 /* RuntimesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E84CF8C02B0FEB8300ECA259 /* RuntimesView.swift */; };
|
||||
E872EE4E2808D4F100D3DD8B /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E872EE502808D4F100D3DD8B /* Localizable.strings */; };
|
||||
E84B7D0D2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E84B7D0C2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift */; };
|
||||
E84E4F522B323A5F003F3959 /* CornerRadiusModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E84E4F512B323A5F003F3959 /* CornerRadiusModifier.swift */; };
|
||||
E86671272B309D2F0048559A /* PlatformsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E86671262B309D2F0048559A /* PlatformsView.swift */; };
|
||||
E87AB3C52939B65E00D72F43 /* Hardware.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87AB3C42939B65E00D72F43 /* Hardware.swift */; };
|
||||
E87DD6EB25D053FA00D86808 /* Progress+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87DD6EA25D053FA00D86808 /* Progress+.swift */; };
|
||||
E89342FA25EDCC17007CF557 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E89342F925EDCC17007CF557 /* NotificationManager.swift */; };
|
||||
|
|
@ -310,9 +311,10 @@
|
|||
CAFFFEEE259CEAC400903F81 /* RingProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RingProgressViewStyle.swift; sourceTree = "<group>"; };
|
||||
E81D7E9F2805250100A205FC /* Collection+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+.swift"; sourceTree = "<group>"; };
|
||||
E832EAF72B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeInstallationStepDetailView.swift; sourceTree = "<group>"; };
|
||||
E84CF8C02B0FEB8300ECA259 /* RuntimesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimesView.swift; sourceTree = "<group>"; };
|
||||
E84B7D0C2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSplitViewWrapper.swift; sourceTree = "<group>"; };
|
||||
E84E4F512B323A5F003F3959 /* CornerRadiusModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CornerRadiusModifier.swift; sourceTree = "<group>"; };
|
||||
E856BB73291EDD3D00DC438B /* XcodesKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = XcodesKit; path = Xcodes/XcodesKit; sourceTree = "<group>"; };
|
||||
E872EE4F2808D4F100D3DD8B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
E86671262B309D2F0048559A /* PlatformsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformsView.swift; sourceTree = "<group>"; };
|
||||
E87AB3C42939B65E00D72F43 /* Hardware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Hardware.swift; sourceTree = "<group>"; };
|
||||
E87DD6EA25D053FA00D86808 /* Progress+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Progress+.swift"; sourceTree = "<group>"; };
|
||||
E89342F925EDCC17007CF557 /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -373,6 +375,7 @@
|
|||
CA452BAF259FD9770072DFA4 /* ProgressIndicator.swift */,
|
||||
536CFDD3263C9A8000026CE0 /* XcodesSheet.swift */,
|
||||
53CBAB2B263DCC9100410495 /* XcodesAlert.swift */,
|
||||
E84B7D0C2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift */,
|
||||
);
|
||||
path = Common;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -646,7 +649,8 @@
|
|||
B0C6AD0A2AD9178E00E64698 /* IdenticalBuildView.swift */,
|
||||
B0C6AD0C2AD91D7900E64698 /* IconView.swift */,
|
||||
E832EAF72B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift */,
|
||||
E84CF8C02B0FEB8300ECA259 /* RuntimesView.swift */,
|
||||
E86671262B309D2F0048559A /* PlatformsView.swift */,
|
||||
E84E4F512B323A5F003F3959 /* CornerRadiusModifier.swift */,
|
||||
);
|
||||
path = InfoPane;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -868,7 +872,6 @@
|
|||
CA9FF8CF25959A9700E47BAF /* HelperXPCShared.swift in Sources */,
|
||||
CA735109257BF96D00EA9CF8 /* AttributedText.swift in Sources */,
|
||||
CAFBDC4E2599B33D003DCC5A /* MainToolbar.swift in Sources */,
|
||||
E84CF8C12B0FEB8300ECA259 /* RuntimesView.swift in Sources */,
|
||||
CA11E7BA2598476C00D2EE1C /* XcodeCommands.swift in Sources */,
|
||||
CAA8589B25A2B83000ACF8C0 /* Aria2CError.swift in Sources */,
|
||||
536CFDD2263C94DE00026CE0 /* SignedInView.swift in Sources */,
|
||||
|
|
@ -897,6 +900,7 @@
|
|||
B0403CFC2AD9A6BF00137C09 /* InstalledStateButtons.swift in Sources */,
|
||||
36741BFF291E50F500A85AAE /* FileError.swift in Sources */,
|
||||
CA9FF8872595607900E47BAF /* InstalledXcode.swift in Sources */,
|
||||
E84E4F522B323A5F003F3959 /* CornerRadiusModifier.swift in Sources */,
|
||||
B0403CF22AD934B600137C09 /* CompatibilityView.swift in Sources */,
|
||||
B0403CFE2ADA712C00137C09 /* InfoPaneControls.swift in Sources */,
|
||||
53CBAB2C263DCC9100410495 /* XcodesAlert.swift in Sources */,
|
||||
|
|
@ -910,6 +914,7 @@
|
|||
CABFA9C12592EEEA00380FEE /* Version+.swift in Sources */,
|
||||
E8D655C0288DD04700A139C2 /* SelectedActionType.swift in Sources */,
|
||||
36741BFD291E4FDB00A85AAE /* DownloadPreferencePane.swift in Sources */,
|
||||
E86671272B309D2F0048559A /* PlatformsView.swift in Sources */,
|
||||
CA9FF8522595080100E47BAF /* AcknowledgementsView.swift in Sources */,
|
||||
CABFA9CE2592EEEA00380FEE /* Version+Xcode.swift in Sources */,
|
||||
CAFBDB912598FE80003DCC5A /* SelectedXcode.swift in Sources */,
|
||||
|
|
@ -921,6 +926,7 @@
|
|||
E8B20CBF2A2EDEC20057D816 /* SDKs+Xcode.swift in Sources */,
|
||||
CA9FF877259528CC00E47BAF /* Version+XcodeReleases.swift in Sources */,
|
||||
CABFAA2D2592FBFC00380FEE /* Configure.swift in Sources */,
|
||||
E84B7D0D2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift in Sources */,
|
||||
CA73510D257BFCEF00EA9CF8 /* NSAttributedString+.swift in Sources */,
|
||||
CAFBDB952598FE96003DCC5A /* FocusedValues.swift in Sources */,
|
||||
B0403CF42AD9381D00137C09 /* SDKsView.swift in Sources */,
|
||||
|
|
@ -1040,7 +1046,7 @@
|
|||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
|
|
@ -1227,7 +1233,7 @@
|
|||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
|
|
@ -1285,7 +1291,7 @@
|
|||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = macosx;
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ extension AppState {
|
|||
Task {
|
||||
do {
|
||||
let runtimes = try await self.runtimeService.localInstalledRuntimes()
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.installedRuntimes = runtimes
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,7 +163,6 @@ class AppState: ObservableObject {
|
|||
checkIfHelperIsInstalled()
|
||||
setupAutoInstallTimer()
|
||||
setupDefaults()
|
||||
updateInstalledRuntimes()
|
||||
}
|
||||
|
||||
func setupDefaults() {
|
||||
|
|
@ -410,10 +409,7 @@ class AppState: ObservableObject {
|
|||
|
||||
// Check to see if users MacOS is supported
|
||||
if let requiredMacOSVersion = availableXcode.requiredMacOSVersion {
|
||||
let split = requiredMacOSVersion.components(separatedBy: ".").compactMap { Int($0) }
|
||||
let xcodeMinimumMacOSVersion = OperatingSystemVersion(majorVersion: split[safe: 0] ?? 0, minorVersion: split[safe: 1] ?? 0, patchVersion: split[safe: 2] ?? 0)
|
||||
|
||||
if !ProcessInfo.processInfo.isOperatingSystemAtLeast(xcodeMinimumMacOSVersion) {
|
||||
if hasMinSupportedOS(requiredMacOSVersion: requiredMacOSVersion) {
|
||||
// prompt
|
||||
self.presentedAlert = .checkMinSupportedVersion(xcode: availableXcode, macOS: ProcessInfo.processInfo.operatingSystemVersion.versionString())
|
||||
return
|
||||
|
|
@ -428,6 +424,13 @@ class AppState: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
func hasMinSupportedOS(requiredMacOSVersion: String) -> Bool {
|
||||
let split = requiredMacOSVersion.components(separatedBy: ".").compactMap { Int($0) }
|
||||
let xcodeMinimumMacOSVersion = OperatingSystemVersion(majorVersion: split[safe: 0] ?? 0, minorVersion: split[safe: 1] ?? 0, patchVersion: split[safe: 2] ?? 0)
|
||||
|
||||
return !ProcessInfo.processInfo.isOperatingSystemAtLeast(xcodeMinimumMacOSVersion)
|
||||
}
|
||||
|
||||
func install(id: Xcode.ID) {
|
||||
guard let availableXcode = availableXcodes.first(where: { $0.version == id }) else { return }
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
import Foundation
|
||||
import struct XCModel.SDKs
|
||||
import XcodesKit
|
||||
import SwiftUI
|
||||
|
||||
extension SDKs {
|
||||
/// Loops through all SDK's and returns an array of buildNumbers (to be used to correlate runtimes)
|
||||
|
|
@ -33,3 +35,24 @@ extension SDKs {
|
|||
return buildNumbers
|
||||
}
|
||||
}
|
||||
|
||||
extension DownloadableRuntime {
|
||||
func icon() -> Image {
|
||||
switch self.platform {
|
||||
case .iOS:
|
||||
return Image(systemName: "iphone")
|
||||
case .macOS:
|
||||
return Image(systemName: "macwindow")
|
||||
case .watchOS:
|
||||
return Image(systemName: "applewatch")
|
||||
case .tvOS:
|
||||
return Image(systemName: "appletv")
|
||||
case .visionOS:
|
||||
if #available(macOS 14, *) {
|
||||
return Image(systemName: "visionpro")
|
||||
} else {
|
||||
return Image(systemName: "eyeglasses")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,9 +61,10 @@ struct CancelInstallButton: View {
|
|||
|
||||
var body: some View {
|
||||
Button(action: cancelInstall) {
|
||||
Text("Cancel")
|
||||
.help(localizeString("StopInstallation"))
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
}
|
||||
.help(localizeString("StopInstallation"))
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
|
||||
private func cancelInstall() {
|
||||
|
|
@ -78,9 +79,9 @@ struct CancelRuntimeInstallButton: View {
|
|||
|
||||
var body: some View {
|
||||
Button(action: cancelInstall) {
|
||||
Text("Cancel")
|
||||
.help(localizeString("StopInstallation"))
|
||||
}
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
}.help(localizeString("StopInstallation"))
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
|
||||
private func cancelInstall() {
|
||||
|
|
|
|||
46
Xcodes/Frontend/Common/NavigationSplitViewWrapper.swift
Normal file
46
Xcodes/Frontend/Common/NavigationSplitViewWrapper.swift
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// NavigationSplitViewWrapper.swift
|
||||
// Xcodes
|
||||
//
|
||||
// Created by Matt Kiazyk on 2023-12-12.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct NavigationSplitViewWrapper<Sidebar, Detail>: View where Sidebar: View, Detail: View {
|
||||
private var sidebar: Sidebar
|
||||
private var detail: Detail
|
||||
|
||||
init(
|
||||
@ViewBuilder sidebar: () -> Sidebar,
|
||||
@ViewBuilder detail: () -> Detail
|
||||
) {
|
||||
self.sidebar = sidebar()
|
||||
self.detail = detail()
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if #available(iOS 16, macOS 13, tvOS 16, watchOS 9, visionOS 1, *) {
|
||||
// Use the latest API available
|
||||
NavigationSplitView {
|
||||
|
||||
if #available(macOS 14, *) {
|
||||
sidebar
|
||||
.toolbar(removing: .sidebarToggle)
|
||||
} else {
|
||||
sidebar
|
||||
}
|
||||
} detail: {
|
||||
detail
|
||||
}
|
||||
} else {
|
||||
// Alternative code for earlier versions of OS.
|
||||
NavigationView {
|
||||
// The first column is the sidebar.
|
||||
sidebar
|
||||
detail
|
||||
}
|
||||
.navigationViewStyle(.columns)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,16 +9,27 @@
|
|||
import SwiftUI
|
||||
|
||||
struct CompatibilityView: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
|
||||
let requiredMacOSVersion: String?
|
||||
|
||||
var body: some View {
|
||||
if let requiredMacOSVersion = requiredMacOSVersion {
|
||||
VStack(alignment: .leading) {
|
||||
Text("Compatibility")
|
||||
.font(.headline)
|
||||
Text(String(format: localizeString("MacOSRequirement"), requiredMacOSVersion))
|
||||
.font(.subheadline)
|
||||
HStack(alignment: .top){
|
||||
VStack(alignment: .leading) {
|
||||
Text("Compatibility")
|
||||
.font(.headline)
|
||||
Text(String(format: localizeString("MacOSRequirement"), requiredMacOSVersion))
|
||||
.font(.subheadline)
|
||||
.foregroundColor(appState.hasMinSupportedOS(requiredMacOSVersion: requiredMacOSVersion) ? .red : .primary)
|
||||
}
|
||||
Spacer()
|
||||
if appState.hasMinSupportedOS(requiredMacOSVersion: requiredMacOSVersion) {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.font(.largeTitle)
|
||||
}
|
||||
}
|
||||
.xcodesBackground()
|
||||
} else {
|
||||
EmptyView()
|
||||
}
|
||||
|
|
@ -28,4 +39,5 @@ struct CompatibilityView: View {
|
|||
#Preview {
|
||||
CompatibilityView(requiredMacOSVersion: "10.15.4")
|
||||
.padding()
|
||||
.environmentObject(AppState())
|
||||
}
|
||||
|
|
|
|||
35
Xcodes/Frontend/InfoPane/CornerRadiusModifier.swift
Normal file
35
Xcodes/Frontend/InfoPane/CornerRadiusModifier.swift
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// CornerRadiusModifier.swift
|
||||
// Xcodes
|
||||
//
|
||||
// Created by Matt Kiazyk on 2023-12-19.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct CornerRadiusModifier: ViewModifier {
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding()
|
||||
.background(.background)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous))
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func xcodesBackground() -> some View {
|
||||
self.modifier(
|
||||
CornerRadiusModifier()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct Previews_CornerRadius_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
HStack {
|
||||
Text("XCODES RULES!")
|
||||
}.xcodesBackground()
|
||||
}
|
||||
}
|
||||
|
|
@ -33,6 +33,7 @@ struct IdenticalBuildsView: View {
|
|||
.font(.subheadline)
|
||||
}
|
||||
}
|
||||
.xcodesBackground()
|
||||
.accessibilityElement()
|
||||
.accessibility(label: Text("IdenticalBuilds"))
|
||||
.accessibility(value: Text(accessibilityDescription))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import AppKit
|
||||
import XcodesKit
|
||||
import Path
|
||||
import SwiftUI
|
||||
import Version
|
||||
|
|
@ -7,44 +8,65 @@ import struct XCModel.SDKs
|
|||
|
||||
struct InfoPane: View {
|
||||
let xcode: Xcode
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
IconView(installState: xcode.installState)
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
|
||||
Text(verbatim: "Xcode \(xcode.description) \(xcode.version.buildMetadataIdentifiersDisplay)")
|
||||
.font(.title)
|
||||
|
||||
InfoPaneControls(xcode: xcode)
|
||||
|
||||
Divider()
|
||||
|
||||
Group {
|
||||
RuntimesView(xcode: xcode)
|
||||
ReleaseDateView(date: xcode.releaseDate)
|
||||
ReleaseNotesView(url: xcode.releaseNotesURL)
|
||||
IdenticalBuildsView(builds: xcode.identicalBuilds)
|
||||
CompatibilityView(requiredMacOSVersion: xcode.requiredMacOSVersion)
|
||||
SDKsView(sdks: xcode.sdks)
|
||||
CompilersView(compilers: xcode.compilers)
|
||||
ScrollView(.vertical) {
|
||||
HStack(alignment: .top) {
|
||||
VStack {
|
||||
VStack(spacing: 5) {
|
||||
HStack {
|
||||
IconView(installState: xcode.installState)
|
||||
|
||||
Text(verbatim: "Xcode \(xcode.description) \(xcode.version.buildMetadataIdentifiersDisplay)")
|
||||
.font(.title)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
InfoPaneControls(xcode: xcode)
|
||||
}
|
||||
.xcodesBackground()
|
||||
|
||||
|
||||
VStack {
|
||||
Text("Platforms")
|
||||
.font(.title3)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
PlatformsView(xcode: xcode)
|
||||
}
|
||||
.xcodesBackground()
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
ReleaseDateView(date: xcode.releaseDate, url: xcode.releaseNotesURL)
|
||||
CompatibilityView(requiredMacOSVersion: xcode.requiredMacOSVersion)
|
||||
IdenticalBuildsView(builds: xcode.identicalBuilds)
|
||||
SDKandCompilers
|
||||
}
|
||||
.frame(width: 200)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var SDKandCompilers: some View {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
SDKsView(sdks: xcode.sdks)
|
||||
CompilersView(compilers: xcode.compilers)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding()
|
||||
.background(.background)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous))
|
||||
}
|
||||
}
|
||||
|
||||
#Preview(PreviewName.allCases[0].rawValue) { makePreviewContent(for: 0) }
|
||||
#Preview(PreviewName.allCases[1].rawValue) { makePreviewContent(for: 1) }
|
||||
#Preview(PreviewName.allCases[2].rawValue) { makePreviewContent(for: 2) }
|
||||
#Preview(PreviewName.allCases[3].rawValue) { makePreviewContent(for: 3) }
|
||||
#Preview(PreviewName.allCases[4].rawValue) { makePreviewContent(for: 4) }
|
||||
#Preview(XcodePreviewName.allCases[0].rawValue) { makePreviewContent(for: 0) }
|
||||
#Preview(XcodePreviewName.allCases[1].rawValue) { makePreviewContent(for: 1) }
|
||||
#Preview(XcodePreviewName.allCases[2].rawValue) { makePreviewContent(for: 2) }
|
||||
#Preview(XcodePreviewName.allCases[3].rawValue) { makePreviewContent(for: 3) }
|
||||
#Preview(XcodePreviewName.allCases[4].rawValue) { makePreviewContent(for: 4) }
|
||||
|
||||
private func makePreviewContent(for index: Int) -> some View {
|
||||
let name = PreviewName.allCases[index]
|
||||
let name = XcodePreviewName.allCases[index]
|
||||
return InfoPane(xcode: xcodeDict[name]!)
|
||||
.environmentObject(configure(AppState()) {
|
||||
$0.allXcodes = [xcodeDict[name]!]
|
||||
|
|
@ -53,17 +75,17 @@ private func makePreviewContent(for index: Int) -> some View {
|
|||
.padding()
|
||||
}
|
||||
|
||||
enum PreviewName: String, CaseIterable, Identifiable {
|
||||
enum XcodePreviewName: String, CaseIterable, Identifiable {
|
||||
case Populated_Installed_Selected
|
||||
case Populated_Installed_Unselected
|
||||
case Populated_Uninstalled
|
||||
case Basic_Installed
|
||||
case Basic_Installing
|
||||
|
||||
var id: PreviewName { self }
|
||||
var id: XcodePreviewName { self }
|
||||
}
|
||||
|
||||
var xcodeDict: [PreviewName: Xcode] = [
|
||||
var xcodeDict: [XcodePreviewName: Xcode] = [
|
||||
.Populated_Installed_Selected: .init(
|
||||
version: _versionNoMeta,
|
||||
installState: .installed(Path(_path)!),
|
||||
|
|
@ -121,15 +143,48 @@ var xcodeDict: [PreviewName: Xcode] = [
|
|||
),
|
||||
]
|
||||
|
||||
var downloadableRuntimes: [DownloadableRuntime] = {
|
||||
var runtimes = try! JSONDecoder().decode([DownloadableRuntime].self, from: Current.files.contents(atPath: Path.runtimeCacheFile.string)!)
|
||||
// set iOS to installed
|
||||
let iOSIndex = runtimes.firstIndex { $0.sdkBuildUpdate == "19E239" }!
|
||||
var iOSRuntime = runtimes[iOSIndex]
|
||||
iOSRuntime.installState = .installed
|
||||
runtimes[iOSIndex] = iOSRuntime
|
||||
|
||||
let watchOSIndex = runtimes.firstIndex { $0.sdkBuildUpdate == "20R362" }!
|
||||
var runtime = runtimes[watchOSIndex]
|
||||
runtime.installState = .installing(
|
||||
RuntimeInstallationStep.downloading(
|
||||
progress:configure(Progress()) {
|
||||
$0.kind = .file
|
||||
$0.fileOperationKind = .downloading
|
||||
$0.estimatedTimeRemaining = 123
|
||||
$0.totalUnitCount = 11_944_848_484
|
||||
$0.completedUnitCount = 848_444_920
|
||||
$0.throughput = 9_211_681
|
||||
}
|
||||
)
|
||||
)
|
||||
runtimes[watchOSIndex] = runtime
|
||||
|
||||
return runtimes
|
||||
}()
|
||||
|
||||
var installedRuntimes: [CoreSimulatorImage] = {
|
||||
[CoreSimulatorImage(uuid: "85B22F5B-048B-4331-B6E2-F4196D8B7475", path: ["relative" : "file:///Library/Developer/CoreSimulator/Images/85B22F5B-048B-4331-B6E2-F4196D8B7475.dmg"], runtimeInfo: CoreSimulatorRuntimeInfo(build: "19E240"))] // same as iOS in _SDK's
|
||||
}()
|
||||
|
||||
|
||||
private let _versionNoMeta = Version(major: 12, minor: 3, patch: 0)
|
||||
private let _versionWithMeta = Version(major: 12, minor: 3, patch: 1, buildMetadataIdentifiers: ["1234A"])
|
||||
private let _path = "/Applications/Xcode-12.3.0.app"
|
||||
private let _requiredMacOSVersion = "10.15.4"
|
||||
private let _sdks = SDKs(
|
||||
macOS: .init(number: "11.1"),
|
||||
iOS: .init(number: "14.3"),
|
||||
watchOS: .init(number: "7.3"),
|
||||
tvOS: .init(number: "14.3")
|
||||
iOS: .init(number: "15.4", "19E239"),
|
||||
watchOS: .init(number: "7.3", "20R362"),
|
||||
tvOS: .init(number: "14.3", "20K67"),
|
||||
visionOS: .init(number: "1.0", "21N5233e")
|
||||
)
|
||||
private let _compilers = Compilers(
|
||||
gcc: .init(number: "4"),
|
||||
|
|
|
|||
|
|
@ -15,12 +15,18 @@ struct InfoPaneControls: View {
|
|||
VStack (alignment: .leading) {
|
||||
switch xcode.installState {
|
||||
case .notInstalled:
|
||||
NotInstalledStateButtons(
|
||||
downloadFileSizeString: xcode.downloadFileSizeString,
|
||||
id: xcode.id)
|
||||
HStack {
|
||||
Spacer()
|
||||
NotInstalledStateButtons(
|
||||
downloadFileSizeString: xcode.downloadFileSizeString,
|
||||
id: xcode.id)
|
||||
}
|
||||
|
||||
case .installing(let installationStep):
|
||||
InstallationStepDetailView(installationStep: installationStep)
|
||||
CancelInstallButton(xcode: xcode)
|
||||
HStack(alignment: .top) {
|
||||
InstallationStepDetailView(installationStep: installationStep)
|
||||
CancelInstallButton(xcode: xcode)
|
||||
}
|
||||
case .installed(_):
|
||||
InstalledStateButtons(xcode: xcode)
|
||||
}
|
||||
|
|
@ -28,14 +34,14 @@ struct InfoPaneControls: View {
|
|||
}
|
||||
}
|
||||
|
||||
#Preview(PreviewName.allCases[0].rawValue) { makePreviewContent(for: 0) }
|
||||
#Preview(PreviewName.allCases[1].rawValue) { makePreviewContent(for: 1) }
|
||||
#Preview(PreviewName.allCases[2].rawValue) { makePreviewContent(for: 2) }
|
||||
#Preview(PreviewName.allCases[3].rawValue) { makePreviewContent(for: 3) }
|
||||
#Preview(PreviewName.allCases[4].rawValue) { makePreviewContent(for: 4) }
|
||||
#Preview(XcodePreviewName.allCases[0].rawValue) { makePreviewContent(for: 0) }
|
||||
#Preview(XcodePreviewName.allCases[1].rawValue) { makePreviewContent(for: 1) }
|
||||
#Preview(XcodePreviewName.allCases[2].rawValue) { makePreviewContent(for: 2) }
|
||||
#Preview(XcodePreviewName.allCases[3].rawValue) { makePreviewContent(for: 3) }
|
||||
#Preview(XcodePreviewName.allCases[4].rawValue) { makePreviewContent(for: 4) }
|
||||
|
||||
private func makePreviewContent(for index: Int) -> some View {
|
||||
let name = PreviewName.allCases[index]
|
||||
let name = XcodePreviewName.allCases[index]
|
||||
|
||||
return InfoPaneControls(xcode: xcodeDict[name]!)
|
||||
.environmentObject(configure(AppState()) {
|
||||
|
|
|
|||
104
Xcodes/Frontend/InfoPane/PlatformsView.swift
Normal file
104
Xcodes/Frontend/InfoPane/PlatformsView.swift
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
//
|
||||
// PlatformsView.swift
|
||||
// Xcodes
|
||||
//
|
||||
// Created by Matt Kiazyk on 2023-12-18.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import XcodesKit
|
||||
|
||||
struct PlatformsView: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
|
||||
let xcode: Xcode
|
||||
|
||||
var body: some View {
|
||||
|
||||
let builds = xcode.sdks?.allBuilds()
|
||||
let runtimes = builds?.flatMap { sdkBuild in
|
||||
appState.downloadableRuntimes.filter {
|
||||
$0.sdkBuildUpdate == sdkBuild
|
||||
}
|
||||
}
|
||||
|
||||
ForEach(runtimes ?? [], id: \.simulatorVersion.buildUpdate) { runtime in
|
||||
runtimeView(runtime: runtime)
|
||||
.frame(minWidth: 200)
|
||||
.padding()
|
||||
.background(.quinary)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous))
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func runtimeView(runtime: DownloadableRuntime) -> some View {
|
||||
VStack(spacing: 10) {
|
||||
HStack {
|
||||
runtime.icon()
|
||||
Text("\(runtime.visibleIdentifier)")
|
||||
.font(.headline)
|
||||
pathIfAvailable(xcode: xcode, runtime: runtime)
|
||||
Spacer()
|
||||
Text(runtime.downloadFileSizeString)
|
||||
.font(.subheadline)
|
||||
}
|
||||
switch runtime.installState {
|
||||
case .installed:
|
||||
EmptyView()
|
||||
case .notInstalled:
|
||||
// TODO: Update the downloadableRuntimes with the appropriate installState so we don't have to check path awkwardly
|
||||
if let path = appState.runtimeInstallPath(xcode: xcode, runtime: runtime) {
|
||||
EmptyView()
|
||||
} else {
|
||||
HStack {
|
||||
Spacer()
|
||||
DownloadRuntimeButton(runtime: runtime)
|
||||
}
|
||||
}
|
||||
|
||||
case .installing(let installationStep):
|
||||
HStack(alignment: .top, spacing: 5){
|
||||
RuntimeInstallationStepDetailView(installationStep: installationStep)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
CancelRuntimeInstallButton(runtime: runtime)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func pathIfAvailable(xcode: Xcode, runtime: DownloadableRuntime) -> some View {
|
||||
if let path = appState.runtimeInstallPath(xcode: xcode, runtime: runtime) {
|
||||
Button(action: { appState.reveal(path: path.string) }) {
|
||||
Image(systemName: "arrow.right.circle.fill")
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
.help("RevealInFinder")
|
||||
} else {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview(XcodePreviewName.allCases[0].rawValue) { makePreviewContent(for: 0) }
|
||||
|
||||
private func makePreviewContent(for index: Int) -> some View {
|
||||
let name = XcodePreviewName.allCases[index]
|
||||
let runtimes = downloadableRuntimes
|
||||
|
||||
return PlatformsView(xcode: xcodeDict[name]!)
|
||||
.environmentObject({ () -> AppState in
|
||||
let a = AppState()
|
||||
a.allXcodes = [xcodeDict[name]!]
|
||||
a.installedRuntimes = installedRuntimes
|
||||
a.downloadableRuntimes = runtimes
|
||||
|
||||
return a
|
||||
|
||||
}())
|
||||
.frame(width: 300)
|
||||
.padding()
|
||||
}
|
||||
|
|
@ -10,26 +10,37 @@ import SwiftUI
|
|||
|
||||
struct ReleaseDateView: View {
|
||||
let date: Date?
|
||||
|
||||
let url: URL?
|
||||
var body: some View {
|
||||
if let date = date {
|
||||
VStack(alignment: .leading) {
|
||||
Text("ReleaseDate")
|
||||
.font(.headline)
|
||||
Text("\(date, style: .date)")
|
||||
.font(.subheadline)
|
||||
}
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Text("ReleaseDate")
|
||||
.font(.headline)
|
||||
Spacer()
|
||||
if let url {
|
||||
ReleaseNotesView(url: url)
|
||||
}
|
||||
}
|
||||
|
||||
Text("\(date, style: .date)")
|
||||
.font(.subheadline)
|
||||
|
||||
}
|
||||
|
||||
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding()
|
||||
.background(.background)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous))
|
||||
} else {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
|
||||
init(date: Date? = nil) {
|
||||
self.date = date
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ReleaseDateView(date: Date())
|
||||
ReleaseDateView(date: Date(), url: URL(string: "https://www.xcodes.app")!)
|
||||
.padding()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@ struct ReleaseNotesView: View {
|
|||
var body: some View {
|
||||
if let url = url {
|
||||
Button(action: { openURL(url) }) {
|
||||
Label("ReleaseNotes", systemImage: "link")
|
||||
Image(systemName: "link.circle.fill")
|
||||
.font(.title)
|
||||
}
|
||||
.buttonStyle(LinkButtonStyle())
|
||||
.buttonStyle(.plain)
|
||||
.contextMenu(menuItems: {
|
||||
CopyReleaseNoteButton(url: url)
|
||||
})
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.help("ReleaseNotes.help")
|
||||
} else {
|
||||
EmptyView()
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
//
|
||||
// RuntimesView.swift
|
||||
// Xcodes
|
||||
//
|
||||
// Created by Matt Kiazyk on 2023-11-23.
|
||||
// Copyright © 2023 Robots and Pencils. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import XcodesKit
|
||||
|
||||
struct RuntimesView: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
let xcode: Xcode
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Text("Platforms")
|
||||
.font(.headline)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
let builds = xcode.sdks?.allBuilds()
|
||||
let runtimes = builds?.flatMap { sdkBuild in
|
||||
appState.downloadableRuntimes.filter {
|
||||
$0.sdkBuildUpdate == sdkBuild
|
||||
}
|
||||
}
|
||||
|
||||
ForEach(runtimes ?? [], id: \.simulatorVersion.buildUpdate) { runtime in
|
||||
VStack {
|
||||
runtimeRow(runtime: runtime)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func runtimeRow(runtime: DownloadableRuntime) -> some View {
|
||||
HStack {
|
||||
Text("\(runtime.visibleIdentifier)")
|
||||
.font(.subheadline)
|
||||
Spacer()
|
||||
Text(runtime.downloadFileSizeString)
|
||||
.font(.subheadline)
|
||||
|
||||
switch runtime.installState {
|
||||
case .installed, .notInstalled:
|
||||
// it's installed if we have a path
|
||||
if let path = appState.runtimeInstallPath(xcode: xcode, runtime: runtime) {
|
||||
Button(action: { appState.reveal(path: path.string) }) {
|
||||
Image(systemName: "arrow.right.circle.fill")
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
.help("RevealInFinder")
|
||||
} else {
|
||||
DownloadRuntimeButton(runtime: runtime)
|
||||
}
|
||||
case .installing(_):
|
||||
CancelRuntimeInstallButton(runtime: runtime)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
switch runtime.installState {
|
||||
|
||||
case .installing(let installationStep):
|
||||
RuntimeInstallationStepDetailView(installationStep: installationStep)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
default:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#Preview {
|
||||
// RuntimesView()
|
||||
//}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
import ErrorHandling
|
||||
import SwiftUI
|
||||
import XcodesKit
|
||||
import Path
|
||||
import Version
|
||||
|
||||
struct MainWindow: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
|
|
@ -16,9 +18,9 @@ struct MainWindow: View {
|
|||
@AppStorage("isInstalledOnly") private var isInstalledOnly = false
|
||||
|
||||
var body: some View {
|
||||
HSplitView {
|
||||
NavigationSplitViewWrapper {
|
||||
XcodeListView(selectedXcodeID: $selectedXcodeID, searchText: searchText, category: category, isInstalledOnly: isInstalledOnly)
|
||||
.frame(minWidth: 300)
|
||||
.frame(minWidth: 250)
|
||||
.layoutPriority(1)
|
||||
.alert(item: $appState.xcodeBeingConfirmedForUninstallation) { xcode in
|
||||
Alert(title: Text(String(format: localizeString("Alert.Uninstall.Title"), xcode.description)),
|
||||
|
|
@ -26,25 +28,73 @@ struct MainWindow: View {
|
|||
primaryButton: .destructive(Text("Uninstall"), action: { self.appState.uninstall(xcode: xcode) }),
|
||||
secondaryButton: .cancel(Text("Cancel")))
|
||||
}
|
||||
|
||||
if isShowingInfoPane {
|
||||
Group {
|
||||
if let xcode = xcode {
|
||||
InfoPane(xcode: xcode)
|
||||
.searchable(text: $searchText, placement: .sidebar)
|
||||
.mainToolbar(
|
||||
category: $category,
|
||||
isInstalledOnly: $isInstalledOnly,
|
||||
isShowingInfoPane: $isShowingInfoPane
|
||||
)
|
||||
} detail: {
|
||||
Group {
|
||||
if let xcode = xcode {
|
||||
InfoPane(xcode: xcode)
|
||||
} else {
|
||||
UnselectedView()
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.toolbar {
|
||||
ToolbarItemGroup {
|
||||
Button(action: { appState.presentedSheet = .signIn }, label: {
|
||||
Label("Login", systemImage: "person.circle")
|
||||
})
|
||||
.help("LoginDescription")
|
||||
if #available(macOS 14, *) {
|
||||
SettingsLink(label: {
|
||||
Label("Preferences", systemImage: "gearshape")
|
||||
})
|
||||
.help("PreferencesDescription")
|
||||
} else {
|
||||
UnselectedView()
|
||||
Button(action: {
|
||||
if #available(macOS 13, *) {
|
||||
NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil)
|
||||
} else {
|
||||
NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil)
|
||||
}
|
||||
}, label: {
|
||||
Label("Preferences", systemImage: "gearshape")
|
||||
})
|
||||
.help("PreferencesDescription")
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.frame(minWidth: 300, maxWidth: .infinity)
|
||||
}
|
||||
}
|
||||
.mainToolbar(
|
||||
category: $category,
|
||||
isInstalledOnly: $isInstalledOnly,
|
||||
isShowingInfoPane: $isShowingInfoPane,
|
||||
searchText: $searchText
|
||||
)
|
||||
|
||||
// HSplitView {
|
||||
// XcodeListView(selectedXcodeID: $selectedXcodeID, searchText: searchText, category: category, isInstalledOnly: isInstalledOnly)
|
||||
// .frame(minWidth: 300)
|
||||
// .layoutPriority(1)
|
||||
// .alert(item: $appState.xcodeBeingConfirmedForUninstallation) { xcode in
|
||||
// Alert(title: Text(String(format: localizeString("Alert.Uninstall.Title"), xcode.description)),
|
||||
// message: Text("Alert.Uninstall.Message"),
|
||||
// primaryButton: .destructive(Text("Uninstall"), action: { self.appState.uninstall(xcode: xcode) }),
|
||||
// secondaryButton: .cancel(Text("Cancel")))
|
||||
// }
|
||||
// .searchable(text: $searchText)
|
||||
//
|
||||
// if isShowingInfoPane {
|
||||
// Group {
|
||||
// if let xcode = xcode {
|
||||
// InfoPane(xcode: xcode)
|
||||
// } else {
|
||||
// UnselectedView()
|
||||
// }
|
||||
// }
|
||||
// .padding()
|
||||
// .frame(minWidth: 300, maxWidth: .infinity)
|
||||
// }
|
||||
// }
|
||||
|
||||
.bottomStatusBar()
|
||||
.padding([.top], 0)
|
||||
.navigationSubtitle(subtitleText)
|
||||
|
|
@ -197,6 +247,16 @@ struct MainWindow: View {
|
|||
|
||||
struct MainWindow_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
MainWindow()
|
||||
MainWindow().environmentObject({ () -> AppState in
|
||||
let a = AppState()
|
||||
a.allXcodes = [
|
||||
Xcode(version: Version("12.0.0+1234A")!, identicalBuilds: [Version("12.0.0+1234A")!, Version("12.0.0-RC+1234A")!], installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil),
|
||||
Xcode(version: Version("12.3.0")!, installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: true, icon: nil),
|
||||
Xcode(version: Version("12.2.0")!, installState: .notInstalled, selected: false, icon: nil),
|
||||
Xcode(version: Version("12.1.0")!, installState: .installing(.downloading(progress: configure(Progress(totalUnitCount: 100)) { $0.completedUnitCount = 40 })), selected: false, icon: nil),
|
||||
Xcode(version: Version("12.0.0")!, installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil),
|
||||
]
|
||||
return a
|
||||
}())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ struct MainToolbarModifier: ViewModifier {
|
|||
@Binding var category: XcodeListCategory
|
||||
@Binding var isInstalledOnly: Bool
|
||||
@Binding var isShowingInfoPane: Bool
|
||||
@Binding var searchText: String
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
|
|
@ -14,10 +13,6 @@ struct MainToolbarModifier: ViewModifier {
|
|||
|
||||
private var toolbar: some ToolbarContent {
|
||||
ToolbarItemGroup {
|
||||
Button(action: { appState.presentedSheet = .signIn }, label: {
|
||||
Label("Login", systemImage: "person.circle")
|
||||
})
|
||||
.help("LoginDescription")
|
||||
|
||||
ProgressButton(
|
||||
isInProgress: appState.isUpdating,
|
||||
|
|
@ -27,7 +22,7 @@ struct MainToolbarModifier: ViewModifier {
|
|||
}
|
||||
.keyboardShortcut(KeyEquivalent("r"))
|
||||
.help("RefreshDescription")
|
||||
|
||||
Spacer()
|
||||
Button(action: {
|
||||
switch category {
|
||||
case .all: category = .release
|
||||
|
|
@ -75,39 +70,6 @@ struct MainToolbarModifier: ViewModifier {
|
|||
}
|
||||
.help("FilterInstalledDescription")
|
||||
|
||||
Button(action: { isShowingInfoPane.toggle() }) {
|
||||
if isShowingInfoPane {
|
||||
Label("Info", systemImage: "info.circle.fill")
|
||||
.foregroundColor(.accentColor)
|
||||
} else {
|
||||
Label("Info", systemImage: "info.circle")
|
||||
}
|
||||
}
|
||||
.keyboardShortcut(KeyboardShortcut("i", modifiers: [.command, .option]))
|
||||
.help("InfoDescription")
|
||||
|
||||
if #available(macOS 14, *) {
|
||||
SettingsLink(label: {
|
||||
Label("Preferences", systemImage: "gearshape")
|
||||
})
|
||||
.help("PreferencesDescription")
|
||||
} else {
|
||||
Button(action: {
|
||||
if #available(macOS 13, *) {
|
||||
NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil)
|
||||
} else {
|
||||
NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil)
|
||||
}
|
||||
}, label: {
|
||||
Label("Preferences", systemImage: "gearshape")
|
||||
})
|
||||
.help("PreferencesDescription")
|
||||
}
|
||||
|
||||
TextField("Search", text: $searchText)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.frame(width: 200)
|
||||
.help("SearchDescription")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -116,15 +78,13 @@ extension View {
|
|||
func mainToolbar(
|
||||
category: Binding<XcodeListCategory>,
|
||||
isInstalledOnly: Binding<Bool>,
|
||||
isShowingInfoPane: Binding<Bool>,
|
||||
searchText: Binding<String>
|
||||
isShowingInfoPane: Binding<Bool>
|
||||
) -> some View {
|
||||
self.modifier(
|
||||
MainToolbarModifier(
|
||||
category: category,
|
||||
isInstalledOnly: isInstalledOnly,
|
||||
isShowingInfoPane: isShowingInfoPane,
|
||||
searchText: searchText
|
||||
isShowingInfoPane: isShowingInfoPane
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ struct XcodeListView: View {
|
|||
List(visibleXcodes, selection: $selectedXcodeID) { xcode in
|
||||
XcodeListViewRow(xcode: xcode, selected: selectedXcodeID == xcode.id, appState: appState)
|
||||
}
|
||||
.listStyle(.sidebar)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -335,6 +335,33 @@ For more information, please refer to <<http://unlicense.org/>>\
|
|||
otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.\
|
||||
\
|
||||
|
||||
\fs34 SwiftUIMasonry\
|
||||
\
|
||||
|
||||
\fs26 MIT License\
|
||||
\
|
||||
Copyright (c) 2022 Ciaran O'Brien\
|
||||
\
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy\
|
||||
of this software and associated documentation files (the "Software"), to deal\
|
||||
in the Software without restriction, including without limitation the rights\
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\
|
||||
copies of the Software, and to permit persons to whom the Software is\
|
||||
furnished to do so, subject to the following conditions:\
|
||||
\
|
||||
The above copyright notice and this permission notice shall be included in all\
|
||||
copies or substantial portions of the Software.\
|
||||
\
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\
|
||||
SOFTWARE.\
|
||||
\
|
||||
\
|
||||
|
||||
\fs34 DockProgress\
|
||||
\
|
||||
|
||||
|
|
|
|||
|
|
@ -8193,6 +8193,7 @@
|
|||
}
|
||||
},
|
||||
"Info" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"ca" : {
|
||||
"stringUnit" : {
|
||||
|
|
@ -8299,6 +8300,7 @@
|
|||
}
|
||||
},
|
||||
"InfoDescription" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"ca" : {
|
||||
"stringUnit" : {
|
||||
|
|
@ -15465,6 +15467,7 @@
|
|||
}
|
||||
},
|
||||
"ReleaseNotes" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"ca" : {
|
||||
"stringUnit" : {
|
||||
|
|
@ -16125,6 +16128,7 @@
|
|||
}
|
||||
},
|
||||
"Search" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"ca" : {
|
||||
"stringUnit" : {
|
||||
|
|
@ -16237,6 +16241,7 @@
|
|||
}
|
||||
},
|
||||
"SearchDescription" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"ca" : {
|
||||
"stringUnit" : {
|
||||
|
|
@ -18438,6 +18443,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"XCODES RULES!" : {
|
||||
|
||||
}
|
||||
},
|
||||
"version" : "1.0"
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ struct XcodesApp: App {
|
|||
guard !isTesting else { return }
|
||||
if case .active = newScenePhase {
|
||||
appState.updateIfNeeded()
|
||||
appState.updateInstalledRuntimes()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,14 +9,28 @@ import Foundation
|
|||
|
||||
public struct CoreSimulatorPlist: Decodable {
|
||||
public let images: [CoreSimulatorImage]
|
||||
|
||||
public init(images: [CoreSimulatorImage]) {
|
||||
self.images = images
|
||||
}
|
||||
}
|
||||
|
||||
public struct CoreSimulatorImage: Decodable {
|
||||
public let uuid: String
|
||||
public let path: [String: String]
|
||||
public let runtimeInfo: CoreSimulatorRuntimeInfo
|
||||
|
||||
public init(uuid: String, path: [String : String], runtimeInfo: CoreSimulatorRuntimeInfo) {
|
||||
self.uuid = uuid
|
||||
self.path = path
|
||||
self.runtimeInfo = runtimeInfo
|
||||
}
|
||||
}
|
||||
|
||||
public struct CoreSimulatorRuntimeInfo: Decodable {
|
||||
public let build: String
|
||||
|
||||
public init(build: String) {
|
||||
self.build = build
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue