Use thin header dividers and a single cell editing border
Reviewed by GPT-6 Astra. Visually verified the editing fixture; native typing/resize tests and iOS simulator build pass.
4 files changed,
+18
−11
native/apple/Sources/SheetsKit/SheetCellEditor.swift+2 −0native/apple/Sources/SheetsKit/SheetGrid.swift+5 −5native/apple/Sources/SheetsKit/SheetResizeHandle.swift+6 −1native/apple/Tests/SheetsKitTests/SheetNativeInteractionTests.swift+5 −5
CI passed — run details
diff --git a/native/apple/Sources/SheetsKit/SheetCellEditor.swift b/native/apple/Sources/SheetsKit/SheetCellEditor.swift| index 5871425..162aef2 100644 |
| --- a/native/apple/Sources/SheetsKit/SheetCellEditor.swift |
| +++ b/native/apple/Sources/SheetsKit/SheetCellEditor.swift |
| @@ -14,6 +14,8 @@ struct SheetCellEditor: NSViewRepresentable { |
| func makeNSView(context: Context) -> CellTextField { |
| let field = CellTextField() |
| field.isBordered = false; field.drawsBackground = false |
| + // The cell already draws its selection border around the full cell. |
| + field.focusRingType = .none |
| field.font = .systemFont(ofSize: 13) |
| field.delegate = context.coordinator |
| field.stringValue = text |
diff --git a/native/apple/Sources/SheetsKit/SheetGrid.swift b/native/apple/Sources/SheetsKit/SheetGrid.swift| index 11fa242..d01be06 100644 |
| --- a/native/apple/Sources/SheetsKit/SheetGrid.swift |
| +++ b/native/apple/Sources/SheetsKit/SheetGrid.swift |
| @@ -14,14 +14,14 @@ struct SheetGrid: View { |
| Section { |
| ForEach(0..<model.rows, id: \.self) { row in |
| HStack(spacing: 0) { |
| - VStack(spacing: 0) { |
| + ZStack(alignment: .bottom) { |
| Button { selectRow(row) } label: { |
| - Text(String(row + 1)).frame(width: 48, height: max(0, height(row) - 8)).background(.quaternary) |
| + Text(String(row + 1)).frame(width: 48, height: height(row)).background(.quaternary) |
| }.buttonStyle(.plain).accessibilityLabel(String(format: sheetsMacString("Select row %d"), row + 1)) |
| #if os(macOS) |
| resizeHandle(axis: "row", index: row).frame(width: 48, height: 8) |
| #else |
| - Rectangle().fill(.secondary.opacity(0.15)).frame(width: 48, height: 8) |
| + Color.clear.frame(width: 48, height: 8) |
| .contentShape(.rect).gesture(resizeGesture(axis: "row", index: row)) |
| .accessibilityHidden(true) |
| #endif |
| @@ -40,10 +40,10 @@ struct SheetGrid: View { |
| HStack(spacing: 0) { |
| Text("").frame(width: 48, height: 32) |
| ForEach(0..<model.cols, id: \.self) { col in |
| - HStack(spacing: 0) { |
| + ZStack(alignment: .trailing) { |
| Button { selectColumn(col) } label: { |
| Text(Coordinate(r: 0, c: col).ref.dropLast()) |
| - .frame(width: max(0, width(col) - 8), height: 32) |
| + .frame(width: width(col), height: 32) |
| .contentShape(.rect) |
| }.buttonStyle(.plain) |
| .accessibilityLabel(String(format: sheetsMacString("Select column %@"), String(Coordinate(r: 0, c: col).ref.dropLast()))) |
diff --git a/native/apple/Sources/SheetsKit/SheetResizeHandle.swift b/native/apple/Sources/SheetsKit/SheetResizeHandle.swift| index 36d1cc0..c1ef577 100644 |
| --- a/native/apple/Sources/SheetsKit/SheetResizeHandle.swift |
| +++ b/native/apple/Sources/SheetsKit/SheetResizeHandle.swift |
| @@ -14,7 +14,12 @@ struct SheetResizeHandle: NSViewRepresentable { |
| final class Handle: NSControl { |
| override func draw(_ dirtyRect: NSRect) { |
| NSColor.secondaryLabelColor.withAlphaComponent(0.15).setFill() |
| - bounds.fill() |
| + // Keep the 8-point mouse target, but paint only the grid boundary. |
| + let pixel = 1 / (window?.backingScaleFactor ?? 2) |
| + let rule = actions?.vertical == true |
| + ? NSRect(x: bounds.minX, y: bounds.minY, width: bounds.width, height: pixel) |
| + : NSRect(x: bounds.maxX - pixel, y: bounds.minY, width: pixel, height: bounds.height) |
| + rule.fill() |
| } |
| var actions: SheetResizeHandle? |
| private var start: NSPoint? |
diff --git a/native/apple/Tests/SheetsKitTests/SheetNativeInteractionTests.swift b/native/apple/Tests/SheetsKitTests/SheetNativeInteractionTests.swift| index 7b33d64..8ff1ddd 100644 |
| --- a/native/apple/Tests/SheetsKitTests/SheetNativeInteractionTests.swift |
| +++ b/native/apple/Tests/SheetsKitTests/SheetNativeInteractionTests.swift |
| @@ -31,6 +31,11 @@ final class SheetNativeInteractionTests: XCTestCase { |
| key("e", code: 14, window: window) |
| try await Task.sleep(for: .milliseconds(100)) |
| XCTAssertEqual(model.draft, "he", "The initial character must not be selected and overwritten") |
| + if ProcessInfo.processInfo.environment["OFICINA_CAPTURE_FIXTURES"] == "1", |
| + let bitmap = host.bitmapImageRepForCachingDisplay(in: host.bounds) { |
| + host.cacheDisplay(in: host.bounds, to: bitmap) |
| + try bitmap.representation(using: .png, properties: [:])?.write(to: URL(fileURLWithPath: "/tmp/oficina-sheets-grid-fixture.png")) |
| + } |
| key("\t", code: 48, window: window) |
| try await Task.sleep(for: .milliseconds(500)) |
| XCTAssertEqual(model.selection.start, Coordinate(r: 0, c: 0)) |
| @@ -42,11 +47,6 @@ final class SheetNativeInteractionTests: XCTestCase { |
| try await Task.sleep(for: .milliseconds(100)) |
| XCTAssertFalse(model.hasUnconfirmedChanges) |
| XCTAssertEqual(model.selection.start, Coordinate(r: 0, c: 0)) |
| - if ProcessInfo.processInfo.environment["OFICINA_CAPTURE_FIXTURES"] == "1", |
| - let bitmap = host.bitmapImageRepForCachingDisplay(in: host.bounds) { |
| - host.cacheDisplay(in: host.bounds, to: bitmap) |
| - try bitmap.representation(using: .png, properties: [:])?.write(to: URL(fileURLWithPath: "/tmp/oficina-sheets-grid-fixture.png")) |
| - } |
| // Column B header then row 2 header select complete ranges. |
| click(NSPoint(x: 200, y: 375), window: window) |
| try await Task.sleep(for: .milliseconds(100)) |