Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Xcodes/Frontend/Common/NavigationSplitViewWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,20 @@ import SwiftUI
struct NavigationSplitViewWrapper<Sidebar, Detail>: View where Sidebar: View, Detail: View {
private var sidebar: Sidebar
private var detail: Detail
@Binding private var columnVisibility: NavigationSplitViewVisibility

init(
columnVisibility: Binding<NavigationSplitViewVisibility>,
@ViewBuilder sidebar: () -> Sidebar,
@ViewBuilder detail: () -> Detail
) {
self._columnVisibility = columnVisibility
self.sidebar = sidebar()
self.detail = detail()
}

var body: some View {
NavigationSplitView {
NavigationSplitView(columnVisibility: $columnVisibility) {
if #available(macOS 14, *) {
sidebar
.navigationSplitViewColumnWidth(min: 290, ideal: 290)
Expand Down
6 changes: 4 additions & 2 deletions Xcodes/Frontend/MainWindow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ struct MainWindow: View {
@EnvironmentObject var appState: AppState
@State private var selectedXcodeID: Xcode.ID?
@State private var searchText: String = ""
@State private var columnVisibility: NavigationSplitViewVisibility = .automatic
@AppStorage("lastUpdated") private var lastUpdated: Double?
// These two properties should be per-scene state managed by @SceneStorage property wrappers.
// There's currently a bug with @SceneStorage on macOS, though, where quitting the app will discard the values, which removes a lot of its utility.
Expand All @@ -19,7 +20,7 @@ struct MainWindow: View {
@AppStorage("isInstalledOnly") private var isInstalledOnly = false

var body: some View {
NavigationSplitViewWrapper {
NavigationSplitViewWrapper(columnVisibility: $columnVisibility) {
XcodeListView(selectedXcodeID: $selectedXcodeID, searchText: searchText, category: category, isInstalledOnly: isInstalledOnly, architecture: architecture)
.layoutPriority(1)
.alert(item: $appState.xcodeBeingConfirmedForUninstallation) { xcode in
Expand All @@ -33,7 +34,8 @@ struct MainWindow: View {
category: $category,
isInstalledOnly: $isInstalledOnly,
isShowingInfoPane: $isShowingInfoPane,
architecture: $architecture
architecture: $architecture,
isSidebarCollapsed: columnVisibility == .detailOnly
)
} detail: {
Group {
Expand Down
104 changes: 60 additions & 44 deletions Xcodes/Frontend/XcodeList/MainToolbar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,60 +6,74 @@ struct MainToolbarModifier: ViewModifier {
@Binding var isInstalledOnly: Bool
@Binding var isShowingInfoPane: Bool
@Binding var architectures: XcodeListArchitecture
var isSidebarCollapsed = false

func body(content: Content) -> some View {
content
.toolbar { toolbar }
}

@ToolbarContentBuilder
private var toolbar: some ToolbarContent {
ToolbarItemGroup {
ProgressButton(
isInProgress: appState.isUpdating,
action: appState.update
) {
Label("Refresh", systemImage: "arrow.clockwise")
}
.keyboardShortcut(KeyEquivalent("r"))
.help("RefreshDescription")

Spacer()

let isFiltering = isInstalledOnly || category != .all || !architectures.isCurrentMachineDefault
Menu("Filter", systemImage: "line.horizontal.3.decrease.circle") {
Section {
Toggle("Installed Only", systemImage: "arrow.down.app", isOn: $isInstalledOnly) .labelStyle(.titleAndIcon)
if !isSidebarCollapsed {
ToolbarItemGroup {
ProgressButton(
isInProgress: appState.isUpdating,
action: appState.update
) {
Label("Refresh", systemImage: "arrow.clockwise")
}
.help("FilterInstalledDescription")

Section {
Picker("Category", selection: $category) {
Label("All", systemImage: "line.horizontal.3.decrease.circle")
.tag(XcodeListCategory.all)
Label("ReleaseOnly", systemImage: "line.horizontal.3.decrease.circle.fill")
.tag(XcodeListCategory.release)
Label("BetaOnly", systemImage: "line.horizontal.3.decrease.circle.fill")
.tag(XcodeListCategory.beta)
.keyboardShortcut(KeyEquivalent("r"))
.help("RefreshDescription")

Spacer()

let isFiltering = isInstalledOnly || category != .all || !architectures.isCurrentMachineDefault
Menu("Filter", systemImage: filterSymbolName) {
Section {
Toggle("Installed Only", systemImage: "arrow.down.app", isOn: $isInstalledOnly) .labelStyle(.titleAndIcon)
}
}
.help("FilterAvailableDescription")
.disabled(category.isManaged)

Section {
Picker("Architecture", selection: $architectures) {
Label(architecturesLabel(for: .universal), systemImage: "cpu.fill")
.tag(XcodeListArchitecture.universal)
Label(architecturesLabel(for: .appleSilicon), systemImage: "m4.button.horizontal")
.foregroundColor(.accentColor)
.tag(XcodeListArchitecture.appleSilicon)
.help("FilterInstalledDescription")

Section {
Picker("Category", selection: $category) {
Label("All", systemImage: "line.horizontal.3.decrease.circle")
.tag(XcodeListCategory.all)
Label("ReleaseOnly", systemImage: "line.horizontal.3.decrease.circle.fill")
.tag(XcodeListCategory.release)
Label("BetaOnly", systemImage: "line.horizontal.3.decrease.circle.fill")
.tag(XcodeListCategory.beta)
}
}
.help("FilterArchitecturesDescription")
.disabled(architectures.isManaged)
.help("FilterAvailableDescription")
.disabled(category.isManaged)

Section {
Picker("Architecture", selection: $architectures) {
Label(architecturesLabel(for: .universal), systemImage: "cpu.fill")
.tag(XcodeListArchitecture.universal)
Label(architecturesLabel(for: .appleSilicon), systemImage: "m4.button.horizontal")
.foregroundColor(.accentColor)
.tag(XcodeListArchitecture.appleSilicon)
}
.help("FilterArchitecturesDescription")
.disabled(architectures.isManaged)
}
.labelStyle(.titleAndIcon)
}
.labelStyle(.titleAndIcon)
.pickerStyle(.inline)
.menuIndicator(.hidden)
.symbolVariant(isFiltering ? .fill : .none)
}
.pickerStyle(.inline)
.symbolVariant(isFiltering ? .fill : .none)
}
}

/// On macOS 26+ toolbar items already get their own glass background, so the circled symbol is redundant.
private var filterSymbolName: String {
if #available(macOS 26, *) {
return "line.3.horizontal.decrease"
} else {
return "line.horizontal.3.decrease.circle"
}
}

Expand All @@ -73,14 +87,16 @@ extension View {
category: Binding<XcodeListCategory>,
isInstalledOnly: Binding<Bool>,
isShowingInfoPane: Binding<Bool>,
architecture: Binding<XcodeListArchitecture>
architecture: Binding<XcodeListArchitecture>,
isSidebarCollapsed: Bool = false
) -> some View {
modifier(
MainToolbarModifier(
category: category,
isInstalledOnly: isInstalledOnly,
isShowingInfoPane: isShowingInfoPane,
architectures: architecture
architectures: architecture,
isSidebarCollapsed: isSidebarCollapsed
)
)
}
Expand Down
Loading