mirror of
https://github.com/XcodesOrg/XcodesApp.git
synced 2026-03-28 09:25:47 +00:00
redesigned infoPane
This commit is contained in:
parent
7070575a32
commit
ede9bd1c46
19 changed files with 485 additions and 68 deletions
|
|
@ -115,7 +115,9 @@
|
|||
E81D7EA02805250100A205FC /* Collection+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E81D7E9F2805250100A205FC /* Collection+.swift */; };
|
||||
E832EAF82B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E832EAF72B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift */; };
|
||||
E84B7D0D2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E84B7D0C2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift */; };
|
||||
E84B7D0F2B30986700DBDA2B /* InfoPane2.swift in Sources */ = {isa = PBXBuildFile; fileRef = E84B7D0E2B30986700DBDA2B /* InfoPane2.swift */; };
|
||||
E84CF8C12B0FEB8300ECA259 /* RuntimesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E84CF8C02B0FEB8300ECA259 /* RuntimesView.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 */; };
|
||||
|
|
@ -311,8 +313,10 @@
|
|||
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>"; };
|
||||
E84B7D0C2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSplitViewWrapper.swift; sourceTree = "<group>"; };
|
||||
E84B7D0E2B30986700DBDA2B /* InfoPane2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoPane2.swift; sourceTree = "<group>"; };
|
||||
E84CF8C02B0FEB8300ECA259 /* RuntimesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimesView.swift; sourceTree = "<group>"; };
|
||||
E856BB73291EDD3D00DC438B /* XcodesKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = XcodesKit; path = Xcodes/XcodesKit; 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>"; };
|
||||
|
|
@ -648,6 +652,8 @@
|
|||
B0C6AD0C2AD91D7900E64698 /* IconView.swift */,
|
||||
E832EAF72B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift */,
|
||||
E84CF8C02B0FEB8300ECA259 /* RuntimesView.swift */,
|
||||
E84B7D0E2B30986700DBDA2B /* InfoPane2.swift */,
|
||||
E86671262B309D2F0048559A /* PlatformsView.swift */,
|
||||
);
|
||||
path = InfoPane;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -911,6 +917,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 */,
|
||||
|
|
@ -922,6 +929,7 @@
|
|||
E8B20CBF2A2EDEC20057D816 /* SDKs+Xcode.swift in Sources */,
|
||||
CA9FF877259528CC00E47BAF /* Version+XcodeReleases.swift in Sources */,
|
||||
CABFAA2D2592FBFC00380FEE /* Configure.swift in Sources */,
|
||||
E84B7D0F2B30986700DBDA2B /* InfoPane2.swift in Sources */,
|
||||
E84B7D0D2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift in Sources */,
|
||||
CA73510D257BFCEF00EA9CF8 /* NSAttributedString+.swift in Sources */,
|
||||
CAFBDB952598FE96003DCC5A /* FocusedValues.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -9,16 +9,30 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding()
|
||||
.background(.background)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous))
|
||||
} else {
|
||||
EmptyView()
|
||||
}
|
||||
|
|
@ -28,4 +42,5 @@ struct CompatibilityView: View {
|
|||
#Preview {
|
||||
CompatibilityView(requiredMacOSVersion: "10.15.4")
|
||||
.padding()
|
||||
.environmentObject(AppState())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ struct IdenticalBuildsView: View {
|
|||
.font(.subheadline)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding()
|
||||
.background(.background)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous))
|
||||
.accessibilityElement()
|
||||
.accessibility(label: Text("IdenticalBuilds"))
|
||||
.accessibility(value: Text(accessibilityDescription))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import AppKit
|
||||
import XcodesKit
|
||||
import Path
|
||||
import SwiftUI
|
||||
import Version
|
||||
|
|
@ -23,7 +24,7 @@ struct InfoPane: View {
|
|||
|
||||
Group {
|
||||
RuntimesView(xcode: xcode)
|
||||
ReleaseDateView(date: xcode.releaseDate)
|
||||
ReleaseDateView(date: xcode.releaseDate, url: xcode.releaseNotesURL)
|
||||
ReleaseNotesView(url: xcode.releaseNotesURL)
|
||||
IdenticalBuildsView(builds: xcode.identicalBuilds)
|
||||
CompatibilityView(requiredMacOSVersion: xcode.requiredMacOSVersion)
|
||||
|
|
@ -37,14 +38,14 @@ struct InfoPane: 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 InfoPane(xcode: xcodeDict[name]!)
|
||||
.environmentObject(configure(AppState()) {
|
||||
$0.allXcodes = [xcodeDict[name]!]
|
||||
|
|
@ -53,17 +54,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 +122,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"),
|
||||
|
|
|
|||
176
Xcodes/Frontend/InfoPane/InfoPane2.swift
Normal file
176
Xcodes/Frontend/InfoPane/InfoPane2.swift
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
//
|
||||
// InfoPane2.swift
|
||||
// Xcodes
|
||||
//
|
||||
// Created by Matt Kiazyk on 2023-12-18.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import Path
|
||||
import SwiftUI
|
||||
import Version
|
||||
import struct XCModel.Compilers
|
||||
import struct XCModel.SDKs
|
||||
|
||||
struct InfoPane2: View {
|
||||
|
||||
let xcode: Xcode
|
||||
var body: some View {
|
||||
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)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding()
|
||||
.background(.background)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous))
|
||||
|
||||
|
||||
VStack {
|
||||
Text("Platforms")
|
||||
.font(.title3)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
PlatformsView(xcode: xcode)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding()
|
||||
.background(.background)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous))
|
||||
}
|
||||
|
||||
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(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 = XcodePreviewName.allCases[index]
|
||||
return InfoPane2(xcode: xcodeDict[name]!)
|
||||
.environmentObject(configure(AppState()) {
|
||||
$0.allXcodes = [xcodeDict[name]!]
|
||||
})
|
||||
.frame(width: 600, height: 400)
|
||||
.padding()
|
||||
}
|
||||
|
||||
enum Preview2Name: String, CaseIterable, Identifiable {
|
||||
case Populated_Installed_Selected
|
||||
case Populated_Installed_Unselected
|
||||
case Populated_Uninstalled
|
||||
case Basic_Installed
|
||||
case Basic_Installing
|
||||
|
||||
var id: Preview2Name { self }
|
||||
}
|
||||
|
||||
var xcodeDict2: [Preview2Name: Xcode] = [
|
||||
.Populated_Installed_Selected: .init(
|
||||
version: _versionNoMeta,
|
||||
installState: .installed(Path(_path)!),
|
||||
selected: true,
|
||||
icon: NSWorkspace.shared.icon(forFile: _path),
|
||||
requiredMacOSVersion: _requiredMacOSVersion,
|
||||
releaseNotesURL: URL(string: "https://developer.apple.com/documentation/xcode-release-notes/xcode-12_3-release-notes/")!,
|
||||
releaseDate: Date(),
|
||||
sdks: _sdks,
|
||||
compilers: _compilers,
|
||||
downloadFileSize: _downloadFileSize
|
||||
),
|
||||
.Populated_Installed_Unselected: .init(
|
||||
version: _versionNoMeta,
|
||||
installState: .installed(Path(_path)!),
|
||||
selected: false,
|
||||
icon: NSWorkspace.shared.icon(forFile: _path),
|
||||
sdks: _sdks,
|
||||
compilers: _compilers,
|
||||
downloadFileSize: _downloadFileSize
|
||||
),
|
||||
.Populated_Uninstalled: .init(
|
||||
version: Version(major: 12, minor: 3, patch: 0),
|
||||
installState: .notInstalled,
|
||||
selected: false,
|
||||
icon: nil,
|
||||
sdks: _sdks,
|
||||
compilers: _compilers,
|
||||
downloadFileSize: _downloadFileSize
|
||||
),
|
||||
.Basic_Installed: .init(
|
||||
version: _versionWithMeta,
|
||||
installState: .installed(Path(_path)!),
|
||||
selected: false,
|
||||
icon: nil,
|
||||
sdks: nil,
|
||||
compilers: nil
|
||||
),
|
||||
.Basic_Installing: .init(
|
||||
version: _versionWithMeta,
|
||||
installState: .installing(.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
|
||||
}
|
||||
)),
|
||||
selected: false,
|
||||
icon: nil,
|
||||
sdks: nil,
|
||||
compilers: nil
|
||||
),
|
||||
]
|
||||
|
||||
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")
|
||||
)
|
||||
private let _compilers = Compilers(
|
||||
gcc: .init(number: "4"),
|
||||
llvm_gcc: .init(number: "213"),
|
||||
llvm: .init(number: "2.3"),
|
||||
clang: .init(number: "7.3"),
|
||||
swift: .init(number: "5.3.2")
|
||||
)
|
||||
private let _downloadFileSize: Int64 = 242_342_424
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ struct MainWindow: View {
|
|||
var body: some View {
|
||||
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)),
|
||||
|
|
@ -37,7 +37,7 @@ struct MainWindow: View {
|
|||
} detail: {
|
||||
Group {
|
||||
if let xcode = xcode {
|
||||
InfoPane(xcode: xcode)
|
||||
InfoPane2(xcode: xcode)
|
||||
} else {
|
||||
UnselectedView()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,18 +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")
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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\
|
||||
\
|
||||
|
||||
|
|
|
|||
|
|
@ -15467,6 +15467,7 @@
|
|||
}
|
||||
},
|
||||
"ReleaseNotes" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"ca" : {
|
||||
"stringUnit" : {
|
||||
|
|
|
|||
|
|
@ -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