crossmate

A collaborative crossword app for iOS
Log | Files | Refs | LICENSE

commit 88de048dde700b624f78bd3215e5d39e229c8504
parent b638eee5c295aa008dbef288a2bb3e5437765eb0
Author: Michael Camilleri <[email protected]>
Date:   Wed, 20 May 2026 18:47:56 +0900

Adjust display of text in grid cells

Grid cells now hide the old word-start corner dots and instead show clue
numbers when the laid-out square size is at least 34 points, using a shared
grid metrics helper so the visibility check matches the custom layout’s
cell-size calculation. Rebus entries can also shrink further before truncating,
making multi-letter rebuses fit better in small cells.

Co-Authored-By: Codex GPT 5.5 <[email protected]>

Diffstat:
MCrossmate/Views/CellView.swift | 37+++++++++++--------------------------
MCrossmate/Views/GridView.swift | 96++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
2 files changed, 91 insertions(+), 42 deletions(-)

diff --git a/Crossmate/Views/CellView.swift b/Crossmate/Views/CellView.swift @@ -6,6 +6,7 @@ struct CellView: View, Equatable { let mark: CellMark let isSelected: Bool let isHighlighted: Bool + let showsNumber: Bool /// Passive cross-reference texture for this cell, or `nil` if the cell /// belongs to no cross-referenced clue. Each group in the puzzle is /// assigned a distinct pattern so separate links read differently. @@ -29,6 +30,7 @@ struct CellView: View, Equatable { && lhs.mark == rhs.mark && lhs.isSelected == rhs.isSelected && lhs.isHighlighted == rhs.isHighlighted + && lhs.showsNumber == rhs.showsNumber && lhs.crossRefPattern == rhs.crossRefPattern && lhs.gridRow == rhs.gridRow && lhs.gridCol == rhs.gridCol @@ -58,18 +60,22 @@ struct CellView: View, Equatable { .stroke(Color.black.opacity(0.55), lineWidth: 1) .padding(1.5) } - if cell.number != nil { - CornerDot() - .fill(Color(.systemGray3)) - } Text(entry) .font(.system(size: 34, weight: .semibold, design: .rounded)) .foregroundStyle(entryStyle) .lineLimit(1) - .minimumScaleFactor(0.2) + .minimumScaleFactor(0.1) .allowsTightening(true) .padding(.horizontal, 2) .frame(maxWidth: .infinity, maxHeight: .infinity) + if showsNumber, let number = cell.number { + Text(String(number)) + .font(.system(size: 13, weight: .semibold, design: .rounded)) + .foregroundStyle(Color.secondary.opacity(0.75)) + .lineLimit(1) + .padding(.top, 3) + .padding(.leading, 4) + } if let triangleColor = cornerTriangleColor { CornerTriangle() .fill(triangleColor) @@ -276,27 +282,6 @@ private struct CrossRefLines: Shape { } } -/// Small dot pinned to the top-leading corner of the cell, sized as a -/// fraction of the shorter cell dimension. Marks cells that start a word. -private struct CornerDot: Shape { - var diameterFraction: CGFloat = 0.15 - var insetFraction: CGFloat = 0.05 - - func path(in rect: CGRect) -> Path { - let side = min(rect.width, rect.height) - let diameter = side * diameterFraction - let inset = side * insetFraction - var path = Path() - path.addEllipse(in: CGRect( - x: rect.minX + inset, - y: rect.minY + inset, - width: diameter, - height: diameter - )) - return path - } -} - /// Right-angled triangle pinned to the top-right corner of the cell, sized /// as a fraction of the shorter cell dimension. Used as a small marker for /// revealed and checkedWrong cells. diff --git a/Crossmate/Views/GridView.swift b/Crossmate/Views/GridView.swift @@ -6,10 +6,19 @@ struct GridView: View { let showsSharedAnnotations: Bool private let spacing: CGFloat = 1 + private let numberVisibilityCellSize: CGFloat = 34 + @State private var laidOutGridSize: CGSize = .zero var body: some View { let width = session.puzzle.width let height = session.puzzle.height + let cellSize = PuzzleGridMetrics.cellSize( + in: laidOutGridSize, + columns: width, + rows: height, + spacing: spacing + ) + let showsCellNumbers = cellSize >= numberVisibilityCellSize let tintByCell: [GridPosition: Color] = showsSharedAnnotations ? remoteTrackTints() : [:] let authorTintByID: [String: Color] = showsSharedAnnotations ? Dictionary( @@ -40,6 +49,7 @@ struct GridView: View { mark: square.mark, isSelected: session.selectedRow == r && session.selectedCol == c, isHighlighted: currentWordCells.contains(pos), + showsNumber: showsCellNumbers, crossRefPattern: cellGroups[pos].map { patternPalette[$0 % patternPalette.count] }, @@ -59,6 +69,11 @@ struct GridView: View { } } .background(Color.black) + .onGeometryChange(for: CGSize.self) { proxy in + proxy.size + } action: { newSize in + laidOutGridSize = newSize + } } /// Builds remote word-tint overlays from each peer's persisted cursor @@ -84,6 +99,59 @@ struct GridView: View { // MARK: - Layout +private enum PuzzleGridMetrics { + static func cellSize( + in size: CGSize, + columns: Int, + rows: Int, + spacing: CGFloat + ) -> CGFloat { + guard size.width > 0, size.height > 0 else { return 0 } + return cellSize( + availableWidth: size.width, + availableHeight: size.height, + columns: columns, + rows: rows, + spacing: spacing + ) + } + + static func cellSize( + for proposal: ProposedViewSize, + columns: Int, + rows: Int, + spacing: CGFloat + ) -> CGFloat { + cellSize( + availableWidth: proposal.width, + availableHeight: proposal.height, + columns: columns, + rows: rows, + spacing: spacing + ) + } + + private static func cellSize( + availableWidth: CGFloat?, + availableHeight: CGFloat?, + columns: Int, + rows: Int, + spacing: CGFloat + ) -> CGFloat { + let cols = CGFloat(columns) + let rs = CGFloat(rows) + let width = availableWidth ?? .infinity + let height = availableHeight ?? .infinity + let widthBased = width.isFinite + ? (width - spacing * (cols + 1)) / cols + : .infinity + let heightBased = height.isFinite + ? (height - spacing * (rs + 1)) / rs + : .infinity + return max(0, min(widthBased, heightBased)) + } +} + /// Lays out puzzle cells in a `rows × columns` grid with a uniform square /// cell size. The cell size is chosen to fit the proposed container, with /// `spacing` points between every cell and around the outer edge (the black @@ -100,7 +168,12 @@ private struct PuzzleGridLayout: Layout { subviews: Subviews, cache: inout () ) -> CGSize { - let cellSize = cellSize(for: proposal) + let cellSize = PuzzleGridMetrics.cellSize( + for: proposal, + columns: columns, + rows: rows, + spacing: spacing + ) let width = cellSize * CGFloat(columns) + spacing * CGFloat(columns + 1) let height = cellSize * CGFloat(rows) + spacing * CGFloat(rows + 1) return CGSize(width: width, height: height) @@ -112,7 +185,12 @@ private struct PuzzleGridLayout: Layout { subviews: Subviews, cache: inout () ) { - let cellSize = cellSize(for: ProposedViewSize(width: bounds.width, height: bounds.height)) + let cellSize = PuzzleGridMetrics.cellSize( + for: ProposedViewSize(width: bounds.width, height: bounds.height), + columns: columns, + rows: rows, + spacing: spacing + ) let gridWidth = cellSize * CGFloat(columns) + spacing * CGFloat(columns + 1) let gridHeight = cellSize * CGFloat(rows) + spacing * CGFloat(rows + 1) let originX = bounds.minX + (bounds.width - gridWidth) / 2 @@ -131,18 +209,4 @@ private struct PuzzleGridLayout: Layout { ) } } - - private func cellSize(for proposal: ProposedViewSize) -> CGFloat { - let cols = CGFloat(columns) - let rs = CGFloat(rows) - let availableWidth = proposal.width ?? .infinity - let availableHeight = proposal.height ?? .infinity - let widthBased = availableWidth.isFinite - ? (availableWidth - spacing * (cols + 1)) / cols - : .infinity - let heightBased = availableHeight.isFinite - ? (availableHeight - spacing * (rs + 1)) / rs - : .infinity - return max(0, min(widthBased, heightBased)) - } }