diff --git a/TablePro/Views/Main/Extensions/MainContentCoordinator+RowOperations.swift b/TablePro/Views/Main/Extensions/MainContentCoordinator+RowOperations.swift index aa957e4db..576a077da 100644 --- a/TablePro/Views/Main/Extensions/MainContentCoordinator+RowOperations.swift +++ b/TablePro/Views/Main/Extensions/MainContentCoordinator+RowOperations.swift @@ -3,11 +3,9 @@ import Foundation extension MainContentCoordinator { func addNewRow() { guard !safeModeLevel.blocksAllWrites, - let tabIndex = tabManager.selectedTabIndex, - tabIndex < tabManager.tabs.count else { return } - - let tab = tabManager.tabs[tabIndex] - guard tab.tableContext.isEditable, tab.tableContext.tableName != nil else { return } + let (tab, tabIndex) = tabManager.selectedTabAndIndex, + tab.tableContext.isEditable, + tab.tableContext.tableName != nil else { return } let tabId = tab.id let columnDefaults = tableRowsStore.tableRows(for: tabId).columnDefaults @@ -37,12 +35,11 @@ extension MainContentCoordinator { func deleteSelectedRows(indices: Set) { guard !safeModeLevel.blocksAllWrites, - let tabIndex = tabManager.selectedTabIndex, - tabIndex < tabManager.tabs.count, - tabManager.tabs[tabIndex].tableContext.isEditable, + let (tab, tabIndex) = tabManager.selectedTabAndIndex, + tab.tableContext.isEditable, !indices.isEmpty else { return } - let tabId = tabManager.tabs[tabIndex].id + let tabId = tab.id var deleteResult = RowOperationsManager.DeleteRowsResult( nextRowToSelect: -1, @@ -77,11 +74,9 @@ extension MainContentCoordinator { func duplicateSelectedRow(index: Int) { guard !safeModeLevel.blocksAllWrites, - let tabIndex = tabManager.selectedTabIndex, - tabIndex < tabManager.tabs.count else { return } - - let tab = tabManager.tabs[tabIndex] - guard tab.tableContext.isEditable, tab.tableContext.tableName != nil else { return } + let (tab, tabIndex) = tabManager.selectedTabAndIndex, + tab.tableContext.isEditable, + tab.tableContext.tableName != nil else { return } let tabId = tab.id let columns = tableRowsStore.tableRows(for: tabId).columns @@ -110,10 +105,8 @@ extension MainContentCoordinator { } func undoInsertRow(at rowIndex: Int) { - guard let tabIndex = tabManager.selectedTabIndex, - tabIndex < tabManager.tabs.count else { return } - - let tabId = tabManager.tabs[tabIndex].id + guard let (tab, _) = tabManager.selectedTabAndIndex else { return } + let tabId = tab.id var undoResult = RowOperationsManager.UndoInsertRowResult( adjustedSelection: selectionState.indices, @@ -135,10 +128,8 @@ extension MainContentCoordinator { } func handleUndoResult(_ result: UndoResult) { - guard let tabIndex = tabManager.selectedTabIndex, - tabIndex < tabManager.tabs.count else { return } + guard let (tab, tabIndex) = tabManager.selectedTabAndIndex else { return } - let tab = tabManager.tabs[tabIndex] let tabId = tab.id var application = RowOperationsManager.UndoApplicationResult(adjustedSelection: nil, delta: .none) @@ -159,10 +150,7 @@ extension MainContentCoordinator { } func copySelectedRowsToClipboard(indices: Set) { - guard let index = tabManager.selectedTabIndex, - !indices.isEmpty else { return } - - let tab = tabManager.tabs[index] + guard let (tab, _) = tabManager.selectedTabAndIndex, !indices.isEmpty else { return } let tableRows = tableRowsStore.tableRows(for: tab.id) rowOperationsManager.copySelectedRowsToClipboard( selectedIndices: indices, @@ -171,10 +159,7 @@ extension MainContentCoordinator { } func copySelectedRowsWithHeaders(indices: Set) { - guard let index = tabManager.selectedTabIndex, - !indices.isEmpty else { return } - - let tab = tabManager.tabs[index] + guard let (tab, _) = tabManager.selectedTabAndIndex, !indices.isEmpty else { return } let tableRows = tableRowsStore.tableRows(for: tab.id) rowOperationsManager.copySelectedRowsToClipboard( selectedIndices: indices, @@ -184,9 +169,7 @@ extension MainContentCoordinator { } func copySelectedRowsAsJson(indices: Set) { - guard let index = tabManager.selectedTabIndex, - !indices.isEmpty else { return } - let tab = tabManager.tabs[index] + guard let (tab, _) = tabManager.selectedTabAndIndex, !indices.isEmpty else { return } let tableRows = tableRowsStore.tableRows(for: tab.id) let rows = indices.sorted().compactMap { idx -> [String?]? in guard idx >= 0, idx < tableRows.count else { return nil } @@ -202,10 +185,8 @@ extension MainContentCoordinator { func pasteRows() { guard !safeModeLevel.blocksAllWrites, - let index = tabManager.selectedTabIndex else { return } - - let tab = tabManager.tabs[index] - guard tab.tabType == .table else { return } + let (tab, tabIndex) = tabManager.selectedTabAndIndex, + tab.tabType == .table else { return } let tabId = tab.id let columns = tableRowsStore.tableRows(for: tabId).columns @@ -226,19 +207,19 @@ extension MainContentCoordinator { let newIndices = Set(pasteResult.pastedRows.map { $0.rowIndex }) selectionState.indices = newIndices - tabManager.tabs[index].selectedRowIndices = newIndices - tabManager.tabs[index].hasUserInteraction = true + tabManager.tabs[tabIndex].selectedRowIndices = newIndices + tabManager.tabs[tabIndex].hasUserInteraction = true querySortCache.removeValue(forKey: tabId) dataTabDelegate?.tableViewCoordinator?.applyDelta(pasteResult.delta) } func updateCellInTab(rowIndex: Int, columnIndex: Int, value: String?) { - guard let index = tabManager.selectedTabIndex else { return } - let tabId = tabManager.tabs[index].id + guard let (tab, tabIndex) = tabManager.selectedTabAndIndex else { return } + let tabId = tab.id let delta = mutateActiveTableRows(for: tabId) { rows in rows.edit(row: rowIndex, column: columnIndex, value: value) } - tabManager.tabs[index].hasUserInteraction = true + tabManager.tabs[tabIndex].hasUserInteraction = true dataTabDelegate?.tableViewCoordinator?.applyDelta(delta) } } diff --git a/TablePro/Views/Results/DataGridCoordinator.swift b/TablePro/Views/Results/DataGridCoordinator.swift index 9e568927a..b2c73c212 100644 --- a/TablePro/Views/Results/DataGridCoordinator.swift +++ b/TablePro/Views/Results/DataGridCoordinator.swift @@ -394,6 +394,13 @@ final class TableViewCoordinator: NSObject, NSTableViewDelegate, NSTableViewData displayCache.removeAll() rebuildVisualStateCache() updateCache() + guard let tableView else { return } + let visibleRange = tableView.rows(in: tableView.visibleRect) + guard visibleRange.length > 0 else { return } + tableView.reloadData( + forRowIndexes: IndexSet(integersIn: visibleRange.location..<(visibleRange.location + visibleRange.length)), + columnIndexes: IndexSet(integersIn: 0.. Fixture { + let tabManager = QueryTabManager() + let coordinator = MainContentCoordinator( + connection: TestFixtures.makeConnection(), + tabManager: tabManager, + changeManager: DataChangeManager(), + filterStateManager: FilterStateManager(), + columnVisibilityManager: ColumnVisibilityManager(), + toolbarState: ConnectionToolbarState() + ) + let delegate = DataTabGridDelegate() + let fake = FakeTableViewCoordinator() + delegate.tableViewCoordinator = fake + coordinator.dataTabDelegate = delegate + + tabManager.addTableTab(tableName: "users") + let tabIndex = tabManager.selectedTabIndex ?? 0 + tabManager.tabs[tabIndex].tableContext.isEditable = true + let tabId = tabManager.tabs[tabIndex].id + + let columns = ["id", "name"] + let rows = (0..