Two widths, one height — the symbol picker squares to 5:4, colour holds 6:4, and None's slash follows the swatch out of the grid
The owner's follow-up review on the shipped rectangles (Pipeline card 5004c540): height was close, so it stays exactly 2.1em on both controls; width now diverges by role via PickerRectMetrics.WidthRatio (color 1.5, symbol 1.25) instead of one shared multiplier. The symbol glyph gets a small em-derived inset back (SymbolGlyphControl.glyphInset) — a sliver of breath, not the old padded ring — applied to the face rect before sizing and fitting, still centred on the same midpoint. The collapsed colour swatch reuses ColorSwatchNoneStrike's own geometry to draw the popover's None slash whenever the stored value is nil or unresolvable, rather than sitting empty. Both mounting anchors (the card sidebar's side-by-side columns, the board popover beside the rename field) pick up .fixedSize() so the controls render at their own intrinsic size instead of stretching into whatever slack an HStack proposal leaves them. KanbanTests/SymbolCatalogTests.swift and ColorSwatchPickerTests.swift updated for the per-role widths, the shared-height claim, the glyph inset rule, and the None-face predicate.
This commit is contained in:
@@ -374,7 +374,7 @@ private struct SymbolGlyphView: NSViewRepresentable {
|
||||
}
|
||||
|
||||
func updateNSView(_ control: SymbolGlyphControl, context: Context) {
|
||||
control.metrics = .current
|
||||
control.metrics = .current(.symbol)
|
||||
control.isEnabled = isEnabled
|
||||
control.glyphName = resolvedName
|
||||
control.glyphTint = currentColor.flatMap(Palette.nsColor(for:))
|
||||
@@ -441,15 +441,26 @@ final class SymbolGlyphControl: PickerRectControl {
|
||||
/// 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.
|
||||
///
|
||||
/// **A sliver of padding returns, on purpose, in the 2026-08-10 follow-up.** The edge-to-edge
|
||||
/// draw above went further than the owner's original "reduce padding" asked, once the rectangle
|
||||
/// itself was squared up — a glyph with no breath at all reads as clipped rather than centred. Fix
|
||||
/// is a small em-derived inset (`glyphInset(bodyPointSize:)`) subtracted from the face rect
|
||||
/// *before* sizing and fitting — not a fixed pixel margin, so it scales with the rest of the
|
||||
/// control's font-derived geometry, and not re-added to the drawn image's position, since the
|
||||
/// inset rect is centred on the same midpoint as the uninset one.
|
||||
override func drawFace(in rect: NSRect) {
|
||||
guard rect.width > 0, rect.height > 0 else { return }
|
||||
let inset = Self.glyphInset(bodyPointSize: CardWindowMetrics.bodyPointSize)
|
||||
let faceRect = rect.insetBy(dx: inset, dy: inset)
|
||||
guard faceRect.width > 0, faceRect.height > 0 else { return }
|
||||
let name = ItemSymbol.exists(glyphName) ? glyphName : "questionmark.square.dashed"
|
||||
let config = NSImage.SymbolConfiguration(pointSize: Self.glyphPointSize(forFace: rect), weight: .regular)
|
||||
let config = NSImage.SymbolConfiguration(pointSize: Self.glyphPointSize(forFace: faceRect), weight: .regular)
|
||||
.applying(.init(paletteColors: [glyphTint ?? .labelColor]))
|
||||
guard let image = NSImage(systemSymbolName: name, accessibilityDescription: nil)?
|
||||
.withSymbolConfiguration(config)
|
||||
else { return }
|
||||
let size = Self.fittedSize(for: image.size, in: rect)
|
||||
let size = Self.fittedSize(for: image.size, in: faceRect)
|
||||
image.draw(in: NSRect(
|
||||
x: rect.midX - size.width / 2,
|
||||
y: rect.midY - size.height / 2,
|
||||
@@ -458,14 +469,26 @@ final class SymbolGlyphControl: PickerRectControl {
|
||||
))
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// The glyph's target point size for a face rect — no inset subtracted here (the caller already
|
||||
/// applied `glyphInset(bodyPointSize:)` to `rect` before handing it over), 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 — the 2026-08-10 sliver is that
|
||||
/// second margin reintroduced deliberately, at a much smaller, explicit figure.
|
||||
static func glyphPointSize(forFace rect: NSRect) -> CGFloat {
|
||||
min(rect.width, rect.height)
|
||||
}
|
||||
|
||||
/// **"A sliver of padding around the symbol"** (owner's 2026-08-10 follow-up) — a small, visible
|
||||
/// breath between the glyph and the face's own edge/border, not a ring wide enough to shrink the
|
||||
/// glyph noticeably. `0.18` em sits at the middle of the owner's named 0.15–0.2 range, and — like
|
||||
/// every other figure on this control — is a multiple of the body point size rather than a flat
|
||||
/// pixel constant (10-accessibility.md's full-relative-scaling rule). A pure static function, not
|
||||
/// inlined into `drawFace`, so the figure is assertable without an actual draw.
|
||||
static func glyphInset(bodyPointSize: CGFloat) -> CGFloat {
|
||||
max(1, (bodyPointSize * 0.18).rounded())
|
||||
}
|
||||
|
||||
/// `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
|
||||
|
||||
Reference in New Issue
Block a user