Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- Delete key now respects cell-range selection in the data grid, removing all rows covered by the selection instead of ignoring it.
- Right-clicking inside a multi-row or cell-range selection no longer collapses the selection before the context menu appears.
- Oracle connections no longer crash the app during connect. A short or unexpected handshake packet from the server (such as session-setup metadata or an error) now surfaces the error or continues instead of trapping. (#1683)
- MongoDB filters on `_id` and other ObjectId fields now match. A 24-character hex value is matched as an ObjectId as well as a string, so filtering by `_id` returns the row instead of nothing. (#1682)
- The sidebar and inspector keep their width per connection, the sidebar keeps its collapsed state, and the inspector keeps its selected tab, when you quit and reopen the app.
Expand Down
34 changes: 32 additions & 2 deletions TablePro/Views/Results/KeyHandlingTableView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ final class KeyHandlingTableView: NSTableView {

@objc func delete(_ sender: Any?) {
guard coordinator?.isEditable == true else { return }
if let controller = gridSelection, !controller.isEmpty {
coordinator?.delegate?.dataGridDeleteRows(Set(controller.selection.affectedRows))
return
}
guard !selectedRowIndexes.isEmpty else { return }
coordinator?.delegate?.dataGridDeleteRows(Set(selectedRowIndexes))
}
Expand Down Expand Up @@ -279,7 +283,8 @@ final class KeyHandlingTableView: NSTableView {
override func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
switch item.action {
case #selector(delete(_:)), #selector(deleteBackward(_:)):
return coordinator?.isEditable == true && !selectedRowIndexes.isEmpty
let hasGridSelection = gridSelection?.isEmpty == false
return coordinator?.isEditable == true && (hasGridSelection || !selectedRowIndexes.isEmpty)
case #selector(copy(_:)):
let hasGridSelection = gridSelection?.isEmpty == false
return hasGridSelection || !selectedRowIndexes.isEmpty
Expand Down Expand Up @@ -407,7 +412,7 @@ final class KeyHandlingTableView: NSTableView {

private func deleteSelectedRowsIfPossible() {
guard coordinator?.isEditable == true else { return }
guard !selectedRowIndexes.isEmpty else { return }
guard gridSelection?.isEmpty == false || !selectedRowIndexes.isEmpty else { return }
delete(nil)
}

Expand Down Expand Up @@ -521,6 +526,31 @@ final class KeyHandlingTableView: NSTableView {
scrollColumnToVisible(prevColumn)
}

override func rightMouseDown(with event: NSEvent) {
let point = convert(event.locationInWindow, from: nil)
let clickedRow = row(at: point)
if clickedRow >= 0, clickIsInsideSelection(row: clickedRow, point: point) {
window?.makeFirstResponder(self)
if let menu = menu(for: event) {
NSMenu.popUpContextMenu(menu, with: event, for: self)
Comment on lines +532 to +535

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Route context-menu deletes through the grid selection

When this new right-click fast path preserves a cell-range selection, the menu it opens still comes from DataGridRowView; that menu's Delete item calls DataGridRowView.deleteRow(), which only deletes selectedRowIndices or the clicked row, unlike KeyHandlingTableView.delete(_:) which uses selectionController.selection.affectedRows. For a range spanning rows 2–5, right-clicking inside the range and choosing Delete leaves the range highlighted but deletes only the focused/current row instead of the rows covered by the range.

Useful? React with 👍 / 👎.

}
return
}
super.rightMouseDown(with: event)
}

private func clickIsInsideSelection(row clickedRow: Int, point: NSPoint) -> Bool {
if selectedRowIndexes.contains(clickedRow) { return true }
guard let controller = gridSelection, !controller.isEmpty else { return false }
let clickedColumn = column(at: point)
guard clickedColumn >= 0,
let schema = coordinator?.identitySchema,
let dataColumn = DataGridView.dataColumnIndex(for: clickedColumn, in: self, schema: schema) else {
return false
}
return controller.selection.contains(row: clickedRow, column: dataColumn)
}

override func menu(for event: NSEvent) -> NSMenu? {
let point = convert(event.locationInWindow, from: nil)
let clickedRow = row(at: point)
Expand Down
10 changes: 10 additions & 0 deletions TableProTests/Views/Results/CellSelectionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ struct GridSelectionTests {
#expect(selection.boundingRectangle == rect)
}

@Test("a cell-range spanning rows reports every covered row")
func cellRangeAffectsEveryCoveredRow() {
let selection = GridSelection.single(
GridRect(rows: 2...5, columns: 1...3),
anchor: GridCoord(row: 2, column: 1),
active: GridCoord(row: 5, column: 3)
)
#expect(selection.affectedRows == IndexSet(integersIn: 2...5))
}

@Test("multiple rectangles report union of affected rows and columns")
func multipleRectanglesUnion() {
let selection = GridSelection(
Expand Down
Loading