Skip to content

ADR 0040: Fix italic glyphs clipped at the top

Status

Accepted

Context

User-reported bug: an italic word inside parens like (int) looked like its top was "eaten." In practice this was worse than cosmetic — italic void was rendering legibly as voia: the ascender of the italic d was sheared off, leaving just the rounded bowl, which reads as an a.

drawLine() draws each syntax-highlight run with painter.drawText(QRect(x, y, runWidth, m_lineHeight), Qt::AlignLeft | Qt::AlignVCenter, text). m_lineHeight is set once, in applyConfig(), from the plain font's metrics (m_metrics.height()) — never recomputed per style. QPainter::drawText(QRect, ...) clips text to the given rectangle by default (Qt::TextDontClip is not set). Italic (and to a lesser extent bold) variants of the same font can report a taller ascent than the plain variant — real for most fonts, and closer to guaranteed for a synthetic/oblique italic that some platforms fall back to when a font has no true italic face. Centered with Qt::AlignVCenter inside a box sized for the plain font, an italic run's ascender extends above the top edge of that box and gets clipped.

Decision

Added Qt::TextDontClip to both drawText calls in editor_viewport.cpp (the per-run text draw in drawLine(), and the gutter line-number draw, for the same defensive reason even though plain digits are unlikely to trigger it in practice). The per-run rect's job was always alignment/positioning, not clipping — the outer paintEvent() clip region (docs/adr/0014) already keeps painting inside the viewport, so there was never a reason for a second, tighter clip at the per-run level.

Verified live

Rebuilt and opened a real .c file with int main(void) { and (int)3.5 on screen. Before the fix, italic void visibly read as voia. After: both void and the int inside the cast render with their full, uncut glyphs, confirmed by a zoomed screenshot crop.

Consequences

No downside found: nothing in this codebase relied on per-run clipping (a run's rect width already exactly matches its own measured advance, so there's no overflow to clip horizontally either — this only ever affected the vertical case). All 9/9 core tests still pass (unrelated to this GUI-only change, but re-run as a sanity check).