Skip to content

Scale box button brackets to the content height - #584

Open
LuciusChen wants to merge 2 commits into
zevlg:masterfrom
LuciusChen:fix/scale-svg-button-brackets
Open

Scale box button brackets to the content height#584
LuciusChen wants to merge 2 commits into
zevlg:masterfrom
LuciusChen:fix/scale-svg-button-brackets

Conversation

@LuciusChen

@LuciusChen LuciusChen commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Problem

Box button brackets are SVG images of exactly 1 char height. When the
button body or another glyph earlier on the same line renders taller than
one default-font line — color emoji glyphs (e.g. Apple Color Emoji has
noticeably larger ascent/descent than monospace fonts), CJK fonts, custom
emoji / avatar images — the screen line grows, but the brackets stay 1
char high. Reactions and inline keyboard buttons end up with content
sticking out above and below the button "pill". The same issue affects
ordinary UI buttons such as Download when a taller media icon precedes
the button on its line.

Before

image image image image image image

Approach

This PR introduces a content value for the :height bracket property:

(reaction
 :left-bracket ("(" :width 0.75 :height content :rx 0.5)
 :right-bracket (")" :width 1.0 :height content :rx 0.5 :margin 0.25)
 ...)

With :height content, bracket height and baseline are measured from
the real rendering of the current line prefix and button body

(telega-box-button--content-metrics):

  1. Line height is measured with buffer-text-pixel-size in a work
    buffer. Measuring the actual redisplay result (instead of guessing via
    per-character font-at, which bypasses emoji compositions such as
    VS16/ZWJ sequences) makes emoji compositions, embedded images (custom
    emoji, sender avatars) and (raise ...)-displayed text all "just
    work". An anchor char in the default face is appended, since screen
    line metrics are max(ascents) + max(descents) over all glyphs on the
    line.

  2. Baseline position is recovered with a probe: appending an
    :ascent 100 space of the measured line height grows the line by
    exactly the line's descent, so ascent = 2*H1 - H2. The resulting
    :ascent percentage is ceiling'ed — image ascent is truncated as
    floor(percent * height / 100) on display, so rounding down here
    would place the bracket 1px lower, with content peeking out above the
    button.

  3. Geometry. The SVG viewport stays 1 char high and is
    proportionally scaled up to the displayed :height, so bracket caps
    keep their shape, growing wider for taller content (Telegram-like pill
    look). Two pixel-alignment details:

    • the proportional scale is compensated in the SVG outline width, so
      the outline is exactly :outline-width pixels on display;
    • the mask hole is inset by exactly the outline width (previously it
      was inset by ~1.5x due to half-pixel offsets plus the hole's own
      stroke).

    Both make the bracket's visible background and outline pixel-aligned
    with the button body, where the horizontal outline lines are drawn by
    the :box of telega-box-button--body-face.

Since the left bracket is inserted before the body is known,
telega-ins--with-style re-generates the left bracket image after the
body has been inserted and replaces its display property. Generated
images are cached with the computed height/ascent as part of the cache
key.

:height content is enabled for the reaction (and, via inheritance,
reaction-chosen / reaction-paid), telega-ui (including comments
and file actions such as Download), iv and keyboard-* styles.

After

image image image image image image

Compatibility

  • On Emacs without buffer-text-pixel-size (< 29.1) metrics return nil
    and brackets fall back to the previous fixed 1 char height — no
    behavior change.
  • Styles that do not declare :height content (e.g. default) are
    unaffected: for height = 1 all geometry reduces to the previous
    formulas.
  • Plain-text content measures exactly 1 char high, so buttons with
    ordinary labels look the same as before.

Testing

  • 3 new ERT tests (telega-box-button-content-metrics,
    telega-box-button-content-height-inserter,
    telega-box-button-bracket-metrics) cover the metrics math (including
    the ceiling ascent regression), same-line prefix preservation, left
    bracket re-generation, telega-ui inheritance and generated image
    specs. Full suite passes: 26/26.
  • Rendering verified interactively across font sizes (PragmataPro Mono
    10/14/16/20 + Apple Color Emoji + CJK font): bracket background
    extents are pixel-identical to the button body, measured from actual
    window captures.

🤖 Generated with Claude Code

@zevlg

zevlg commented Jul 26, 2026

Copy link
Copy Markdown
Owner

This looks very promissing! let me first check this locally before merging it in

@LuciusChen
LuciusChen force-pushed the fix/scale-svg-button-brackets branch 3 times, most recently from 75bb8be to 485e65e Compare July 28, 2026 03:05
Content rendered with fonts taller than the default one (color emoji, CJK) or with embedded images makes the screen line taller than 1 char.  This can happen inside a button or earlier on the same line, while bracket images remain exactly 1 char high, so reactions, UI and keyboard buttons look broken.

Introduce the `content' value for the `:height' bracket property.  Measure height and baseline from the real rendering of the current line prefix and button body with `buffer-text-pixel-size'.  An anchor char in the default face preserves the max-ascent plus max-descent metrics used by redisplay.

Recover the baseline by appending an `:ascent 100' probe of the measured line height.  Use `ceiling' for the resulting percentage because image ascent is truncated during display.

Keep the SVG viewport 1 char high and scale it proportionally to the displayed height.  Compensate the outline width for that scale and align the mask hole with the body outline.

Enable content sizing for `reaction', `telega-ui' (including comments and file actions), `iv' and `keyboard-*' styles.  Emacs versions without `buffer-text-pixel-size' keep the previous fixed-height behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@LuciusChen
LuciusChen force-pushed the fix/scale-svg-button-brackets branch from 485e65e to 96fa46a Compare July 28, 2026 07:01
@LuciusChen

Copy link
Copy Markdown
Contributor Author

I've tested all the scenarios shown in the screenshots above, and they now work as expected.

@LuciusChen LuciusChen closed this Jul 28, 2026
@LuciusChen LuciusChen reopened this Jul 28, 2026
@LuciusChen

Copy link
Copy Markdown
Contributor Author

Added an explicit :background nil to SVG bracket images so their transparent area can follow cursor-face highlighting instead of retaining the passive background. The full test suite still passes (26/26).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants