Switch to NavigationSplitView

This commit is contained in:
Matt Kiazyk 2023-12-18 09:00:10 -06:00
parent 638543070b
commit 7070575a32
6 changed files with 148 additions and 63 deletions

View file

@ -114,8 +114,8 @@
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 */; };
E84B7D0D2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E84B7D0C2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift */; };
E84CF8C12B0FEB8300ECA259 /* RuntimesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E84CF8C02B0FEB8300ECA259 /* RuntimesView.swift */; };
E872EE4E2808D4F100D3DD8B /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E872EE502808D4F100D3DD8B /* Localizable.strings */; };
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 +310,9 @@
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>"; };
E84B7D0C2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSplitViewWrapper.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>"; };
E872EE4F2808D4F100D3DD8B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; 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 +373,7 @@
CA452BAF259FD9770072DFA4 /* ProgressIndicator.swift */,
536CFDD3263C9A8000026CE0 /* XcodesSheet.swift */,
53CBAB2B263DCC9100410495 /* XcodesAlert.swift */,
E84B7D0C2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift */,
);
path = Common;
sourceTree = "<group>";
@ -921,6 +922,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 +1042,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 +1229,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 +1287,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;

View 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)
}
}
}

View file

@ -1,6 +1,8 @@
import ErrorHandling
import SwiftUI
import XcodesKit
import Path
import Version
struct MainWindow: View {
@EnvironmentObject var appState: AppState
@ -16,7 +18,7 @@ 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)
.layoutPriority(1)
@ -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
}())
}
}

View file

@ -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,18 @@ 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")
// 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 +90,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
)
)
}

View file

@ -42,6 +42,7 @@ struct XcodeListView: View {
List(visibleXcodes, selection: $selectedXcodeID) { xcode in
XcodeListViewRow(xcode: xcode, selected: selectedXcodeID == xcode.id, appState: appState)
}
.listStyle(.sidebar)
}
}

View file

@ -8193,6 +8193,7 @@
}
},
"Info" : {
"extractionState" : "stale",
"localizations" : {
"ca" : {
"stringUnit" : {
@ -8299,6 +8300,7 @@
}
},
"InfoDescription" : {
"extractionState" : "stale",
"localizations" : {
"ca" : {
"stringUnit" : {
@ -16125,6 +16127,7 @@
}
},
"Search" : {
"extractionState" : "stale",
"localizations" : {
"ca" : {
"stringUnit" : {
@ -16237,6 +16240,7 @@
}
},
"SearchDescription" : {
"extractionState" : "stale",
"localizations" : {
"ca" : {
"stringUnit" : {