Improved progress display

Previously, the progress bar updated only after completing an entire table.

For large tables such as LIBARC, this could make the application appear to have stopped responding.

Phase 10 updates the interface while rows are being exported.
This commit is contained in:
Mark Gregory
2026-10-16 22:40:35 +01:00
parent 9562afddf0
commit 3f3c7cb0ba
42 changed files with 306 additions and 346 deletions
+52 -7
View File
@@ -30,6 +30,9 @@ struct MainView: View {
@State private var resultFilter = ""
@State private var resultSortColumn = 0
@State private var resultSortAscending = true
@State private var hiddenResultColumns: Set<Int> = []
@State private var resultPage = 0
private let resultPageSize = 1_000
@State private var showingEditor = false
@State private var showingDatabaseTransfer = false
@State private var showingQueryHistory = false
@@ -814,6 +817,21 @@ struct MainView: View {
Button(resultSortAscending ? "Ascending" : "Descending") {
resultSortAscending.toggle()
}
Menu("Columns") {
Button("Show All Columns") { hiddenResultColumns.removeAll() }
Divider()
ForEach(model.result.columns.indices, id: \.self) { index in
Button {
if hiddenResultColumns.contains(index) {
hiddenResultColumns.remove(index)
} else {
hiddenResultColumns.insert(index)
}
} label: {
Label(model.result.columns[index], systemImage: hiddenResultColumns.contains(index) ? "square" : "checkmark.square")
}
}
}
Button("Export CSV") { exportResultCSV() }
}
Text(model.queryDurationText)
@@ -837,13 +855,36 @@ struct MainView: View {
}
} else {
resultGrid
HStack(spacing: 12) {
Text("\(displayedRows.count) matching rows · showing \(min(resultPage * resultPageSize + 1, displayedRows.count))–\(min((resultPage + 1) * resultPageSize, displayedRows.count))")
.foregroundStyle(.secondary)
Spacer()
Button("Previous") { resultPage = max(0, resultPage - 1) }
.disabled(resultPage == 0)
Text("Page \(resultPage + 1) / \(max(1, (displayedRows.count + resultPageSize - 1) / resultPageSize))")
Button("Next") { resultPage += 1 }
.disabled((resultPage + 1) * resultPageSize >= displayedRows.count)
}
.font(.system(size: 11))
.padding(.horizontal, 12)
.padding(.vertical, 5)
.background(WB.header)
}
}
.onChange(of: resultFilter) { _, _ in resultPage = 0 }
.onChange(of: resultSortColumn) { _, _ in resultPage = 0 }
.onChange(of: resultSortAscending) { _, _ in resultPage = 0 }
.onChange(of: model.result.columns) { _, _ in
resultPage = 0
hiddenResultColumns.removeAll()
}
}
private var resultGrid: some View {
NativeResultGrid(columns: model.result.columns, rows: displayedRows)
let rows = displayedRows
let start = min(resultPage * resultPageSize, rows.count)
let end = min(start + resultPageSize, rows.count)
return NativeResultGrid(columns: model.result.columns, rows: Array(rows[start..<end]), hiddenColumnIndices: hiddenResultColumns)
}
private var displayedRows: [[String]] {
@@ -1348,6 +1389,7 @@ struct NativeResultGrid: NSViewRepresentable {
let columns: [String]
let rows: [[String]]
var hiddenColumnIndices: Set<Int> = []
func makeCoordinator() -> Coordinator {
Coordinator()
@@ -1385,7 +1427,7 @@ struct NativeResultGrid: NSViewRepresentable {
table.delegate = context.coordinator
table.dataSource = context.coordinator
configureColumns(table)
configureColumns(table, hiddenIndices: hiddenColumnIndices)
scroll.documentView = table
table.reloadData()
@@ -1406,19 +1448,22 @@ struct NativeResultGrid: NSViewRepresentable {
context.coordinator.columns = columns
context.coordinator.rows = rows
if oldColumns != columns {
configureColumns(table)
if oldColumns != columns || Set(table.tableColumns.compactMap { column -> Int? in
let identifier = column.identifier.rawValue
return identifier.hasPrefix("column_") ? Int(identifier.dropFirst(7)) : nil
}) != Set(columns.indices).subtracting(hiddenColumnIndices) {
configureColumns(table, hiddenIndices: hiddenColumnIndices)
}
table.reloadData()
}
private func configureColumns(_ table: NSTableView) {
private func configureColumns(_ table: NSTableView, hiddenIndices: Set<Int>) {
for column in Array(table.tableColumns) {
table.removeTableColumn(column)
}
for (index, name) in columns.enumerated() {
for (index, name) in columns.enumerated() where !hiddenIndices.contains(index) {
let identifier = NSUserInterfaceItemIdentifier(
"column_\(index)"
)