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:
@@ -48,6 +48,7 @@ struct MainView: View {
|
||||
|
||||
@State private var expandedSchemas: Set<String> = []
|
||||
@State private var expandedTableFolders: Set<String> = []
|
||||
@State private var expandedObjectFolders: Set<String> = []
|
||||
@State private var selectedTableName: String? = nil
|
||||
@State private var outputExpanded = true
|
||||
|
||||
@@ -132,8 +133,9 @@ struct MainView: View {
|
||||
/// A query launched from the global toolbar must reveal its editor and
|
||||
/// results, even when an Administration page currently owns the workspace.
|
||||
private func executeSQLInEditor() {
|
||||
navigatorMode = 1
|
||||
// Make the editor the active workspace before starting execution.
|
||||
administrationPage = nil
|
||||
navigatorMode = 1
|
||||
model.runSQL()
|
||||
// The SQLTextView is created only after the workspace changes. Request
|
||||
// focus on the next main-loop turn, after SwiftUI mounts that view.
|
||||
@@ -538,9 +540,9 @@ struct MainView: View {
|
||||
}
|
||||
}
|
||||
|
||||
folderNode("Views", indent: 28)
|
||||
folderNode("Stored Procedures", indent: 28)
|
||||
folderNode("Functions", indent: 28)
|
||||
schemaObjectFolder("Views", database: db, objects: model.schemaViews[db] ?? [], kind: .view)
|
||||
schemaObjectFolder("Stored Procedures", database: db, objects: model.schemaProcedures[db] ?? [], kind: .procedure)
|
||||
schemaObjectFolder("Functions", database: db, objects: model.schemaFunctions[db] ?? [], kind: .function)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
@@ -597,35 +599,78 @@ struct MainView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func folderNode(
|
||||
_ title: String,
|
||||
indent: CGFloat
|
||||
) -> some View {
|
||||
HStack(spacing: 5) {
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.system(size: 10))
|
||||
.foregroundStyle(.secondary)
|
||||
.frame(width: 13)
|
||||
private enum SchemaObjectKind { case view, procedure, function }
|
||||
|
||||
Image(systemName: "folder")
|
||||
.foregroundStyle(.secondary)
|
||||
.frame(width: 17)
|
||||
private func schemaObjectFolder(_ title: String, database: String, objects: [String], kind: SchemaObjectKind) -> some View {
|
||||
let key = database + "." + title
|
||||
let expanded = expandedObjectFolders.contains(key)
|
||||
return VStack(alignment: .leading, spacing: 0) {
|
||||
HStack(spacing: 5) {
|
||||
Image(systemName: expanded ? "chevron.down" : "chevron.right")
|
||||
.font(.system(size: 10)).foregroundStyle(.secondary).frame(width: 13)
|
||||
Image(systemName: "folder").foregroundStyle(.secondary).frame(width: 17)
|
||||
Text(title).font(.system(size: 12))
|
||||
Text("\(objects.count)").font(.caption2).foregroundStyle(.secondary)
|
||||
Spacer(minLength: 0)
|
||||
}
|
||||
.frame(height: 24)
|
||||
.padding(.leading, 28)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
if expanded { expandedObjectFolders.remove(key) }
|
||||
else {
|
||||
expandedObjectFolders.insert(key)
|
||||
if model.schemaTables[database] == nil { model.loadTables(database: database) }
|
||||
}
|
||||
}
|
||||
|
||||
Text(title)
|
||||
.font(.system(size: 12))
|
||||
|
||||
Spacer(minLength: 0)
|
||||
if expanded {
|
||||
ForEach(objects, id: \.self) { name in
|
||||
HStack(spacing: 5) {
|
||||
Image(systemName: kind == .view ? "tablecells" : "curlybraces")
|
||||
.foregroundStyle(.secondary).frame(width: 17)
|
||||
Text(name).font(.system(size: 12)).lineLimit(1)
|
||||
Spacer(minLength: 0)
|
||||
}
|
||||
.frame(height: 23)
|
||||
.padding(.leading, 49)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture(count: 2) { openSchemaObject(database: database, name: name, kind: kind) }
|
||||
.contextMenu {
|
||||
Button(kind == .view ? "Select First Rows" : "Show Definition") {
|
||||
openSchemaObject(database: database, name: name, kind: kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
if objects.isEmpty {
|
||||
Text("No objects (or metadata unavailable)")
|
||||
.font(.caption2).foregroundStyle(.secondary)
|
||||
.padding(.leading, 49).frame(height: 23)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(height: 24)
|
||||
.padding(.leading, indent)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
private func openSchemaObject(database: String, name: String, kind: SchemaObjectKind) {
|
||||
let db = database.replacingOccurrences(of: "`", with: "``")
|
||||
let object = name.replacingOccurrences(of: "`", with: "``")
|
||||
model.selectedDatabase = database
|
||||
switch kind {
|
||||
case .view: model.sql = "SELECT * FROM `\(db)`.`\(object)` LIMIT 500;"
|
||||
case .procedure: model.sql = "SHOW CREATE PROCEDURE `\(db)`.`\(object)`;"
|
||||
case .function: model.sql = "SHOW CREATE FUNCTION `\(db)`.`\(object)`;"
|
||||
}
|
||||
administrationPage = nil
|
||||
navigatorMode = 1
|
||||
model.runSQL()
|
||||
DispatchQueue.main.async { sqlFocusRequest += 1 }
|
||||
}
|
||||
|
||||
// MARK: - Workspace
|
||||
|
||||
private var workspace: some View {
|
||||
Group {
|
||||
if let page = administrationPage {
|
||||
if navigatorMode == 0, let page = administrationPage {
|
||||
WorkbenchAdministrationView(
|
||||
page: page,
|
||||
profile: model.selected
|
||||
@@ -667,7 +712,10 @@ struct MainView: View {
|
||||
ForEach(model.queryTabs) { tab in
|
||||
HStack(spacing: 8) {
|
||||
Button {
|
||||
administrationPage = nil
|
||||
navigatorMode = 1
|
||||
model.activateQuery(tab.id)
|
||||
DispatchQueue.main.async { sqlFocusRequest += 1 }
|
||||
} label: {
|
||||
Label(tab.title, systemImage: "doc.text")
|
||||
.lineLimit(1)
|
||||
@@ -716,7 +764,7 @@ struct MainView: View {
|
||||
private var queryToolbar: some View {
|
||||
HStack(spacing: 14) {
|
||||
Button {
|
||||
model.runSQL()
|
||||
executeSQLInEditor()
|
||||
} label: {
|
||||
Image(systemName: "bolt.fill")
|
||||
.foregroundStyle(.orange)
|
||||
|
||||
Reference in New Issue
Block a user