The combo halves its height — the symbol picker gives up its padding, not its glyph

The owner's evening review superseded the same day's 4:5-ratio pass: 'reduce
vertical size of both pickers by 50%, on symbol picker by reducing padding.'

ComboFieldMetrics.height goes back to 1.4 em (18pt at the standard body,
exactly half the 4:5 pass's 36) — both combos get this for free, since it's
shared. width stops being height's own restatement (widthToHeightRatio is
gone); it holds the 4:5 pass's pixel figure as its own em number instead,
because re-deriving it at the new height would starve the trigger strip and
empty the face zone entirely.

SymbolComboControl.drawFace now sizes its glyph off the actual face rect it
is handed at draw time rather than the pure ComboFieldMetrics.glyphPointSize
(removed, along with its doc — it only ever described the control's intrinsic
portrait width, not the stretched-wide shape the card sidebar's row actually
proposes). No inset — SF Symbols carry their own margins — with the sizing
and overshoot-scaling rules split into two pure static functions so the fit
is assertable without a live draw.

ComboFieldMetricsTests updated for the new figures and the new actual-rect
glyph rule; the superseded 4:5-ratio-at-six-sizes test is replaced by one
pinning height/width independently and asserting faceWidth stays positive at
every size the old test used.

Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
2026-08-09 17:23:14 -04:00
parent 5da86dcf05
commit cb0af4b95a
3 changed files with 126 additions and 64 deletions
+32 -2
View File
@@ -432,15 +432,25 @@ final class SymbolComboControl: ComboFieldControl {
/// draw only ever centred vertically and offset from the leading edge by the swatch's own
/// horizontal padding, which read as left-aligned once the glyph was more than a sliver narrower
/// than the face. The owner's review named this directly ("for symbol picker center the symbol").
///
/// **Sized off the actual `rect`, not `ComboFieldMetrics.glyphPointSize`, since the evening
/// 2026-08-09 review.** That figure (now removed) was a pure derivation from the *intrinsic*
/// portrait width, but `sizeThatFits` obeys any finite width SwiftUI proposes, and the card
/// sidebar's row proposes real width so the control renders wide while a glyph pinned at the
/// intrinsic figure stayed small, a glyph floating in visible dead space. That space *was* the
/// "padding" the owner's "reduce vertical size on symbol picker by reducing padding" was naming,
/// so the fix reads the point size off the rect the face actually receives, via `glyphPointSize(
/// forFace:)` and `fittedSize(for:in:)` below pure functions, not inlined here, so the sizing
/// rule is assertable without an actual draw.
override func drawFace(in rect: NSRect) {
guard rect.width > 0, rect.height > 0 else { return }
let name = ItemSymbol.exists(glyphName) ? glyphName : "questionmark.square.dashed"
let config = NSImage.SymbolConfiguration(pointSize: metrics.glyphPointSize, weight: .regular)
let config = NSImage.SymbolConfiguration(pointSize: Self.glyphPointSize(forFace: rect), weight: .regular)
.applying(.init(paletteColors: [glyphTint ?? .labelColor]))
guard let image = NSImage(systemSymbolName: name, accessibilityDescription: nil)?
.withSymbolConfiguration(config)
else { return }
let size = image.size
let size = Self.fittedSize(for: image.size, in: rect)
image.draw(in: NSRect(
x: rect.midX - size.width / 2,
y: rect.midY - size.height / 2,
@@ -448,6 +458,26 @@ final class SymbolComboControl: ComboFieldControl {
height: size.height
))
}
/// The glyph's target point size for a face rect no inset subtracted, so this is simply whichever
/// of the rect's own two dimensions is smaller. SF Symbols already carry their own internal
/// margins, and the owner's evening review asked for the *dead space around* the glyph gone, not a
/// second margin layered on top of the system's own.
static func glyphPointSize(forFace rect: NSRect) -> CGFloat {
min(rect.width, rect.height)
}
/// `imageSize` unchanged, unless it overshoots `rect` on either axis a symbol configured at
/// `glyphPointSize(forFace:)` can still render wider (or, rarely, taller) than that on its long
/// axis, since SF Symbols are not all square glyphs. Scaled down proportionally so the drawn glyph
/// stays fully inside its face rather than clipping at the edges, and never scaled up: a glyph
/// smaller than its face on both axes is left exactly as configured.
static func fittedSize(for imageSize: NSSize, in rect: NSRect) -> NSSize {
guard rect.width > 0, rect.height > 0 else { return imageSize }
let overshoot = max(imageSize.width / rect.width, imageSize.height / rect.height)
guard overshoot > 1 else { return imageSize }
return NSSize(width: imageSize.width / overshoot, height: imageSize.height / overshoot)
}
}
// MARK: - The popover's content