mirror of
https://github.com/XcodesOrg/XcodesApp.git
synced 2026-03-25 08:55:46 +00:00
Merge pull request #34 from RobotsAndPencils/list-style-update
List style update
This commit is contained in:
commit
38756100b7
9 changed files with 248 additions and 103 deletions
15
DECISIONS.md
15
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.
|
||||
|
|
|
|||
|
|
@ -75,8 +75,9 @@
|
|||
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 */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
|
@ -199,8 +200,9 @@
|
|||
CAFBDB942598FE96003DCC5A /* FocusedValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusedValues.swift; sourceTree = "<group>"; };
|
||||
CAFBDBA525990C76003DCC5A /* SimpleXPCApp.LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = SimpleXPCApp.LICENSE; sourceTree = "<group>"; };
|
||||
CAFBDC4D2599B33D003DCC5A /* MainToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainToolbar.swift; sourceTree = "<group>"; };
|
||||
CAFBDC67259A308B003DCC5A /* InspectorPane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorPane.swift; sourceTree = "<group>"; };
|
||||
CAFBDC67259A308B003DCC5A /* InfoPane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoPane.swift; sourceTree = "<group>"; };
|
||||
CAFBDC6B259A3098003DCC5A /* View+Conditional.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Conditional.swift"; sourceTree = "<group>"; };
|
||||
CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeListViewRow.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
|
@ -305,11 +307,12 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
CA39711824495F0E00AFFB77 /* AppStoreButtonStyle.swift */,
|
||||
CAFBDC67259A308B003DCC5A /* InspectorPane.swift */,
|
||||
CAFBDC67259A308B003DCC5A /* InfoPane.swift */,
|
||||
CAFBDC4D2599B33D003DCC5A /* MainToolbar.swift */,
|
||||
CA44901E2463AD34003D8213 /* Tag.swift */,
|
||||
CAE42486259A68A300B8B246 /* XcodeListCategory.swift */,
|
||||
CAD2E7A32449574E00113D76 /* XcodeListView.swift */,
|
||||
CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */,
|
||||
);
|
||||
path = XcodeList;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -651,10 +654,11 @@
|
|||
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 */,
|
||||
CAFFFED8259CDA5000903F81 /* XcodeListViewRow.swift in Sources */,
|
||||
CABFA9C72592EEEA00380FEE /* Entry+.swift in Sources */,
|
||||
CAE42487259A68A300B8B246 /* XcodeListCategory.swift in Sources */,
|
||||
CABFAA2C2592FBFC00380FEE /* SettingsView.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
@ -170,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(
|
||||
|
|
@ -201,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(
|
||||
|
|
@ -227,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(
|
||||
|
|
@ -253,7 +250,7 @@ struct InspectorPane_Previews: PreviewProvider {
|
|||
})
|
||||
.previewDisplayName("Populated, Uninstalled")
|
||||
|
||||
InspectorPane(selectedXcodeID: nil)
|
||||
InfoPane(selectedXcodeID: nil)
|
||||
.environmentObject(configure(AppState()) {
|
||||
$0.allXcodes = [
|
||||
]
|
||||
|
|
@ -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]))
|
||||
|
|
|
|||
|
|
@ -32,56 +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(selectedXcodeID == xcode.id ? Color(NSColor.selectedMenuItemTextColor) : Color(NSColor.secondaryLabelColor))
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
}
|
||||
.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)
|
||||
XcodeListViewRow(xcode: xcode, selected: selectedXcodeID == xcode.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
112
Xcodes/Frontend/XcodeList/XcodeListViewRow.swift
Normal file
112
Xcodes/Frontend/XcodeList/XcodeListViewRow.swift
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
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)
|
||||
.padding(.trailing, 16)
|
||||
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.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.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: false, highlighted: selected))
|
||||
.help("Install this version")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue