From d122014b7447fef83e09cccd5269383f80fd3bff Mon Sep 17 00:00:00 2001 From: Brandon Evans Date: Wed, 30 Dec 2020 09:01:12 -0700 Subject: [PATCH 1/6] Update install and select state representations in Xcode list --- Xcodes/Frontend/XcodeList/XcodeListView.swift | 45 +++++++++++++------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/Xcodes/Frontend/XcodeList/XcodeListView.swift b/Xcodes/Frontend/XcodeList/XcodeListView.swift index 3503114..b1dd4f7 100644 --- a/Xcodes/Frontend/XcodeList/XcodeListView.swift +++ b/Xcodes/Frontend/XcodeList/XcodeListView.swift @@ -41,23 +41,13 @@ struct XcodeListView: View { Text(verbatim: xcode.path ?? "") .font(.caption) - .foregroundColor(selectedXcodeID == xcode.id ? Color(NSColor.selectedMenuItemTextColor) : Color(NSColor.secondaryLabelColor)) + .foregroundColor(.secondary) } - Spacer() - if xcode.selected { - Tag(text: "SELECTED") - .foregroundColor(.green) - } - - Button(xcode.installed ? "INSTALLED" : "INSTALL") { - print("Installing...") - } - .buttonStyle(AppStoreButtonStyle(installed: xcode.installed, - highlighted: selectedXcodeID == xcode.id)) - .disabled(xcode.installed) + selectControl(for: xcode) + installControl(for: xcode) } .contextMenu { InstallButton(xcode: xcode) @@ -73,7 +63,7 @@ struct XcodeListView: View { } } } - + @ViewBuilder func appIconView(for xcode: Xcode) -> some View { if let icon = xcode.icon { @@ -84,6 +74,33 @@ struct XcodeListView: View { .foregroundColor(.secondary) } } + + @ViewBuilder + private func selectControl(for xcode: Xcode) -> some View { + if xcode.selected { + Image(systemName: "checkmark.circle.fill") + .foregroundColor(.green) + .help("This version is selected as the default") + } + } + + @ViewBuilder + private func installControl(for xcode: Xcode) -> some View { + if xcode.selected { + Button("DEFAULT") { appState.select(id: xcode.id) } + .buttonStyle(AppStoreButtonStyle(installed: false, highlighted: selectedXcodeID == xcode.id)) + .disabled(true) + .help("This version is selected as the default") + } else if xcode.installed { + Button("SELECT") { appState.select(id: xcode.id) } + .buttonStyle(AppStoreButtonStyle(installed: false, highlighted: selectedXcodeID == xcode.id)) + .help("Select this version as the default") + } else { + Button("INSTALL") { print("Installing...") } + .buttonStyle(AppStoreButtonStyle(installed: true, highlighted: selectedXcodeID == xcode.id)) + .help("Install this version") + } + } } struct XcodeListView_Previews: PreviewProvider { From 69c083ca358c686085caae1bcdd83449771ca79c Mon Sep 17 00:00:00 2001 From: Brandon Evans Date: Wed, 30 Dec 2020 09:02:50 -0700 Subject: [PATCH 2/6] Update AppStoreButtonStyle with secondary state --- .../XcodeList/AppStoreButtonStyle.swift | 126 +++++++++++++----- Xcodes/Frontend/XcodeList/XcodeListView.swift | 6 +- 2 files changed, 97 insertions(+), 35 deletions(-) diff --git a/Xcodes/Frontend/XcodeList/AppStoreButtonStyle.swift b/Xcodes/Frontend/XcodeList/AppStoreButtonStyle.swift index c22f16d..3b7b7df 100644 --- a/Xcodes/Frontend/XcodeList/AppStoreButtonStyle.swift +++ b/Xcodes/Frontend/XcodeList/AppStoreButtonStyle.swift @@ -1,75 +1,137 @@ import SwiftUI struct AppStoreButtonStyle: ButtonStyle { - var installed: Bool + var primary: Bool var highlighted: Bool private struct AppStoreButton: View { + @SwiftUI.Environment(\.isEnabled) var isEnabled var configuration: ButtonStyle.Configuration - var installed: Bool + var primary: Bool var highlighted: Bool - // This seems to magically help the highlight colors update on time - @SwiftUI.Environment(\.isFocused) var isFocused var textColor: Color { - if installed { - if highlighted { - return Color.white + if isEnabled { + if primary { + if highlighted { + return Color.accentColor + } + else { + return Color.white + } } else { - return Color.secondary - } - } - else { - if highlighted { return Color.accentColor } + } else { + if primary { + if highlighted { + return Color(.disabledControlTextColor) + } + else { + return Color.white + } + } else { - return Color.white + if highlighted { + return Color.white + } + else { + return Color(.disabledControlTextColor) + } } } } func background(isPressed: Bool) -> some View { Group { - if installed { - EmptyView() + if isEnabled { + if primary { + Capsule() + .fill( + highlighted ? + Color.white : + Color.accentColor + ) + .brightness(isPressed ? -0.25 : 0) + } else { + Capsule() + .fill( + Color(NSColor(red: 0.95, green: 0.95, blue: 0.97, alpha: 1)) + ) + .brightness(isPressed ? -0.25 : 0) + } } else { - Capsule() - .fill( - highlighted ? - Color.white : - Color.accentColor - ) - .brightness(isPressed ? -0.25 : 0) + if primary { + Capsule() + .fill( + highlighted ? + Color.white : + Color(.disabledControlTextColor) + ) + .brightness(isPressed ? -0.25 : 0) + } else { + EmptyView() + } } } } var body: some View { configuration.label - .font(Font.caption.weight(.medium)) + .font(Font.caption.weight(.bold)) .foregroundColor(textColor) - .padding(EdgeInsets(top: 2, leading: 8, bottom: 2, trailing: 8)) - .frame(minWidth: 80) + .padding(EdgeInsets(top: 4, leading: 8, bottom: 4, trailing: 8)) + .frame(minWidth: 65) .background(background(isPressed: configuration.isPressed)) .padding(1) } } func makeBody(configuration: ButtonStyle.Configuration) -> some View { - AppStoreButton(configuration: configuration, installed: installed, highlighted: highlighted) + AppStoreButton(configuration: configuration, primary: primary, highlighted: highlighted) } } struct AppStoreButtonStyle_Previews: PreviewProvider { static var previews: some View { Group { - Button("INSTALLED", action: {}) - .buttonStyle(AppStoreButtonStyle(installed: true, highlighted: false)) - .padding() - Button("INSTALL", action: {}) - .buttonStyle(AppStoreButtonStyle(installed: false, highlighted: false)) - .padding() + ForEach([ColorScheme.light, .dark], id: \.self) { colorScheme in + Group { + Button("OPEN", action: {}) + .buttonStyle(AppStoreButtonStyle(primary: true, highlighted: false)) + .padding() + .background(Color(.textBackgroundColor)) + .previewDisplayName("Primary") + Button("OPEN", action: {}) + .buttonStyle(AppStoreButtonStyle(primary: true, highlighted: true)) + .padding() + .background(Color(.controlAccentColor)) + .previewDisplayName("Primary, Highlighted") + Button("OPEN", action: {}) + .buttonStyle(AppStoreButtonStyle(primary: true, highlighted: false)) + .padding() + .disabled(true) + .background(Color(.textBackgroundColor)) + .previewDisplayName("Primary, Disabled") + Button("INSTALL", action: {}) + .buttonStyle(AppStoreButtonStyle(primary: false, highlighted: false)) + .padding() + .background(Color(.textBackgroundColor)) + .previewDisplayName("Secondary") + Button("INSTALL", action: {}) + .buttonStyle(AppStoreButtonStyle(primary: false, highlighted: true)) + .padding() + .background(Color(.controlAccentColor)) + .previewDisplayName("Secondary, Highlighted") + Button("INSTALL", action: {}) + .buttonStyle(AppStoreButtonStyle(primary: false, highlighted: false)) + .padding() + .disabled(true) + .background(Color(.textBackgroundColor)) + .previewDisplayName("Secondary, Disabled") + } + .environment(\.colorScheme, colorScheme) + } } } } diff --git a/Xcodes/Frontend/XcodeList/XcodeListView.swift b/Xcodes/Frontend/XcodeList/XcodeListView.swift index b1dd4f7..e4ea9de 100644 --- a/Xcodes/Frontend/XcodeList/XcodeListView.swift +++ b/Xcodes/Frontend/XcodeList/XcodeListView.swift @@ -88,16 +88,16 @@ struct XcodeListView: View { private func installControl(for xcode: Xcode) -> some View { if xcode.selected { Button("DEFAULT") { appState.select(id: xcode.id) } - .buttonStyle(AppStoreButtonStyle(installed: false, highlighted: selectedXcodeID == xcode.id)) + .buttonStyle(AppStoreButtonStyle(primary: false, highlighted: selectedXcodeID == xcode.id)) .disabled(true) .help("This version is selected as the default") } else if xcode.installed { Button("SELECT") { appState.select(id: xcode.id) } - .buttonStyle(AppStoreButtonStyle(installed: false, highlighted: selectedXcodeID == xcode.id)) + .buttonStyle(AppStoreButtonStyle(primary: false, highlighted: selectedXcodeID == xcode.id)) .help("Select this version as the default") } else { Button("INSTALL") { print("Installing...") } - .buttonStyle(AppStoreButtonStyle(installed: true, highlighted: selectedXcodeID == xcode.id)) + .buttonStyle(AppStoreButtonStyle(primary: true, highlighted: selectedXcodeID == xcode.id)) .help("Install this version") } } From 69d85a9478963ca31ec44c67a8c58ed7e0b77338 Mon Sep 17 00:00:00 2001 From: Brandon Evans Date: Wed, 30 Dec 2020 09:03:56 -0700 Subject: [PATCH 3/6] Extract XcodeListViewRow --- Xcodes.xcodeproj/project.pbxproj | 4 + Xcodes/Frontend/XcodeList/XcodeListView.swift | 68 +----------- .../Frontend/XcodeList/XcodeListViewRow.swift | 102 ++++++++++++++++++ 3 files changed, 107 insertions(+), 67 deletions(-) create mode 100644 Xcodes/Frontend/XcodeList/XcodeListViewRow.swift diff --git a/Xcodes.xcodeproj/project.pbxproj b/Xcodes.xcodeproj/project.pbxproj index b5e9823..f3178bb 100644 --- a/Xcodes.xcodeproj/project.pbxproj +++ b/Xcodes.xcodeproj/project.pbxproj @@ -77,6 +77,7 @@ CAFBDC4E2599B33D003DCC5A /* MainToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDC4D2599B33D003DCC5A /* MainToolbar.swift */; }; CAFBDC68259A308B003DCC5A /* InspectorPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDC67259A308B003DCC5A /* InspectorPane.swift */; }; CAFBDC6C259A3098003DCC5A /* View+Conditional.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDC6B259A3098003DCC5A /* View+Conditional.swift */; }; + CAFFFED8259CDA5000903F81 /* XcodeListViewRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -201,6 +202,7 @@ CAFBDC4D2599B33D003DCC5A /* MainToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainToolbar.swift; sourceTree = ""; }; CAFBDC67259A308B003DCC5A /* InspectorPane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorPane.swift; sourceTree = ""; }; CAFBDC6B259A3098003DCC5A /* View+Conditional.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Conditional.swift"; sourceTree = ""; }; + CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeListViewRow.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -310,6 +312,7 @@ CA44901E2463AD34003D8213 /* Tag.swift */, CAE42486259A68A300B8B246 /* XcodeListCategory.swift */, CAD2E7A32449574E00113D76 /* XcodeListView.swift */, + CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */, ); path = XcodeList; sourceTree = ""; @@ -655,6 +658,7 @@ CAA1CB4D255A5CFD003FD669 /* SignInPhoneListView.swift in Sources */, CAFBDC6C259A3098003DCC5A /* View+Conditional.swift in Sources */, CABFA9CF2592EEEA00380FEE /* Process.swift in Sources */, + CAFFFED8259CDA5000903F81 /* XcodeListViewRow.swift in Sources */, CABFA9C72592EEEA00380FEE /* Entry+.swift in Sources */, CAE42487259A68A300B8B246 /* XcodeListCategory.swift in Sources */, CABFAA2C2592FBFC00380FEE /* SettingsView.swift in Sources */, diff --git a/Xcodes/Frontend/XcodeList/XcodeListView.swift b/Xcodes/Frontend/XcodeList/XcodeListView.swift index e4ea9de..c3c564f 100644 --- a/Xcodes/Frontend/XcodeList/XcodeListView.swift +++ b/Xcodes/Frontend/XcodeList/XcodeListView.swift @@ -32,73 +32,7 @@ struct XcodeListView: View { var body: some View { List(visibleXcodes, selection: $selectedXcodeID) { xcode in - HStack { - appIconView(for: xcode) - - VStack(alignment: .leading) { - Text(xcode.description) - .font(.body) - - Text(verbatim: xcode.path ?? "") - .font(.caption) - .foregroundColor(.secondary) - } - - Spacer() - - selectControl(for: xcode) - installControl(for: xcode) - } - .contextMenu { - InstallButton(xcode: xcode) - - Divider() - - if xcode.installed { - SelectButton(xcode: xcode) - OpenButton(xcode: xcode) - RevealButton(xcode: xcode) - CopyPathButton(xcode: xcode) - } - } - } - } - - @ViewBuilder - func appIconView(for xcode: Xcode) -> some View { - if let icon = xcode.icon { - Image(nsImage: icon) - } else { - Color.clear - .frame(width: 32, height: 32) - .foregroundColor(.secondary) - } - } - - @ViewBuilder - private func selectControl(for xcode: Xcode) -> some View { - if xcode.selected { - Image(systemName: "checkmark.circle.fill") - .foregroundColor(.green) - .help("This version is selected as the default") - } - } - - @ViewBuilder - private func installControl(for xcode: Xcode) -> some View { - if xcode.selected { - Button("DEFAULT") { appState.select(id: xcode.id) } - .buttonStyle(AppStoreButtonStyle(primary: false, highlighted: selectedXcodeID == xcode.id)) - .disabled(true) - .help("This version is selected as the default") - } else if xcode.installed { - Button("SELECT") { appState.select(id: xcode.id) } - .buttonStyle(AppStoreButtonStyle(primary: false, highlighted: selectedXcodeID == xcode.id)) - .help("Select this version as the default") - } else { - Button("INSTALL") { print("Installing...") } - .buttonStyle(AppStoreButtonStyle(primary: true, highlighted: selectedXcodeID == xcode.id)) - .help("Install this version") + XcodeListViewRow(xcode: xcode, selected: selectedXcodeID == xcode.id) } } } diff --git a/Xcodes/Frontend/XcodeList/XcodeListViewRow.swift b/Xcodes/Frontend/XcodeList/XcodeListViewRow.swift new file mode 100644 index 0000000..54e2a79 --- /dev/null +++ b/Xcodes/Frontend/XcodeList/XcodeListViewRow.swift @@ -0,0 +1,102 @@ +import SwiftUI +import Version + +struct XcodeListViewRow: View { + @EnvironmentObject var appState: AppState + let xcode: Xcode + let selected: Bool + + var body: some View { + HStack { + appIconView(for: xcode) + + VStack(alignment: .leading) { + Text(xcode.description) + .font(.body) + + Text(verbatim: xcode.path ?? "") + .font(.caption) + .foregroundColor(.secondary) + } + + Spacer() + + selectControl(for: xcode) + installControl(for: xcode) + } + .contextMenu { + InstallButton(xcode: xcode) + + Divider() + + if xcode.installed { + SelectButton(xcode: xcode) + OpenButton(xcode: xcode) + RevealButton(xcode: xcode) + CopyPathButton(xcode: xcode) + } + } + } + + @ViewBuilder + func appIconView(for xcode: Xcode) -> some View { + if let icon = xcode.icon { + Image(nsImage: icon) + } else { + Color.clear + .frame(width: 32, height: 32) + .foregroundColor(.secondary) + } + } + + @ViewBuilder + private func selectControl(for xcode: Xcode) -> some View { + if xcode.selected { + Image(systemName: "checkmark.circle.fill") + .foregroundColor(.green) + .help("This version is selected as the default") + } + } + + @ViewBuilder + private func installControl(for xcode: Xcode) -> some View { + if xcode.selected { + Button("DEFAULT") { appState.select(id: xcode.id) } + .buttonStyle(AppStoreButtonStyle(primary: false, highlighted: selected)) + .disabled(true) + } else if xcode.installed { + Button("SELECT") { appState.select(id: xcode.id) } + .buttonStyle(AppStoreButtonStyle(primary: false, highlighted: selected)) + } else { + Button("INSTALL") { print("Installing...") } + .buttonStyle(AppStoreButtonStyle(primary: true, highlighted: selected)) + } + } +} + +struct XcodeListViewRow_Previews: PreviewProvider { + static var previews: some View { + Group { + XcodeListViewRow( + xcode: Xcode(version: Version("12.3.0")!, installState: .installed, selected: true, path: "/Applications/Xcode-12.3.0.app", icon: nil), + selected: false + ) + + XcodeListViewRow( + xcode: Xcode(version: Version("12.2.0")!, installState: .notInstalled, selected: false, path: nil, icon: nil), + selected: false + ) + + XcodeListViewRow( + xcode: Xcode(version: Version("12.1.0")!, installState: .notInstalled, selected: false, path: nil, icon: nil), + selected: false + ) + + XcodeListViewRow( + xcode: Xcode(version: Version("12.0.0")!, installState: .installed, selected: false, path: "/Applications/Xcode-12.3.0.app", icon: nil), + selected: false + ) + } + .environmentObject(AppState()) + } +} From f1bb76b029342a8aeae62e63dc131be2a380eaf7 Mon Sep 17 00:00:00 2001 From: Brandon Evans Date: Wed, 30 Dec 2020 09:04:24 -0700 Subject: [PATCH 4/6] Disable Select button in info pane but don't change title --- Xcodes/Frontend/XcodeList/InspectorPane.swift | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Xcodes/Frontend/XcodeList/InspectorPane.swift b/Xcodes/Frontend/XcodeList/InspectorPane.swift index bc3de77..66da445 100644 --- a/Xcodes/Frontend/XcodeList/InspectorPane.swift +++ b/Xcodes/Frontend/XcodeList/InspectorPane.swift @@ -31,15 +31,12 @@ struct InspectorPane: View { } HStack { - if xcode.selected { - Button("Selected", action: {}) - .disabled(true) - .help("Selected") - } else { - SelectButton(xcode: xcode) - } + SelectButton(xcode: xcode) + .disabled(xcode.selected) + .help("Selected") OpenButton(xcode: xcode) + .help("Open") } } else { InstallButton(xcode: xcode) From 7b4c4fc14c56048ac203f249f6fc5f6198379578 Mon Sep 17 00:00:00 2001 From: Brandon Evans Date: Wed, 30 Dec 2020 09:05:39 -0700 Subject: [PATCH 5/6] Use term "info pane" consistently instead of "inspector pane" --- Xcodes.xcodeproj/project.pbxproj | 8 ++++---- Xcodes/Frontend/MainWindow.swift | 2 +- .../{InspectorPane.swift => InfoPane.swift} | 12 ++++++------ Xcodes/Frontend/XcodeList/MainToolbar.swift | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) rename Xcodes/Frontend/XcodeList/{InspectorPane.swift => InfoPane.swift} (96%) diff --git a/Xcodes.xcodeproj/project.pbxproj b/Xcodes.xcodeproj/project.pbxproj index f3178bb..f0a8f89 100644 --- a/Xcodes.xcodeproj/project.pbxproj +++ b/Xcodes.xcodeproj/project.pbxproj @@ -75,7 +75,7 @@ CAFBDB912598FE80003DCC5A /* SelectedXcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDB902598FE80003DCC5A /* SelectedXcode.swift */; }; CAFBDB952598FE96003DCC5A /* FocusedValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDB942598FE96003DCC5A /* FocusedValues.swift */; }; CAFBDC4E2599B33D003DCC5A /* MainToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDC4D2599B33D003DCC5A /* MainToolbar.swift */; }; - CAFBDC68259A308B003DCC5A /* InspectorPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDC67259A308B003DCC5A /* InspectorPane.swift */; }; + CAFBDC68259A308B003DCC5A /* InfoPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDC67259A308B003DCC5A /* InfoPane.swift */; }; CAFBDC6C259A3098003DCC5A /* View+Conditional.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBDC6B259A3098003DCC5A /* View+Conditional.swift */; }; CAFFFED8259CDA5000903F81 /* XcodeListViewRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */; }; /* End PBXBuildFile section */ @@ -200,7 +200,7 @@ CAFBDB942598FE96003DCC5A /* FocusedValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusedValues.swift; sourceTree = ""; }; CAFBDBA525990C76003DCC5A /* SimpleXPCApp.LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = SimpleXPCApp.LICENSE; sourceTree = ""; }; CAFBDC4D2599B33D003DCC5A /* MainToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainToolbar.swift; sourceTree = ""; }; - CAFBDC67259A308B003DCC5A /* InspectorPane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorPane.swift; sourceTree = ""; }; + CAFBDC67259A308B003DCC5A /* InfoPane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoPane.swift; sourceTree = ""; }; CAFBDC6B259A3098003DCC5A /* View+Conditional.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Conditional.swift"; sourceTree = ""; }; CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeListViewRow.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -307,7 +307,7 @@ isa = PBXGroup; children = ( CA39711824495F0E00AFFB77 /* AppStoreButtonStyle.swift */, - CAFBDC67259A308B003DCC5A /* InspectorPane.swift */, + CAFBDC67259A308B003DCC5A /* InfoPane.swift */, CAFBDC4D2599B33D003DCC5A /* MainToolbar.swift */, CA44901E2463AD34003D8213 /* Tag.swift */, CAE42486259A68A300B8B246 /* XcodeListCategory.swift */, @@ -654,7 +654,7 @@ CA73510D257BFCEF00EA9CF8 /* NSAttributedString+.swift in Sources */, CAFBDB952598FE96003DCC5A /* FocusedValues.swift in Sources */, CABFA9C22592EEEA00380FEE /* Promise+.swift in Sources */, - CAFBDC68259A308B003DCC5A /* InspectorPane.swift in Sources */, + CAFBDC68259A308B003DCC5A /* InfoPane.swift in Sources */, CAA1CB4D255A5CFD003FD669 /* SignInPhoneListView.swift in Sources */, CAFBDC6C259A3098003DCC5A /* View+Conditional.swift in Sources */, CABFA9CF2592EEEA00380FEE /* Process.swift in Sources */, diff --git a/Xcodes/Frontend/MainWindow.swift b/Xcodes/Frontend/MainWindow.swift index 2de7592..fe78e1b 100644 --- a/Xcodes/Frontend/MainWindow.swift +++ b/Xcodes/Frontend/MainWindow.swift @@ -14,7 +14,7 @@ struct MainWindow: View { .frame(minWidth: 300) .layoutPriority(1) - InspectorPane(selectedXcodeID: selectedXcodeID) + InfoPane(selectedXcodeID: selectedXcodeID) .frame(minWidth: 300, maxWidth: .infinity) .frame(width: isShowingInfoPane ? nil : 0) .isHidden(!isShowingInfoPane) diff --git a/Xcodes/Frontend/XcodeList/InspectorPane.swift b/Xcodes/Frontend/XcodeList/InfoPane.swift similarity index 96% rename from Xcodes/Frontend/XcodeList/InspectorPane.swift rename to Xcodes/Frontend/XcodeList/InfoPane.swift index 66da445..9395fab 100644 --- a/Xcodes/Frontend/XcodeList/InspectorPane.swift +++ b/Xcodes/Frontend/XcodeList/InfoPane.swift @@ -4,7 +4,7 @@ import Version import struct XCModel.SDKs import struct XCModel.Compilers -struct InspectorPane: View { +struct InfoPane: View { @EnvironmentObject var appState: AppState let selectedXcodeID: Xcode.ID? @SwiftUI.Environment(\.openURL) var openURL: OpenURLAction @@ -167,10 +167,10 @@ struct InspectorPane: View { } } -struct InspectorPane_Previews: PreviewProvider { +struct InfoPane_Previews: PreviewProvider { static var previews: some View { Group { - InspectorPane(selectedXcodeID: Version(major: 12, minor: 3, patch: 0)) + InfoPane(selectedXcodeID: Version(major: 12, minor: 3, patch: 0)) .environmentObject(configure(AppState()) { $0.allXcodes = [ .init( @@ -198,7 +198,7 @@ struct InspectorPane_Previews: PreviewProvider { }) .previewDisplayName("Populated, Installed, Selected") - InspectorPane(selectedXcodeID: Version(major: 12, minor: 3, patch: 0)) + InfoPane(selectedXcodeID: Version(major: 12, minor: 3, patch: 0)) .environmentObject(configure(AppState()) { $0.allXcodes = [ .init( @@ -224,7 +224,7 @@ struct InspectorPane_Previews: PreviewProvider { }) .previewDisplayName("Populated, Installed, Unselected") - InspectorPane(selectedXcodeID: Version(major: 12, minor: 3, patch: 0)) + InfoPane(selectedXcodeID: Version(major: 12, minor: 3, patch: 0)) .environmentObject(configure(AppState()) { $0.allXcodes = [ .init( @@ -250,7 +250,7 @@ struct InspectorPane_Previews: PreviewProvider { }) .previewDisplayName("Populated, Uninstalled") - InspectorPane(selectedXcodeID: nil) + InfoPane(selectedXcodeID: nil) .environmentObject(configure(AppState()) { $0.allXcodes = [ ] diff --git a/Xcodes/Frontend/XcodeList/MainToolbar.swift b/Xcodes/Frontend/XcodeList/MainToolbar.swift index 8c804b8..392ae32 100644 --- a/Xcodes/Frontend/XcodeList/MainToolbar.swift +++ b/Xcodes/Frontend/XcodeList/MainToolbar.swift @@ -40,10 +40,10 @@ struct MainToolbarModifier: ViewModifier { Button(action: { isShowingInfoPane.toggle() }) { if isShowingInfoPane { - Label("Inspector", systemImage: "info.circle.fill") + Label("Info", systemImage: "info.circle.fill") .foregroundColor(.accentColor) } else { - Label("Inspector", systemImage: "info.circle") + Label("Info", systemImage: "info.circle") } } .keyboardShortcut(KeyboardShortcut("i", modifiers: [.command, .option])) From 15132bdfaace8b87d09494531010507387620f05 Mon Sep 17 00:00:00 2001 From: Brandon Evans Date: Wed, 30 Dec 2020 09:48:56 -0700 Subject: [PATCH 6/6] Psych, switch "default" to "active", add justification --- DECISIONS.md | 15 ++++++++ Xcodes/Backend/XcodeCommands.swift | 6 +++- .../Frontend/XcodeList/XcodeListViewRow.swift | 34 ++++++++++++------- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/DECISIONS.md b/DECISIONS.md index 5671e1a..bc9bd91 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -49,3 +49,18 @@ Uninstallation is not provided yet. I had this partially implemented (one attemp - [erikberglund/SwiftPrivilegedHelper](https://github.com/erikberglund/SwiftPrivilegedHelper) - [aronskaya/smjobbless](https://github.com/aronskaya/smjobbless) - [securing/SimpleXPCApp](https://github.com/securing/SimpleXPCApp) + +## Selecting the active version of Xcode + +This isn't a technical decision, but we spent enough time talking about this that it's probably worth sharing. When a user has more than one version of Xcode installed, a specific version of the developer tools can be selected with the `xcode-select` tool. The selected version of tools like xcodebuild or xcrun will be used unless the DEVELOPER_DIR environment variable has been set to a different path. You can read more about this in the `xcode-select` man pages. Notably, the man pages and [some notarization documentation](https://developer.apple.com/documentation/xcode/notarizing_macos_software_before_distribution) use the term "active" to indicate the Xcode version that's been selected. [This](https://developer.apple.com/library/archive/technotes/tn2339/_index.html#//apple_ref/doc/uid/DTS40014588-CH1-HOW_DO_I_SELECT_THE_DEFAULT_VERSION_OF_XCODE_TO_USE_FOR_MY_COMMAND_LINE_TOOLS_) older tech note uses the term "default". And of course, the `xcode-select` tool has the term "select" in its name. xcodes used the terms "select" and "selected" for this functionality, intending to match the xcode-select tool. + +Here are the descriptions of these terms from [Apple's Style Guide](https://books.apple.com/ca/book/apple-style-guide/id1161855204): + +> active: Use to refer to the app or window currently being used. Preferred to in front. +> default: OK to use to describe the state of settings before the user changes them. See also preset +> preset: Use to refer to a group of customized settings an app provides or the user saves for reuse. +> select: Use select, not choose, to refer to the action users perform when they select among multiple objects + +Xcodes.app has this same functionality as xcodes, which still uses `xcode-select` under the hood, but because the main UI is a list of selectable rows, there _may_ be some ambiguity about the meaning of "selected". "Default" has a less clear connection to `xcode-select`'s name, but does accurately describe the behaviour that results. In Xcode 11 Launch Services also uses the selected Xcode version when opening a (GUI) developer tool bundled with Xcode, like Instruments. We could also try to follow Apple's lead by using the term "active" from the `xcode-select` man pages and notarization documentation. According to the style guide "active" already has a clear meaning in a GUI context. + +Ultimately, we've decided to align with Apple's usage of "active" and "make active" in this specific context, despite possible confusion with the definition in the style guide. diff --git a/Xcodes/Backend/XcodeCommands.swift b/Xcodes/Backend/XcodeCommands.swift index e12cedb..9a4b1c3 100644 --- a/Xcodes/Backend/XcodeCommands.swift +++ b/Xcodes/Backend/XcodeCommands.swift @@ -58,7 +58,11 @@ struct SelectButton: View { var body: some View { Button(action: select) { - Text("Select") + if xcode?.selected == true { + Text("Active") + } else { + Text("Make active") + } } .disabled(xcode?.selected != false) .help("Select") diff --git a/Xcodes/Frontend/XcodeList/XcodeListViewRow.swift b/Xcodes/Frontend/XcodeList/XcodeListViewRow.swift index 54e2a79..1b1b16c 100644 --- a/Xcodes/Frontend/XcodeList/XcodeListViewRow.swift +++ b/Xcodes/Frontend/XcodeList/XcodeListViewRow.swift @@ -22,6 +22,7 @@ struct XcodeListViewRow: View { Spacer() selectControl(for: xcode) + .padding(.trailing, 16) installControl(for: xcode) } .contextMenu { @@ -51,25 +52,34 @@ struct XcodeListViewRow: View { @ViewBuilder private func selectControl(for xcode: Xcode) -> some View { - if xcode.selected { - Image(systemName: "checkmark.circle.fill") - .foregroundColor(.green) - .help("This version is selected as the default") + if xcode.installed { + if xcode.selected { + Image(systemName: "checkmark.circle.fill") + .foregroundColor(.green) + .help("This is the active version") + } else { + Button(action: { appState.select(id: xcode.id) }) { + Image(systemName: "checkmark.circle") + .foregroundColor(.secondary) + } + .buttonStyle(PlainButtonStyle()) + .help("Make this the active version") + } + } else { + EmptyView() } } @ViewBuilder private func installControl(for xcode: Xcode) -> some View { - if xcode.selected { - Button("DEFAULT") { appState.select(id: xcode.id) } - .buttonStyle(AppStoreButtonStyle(primary: false, highlighted: selected)) - .disabled(true) - } else if xcode.installed { - Button("SELECT") { appState.select(id: xcode.id) } - .buttonStyle(AppStoreButtonStyle(primary: false, highlighted: selected)) + if xcode.installed { + Button("OPEN") { appState.open(id: xcode.id) } + .buttonStyle(AppStoreButtonStyle(primary: true, highlighted: selected)) + .help("Open this version") } else { Button("INSTALL") { print("Installing...") } - .buttonStyle(AppStoreButtonStyle(primary: true, highlighted: selected)) + .buttonStyle(AppStoreButtonStyle(primary: false, highlighted: selected)) + .help("Install this version") } } }