The combo drops its padding and turns portrait — a 4:5 field puts the symbol dead center
Owner's first review of the combo rework (2026-08-09): remove the face padding, make the field taller and narrower at about a 4:5 width:height ratio, and center the symbol glyph in its face. All three land in ComboFieldMetrics, so ColorComboControl and SymbolComboControl stay the identical shape they were built to share. - ComboFieldMetrics grows a width figure (height * widthToHeightRatio, 0.8), replacing NSView.noIntrinsicMetric — every combo now carries its own taller, narrower intrinsic size instead of stretching to whatever a caller's frame proposed. - facePaddingH/facePaddingV/glyphPadding are gone; a face fills its zone edge to edge. glyphPointSize is now whichever of the face's own width/height is smaller, with nothing subtracted for padding that no longer exists. - SymbolComboControl.drawFace centers the glyph on both axes — it only ever centered vertically before, despite its own doc comment claiming otherwise. - ComboFieldControl.drawTrigger bounds its chevron square by the smaller of the trigger strip's own width/height, not height alone, since the strip is no longer close to square once the field is much taller than it is wide. - CardSidebarSections drops the sidebar's old '* 0.55' fixed-width frame on both combo rows; each control now sizes itself, and both anchors (card sidebar, board popover) compose the narrower field with no other changes needed. - ComboFieldMetricsTests updated for the new figures, plus a ratio-holds-at-every-size test and a rewritten glyph-fit test matching the no-padding rule. Verification: xcodebuild build succeeded; xcodebuild test -only-testing:KanbanTests — 3220 tests in 559 suites, 3 failures, all PointerLatencyTests (documented locked-screen environmental mode, confirmed unrelated by isolated rerun). Pixel verification unexercised — same locked-screen constraint the first pass hit. Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -223,10 +223,11 @@ struct ComboFieldMetricsTests {
|
||||
@Test("Every figure scales with the body font, and reproduces the shipped numbers at 13pt")
|
||||
func metricsAtTheStandardBody() {
|
||||
let metrics = ComboFieldMetrics.metrics(bodyPointSize: 13)
|
||||
#expect(metrics.height == 18)
|
||||
// 2026-08-09 iteration: taller and narrower than the first pass's 18×(whatever the caller
|
||||
// proposed) bar — `width` is now `ComboFieldMetrics`' own figure, not the caller's.
|
||||
#expect(metrics.height == 36)
|
||||
#expect(metrics.width == 29)
|
||||
#expect(metrics.triggerWidth == 16)
|
||||
#expect(metrics.facePaddingH == 7)
|
||||
#expect(metrics.facePaddingV == 5)
|
||||
#expect(metrics.cornerRadius == 3)
|
||||
#expect(metrics.fieldRadius == 4)
|
||||
#expect(metrics.triggerInset == 2)
|
||||
@@ -237,16 +238,32 @@ struct ComboFieldMetricsTests {
|
||||
let small = ComboFieldMetrics.metrics(bodyPointSize: 11)
|
||||
let large = ComboFieldMetrics.metrics(bodyPointSize: 24)
|
||||
#expect(large.height > small.height)
|
||||
#expect(large.width > small.width)
|
||||
#expect(large.triggerWidth > small.triggerWidth)
|
||||
#expect(large.glyphPointSize > small.glyphPointSize)
|
||||
for metrics in [ComboFieldMetrics.metrics(bodyPointSize: 8), small, large] {
|
||||
#expect(metrics.height >= 1)
|
||||
#expect(metrics.width >= 1)
|
||||
#expect(metrics.triggerWidth >= 1)
|
||||
#expect(metrics.cornerRadius >= 1)
|
||||
#expect(metrics.glyphPointSize >= 1)
|
||||
}
|
||||
}
|
||||
|
||||
/// **The owner's own figure, held still.** "Almost square, about 4:5 ratio" (2026-08-09) is not
|
||||
/// a one-off measurement — `width` is derived from `height` by `widthToHeightRatio`, so this
|
||||
/// holds at every body size rather than only the one somebody happened to check.
|
||||
@Test("The field is taller than wide, at about a 4:5 ratio, from height alone")
|
||||
func fieldIsAlmostSquare() {
|
||||
for size in [8.0, 11.0, 13.0, 17.0, 24.0, 36.0] as [CGFloat] {
|
||||
let metrics = ComboFieldMetrics.metrics(bodyPointSize: size)
|
||||
#expect(metrics.width < metrics.height, "the field should read taller than wide at \(size)pt")
|
||||
let ratio = metrics.width / metrics.height
|
||||
#expect(abs(ratio - ComboFieldMetrics.widthToHeightRatio) < 0.05,
|
||||
"ratio drifted to \(ratio) at \(size)pt, want ~4:5 (0.8)")
|
||||
}
|
||||
}
|
||||
|
||||
/// The field radius runs a point outside the face's so the two rounded rects stay concentric —
|
||||
/// a small thing, and exactly the kind of thing that drifts when two files own it.
|
||||
@Test("The field's radius stays outside the face's")
|
||||
@@ -257,14 +274,20 @@ struct ComboFieldMetricsTests {
|
||||
}
|
||||
}
|
||||
|
||||
/// A glyph must actually fit: the face's height less its own padding is what the symbol is drawn
|
||||
/// at, and a negative or vanishing figure is the bug the 14pt height had.
|
||||
@Test("A glyph fills most of the field's height")
|
||||
/// A glyph must actually fit, and — since the 2026-08-09 iteration removed the face's padding —
|
||||
/// fit *exactly*: `glyphPointSize` is whichever of the face's own two dimensions is smaller, with
|
||||
/// nothing subtracted for a padding that no longer exists. A negative or vanishing figure is the
|
||||
/// bug the first pass's 14pt height had.
|
||||
@Test("A glyph fills its face zone's limiting dimension, with no padding taken out of it")
|
||||
func glyphFillsTheField() {
|
||||
for size in [11.0, 13.0, 17.0, 24.0] as [CGFloat] {
|
||||
let metrics = ComboFieldMetrics.metrics(bodyPointSize: size)
|
||||
#expect(metrics.glyphPointSize > metrics.height * 0.6, "the glyph is lost in its own field at \(size)pt")
|
||||
#expect(metrics.glyphPointSize <= metrics.height)
|
||||
#expect(metrics.glyphPointSize == min(metrics.faceWidth, metrics.height))
|
||||
// The field is taller than wide by construction, so the face reads the same way and
|
||||
// `faceWidth` is the dimension actually doing the limiting.
|
||||
#expect(metrics.faceWidth < metrics.height, "expected the face to be the binding dimension at \(size)pt")
|
||||
#expect(metrics.glyphPointSize == metrics.faceWidth)
|
||||
#expect(metrics.glyphPointSize >= 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user