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:
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))
- }
}