Skip to content

Add :icon: escapes, resolved through Theme::icons, on every Font - #58

Merged
jdolan merged 8 commits into
mainfrom
feature/icon-escapes
Sep 6, 2026
Merged

Add :icon: escapes, resolved through Theme::icons, on every Font#58
jdolan merged 8 commits into
mainfrom
feature/icon-escapes

Conversation

@jdolan

@jdolan jdolan commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • A registered icon renders inline in any Text as :name:, on both the bitmap and TrueType paths, with no opt-in, matching color escapes. Unregistered names stay literal, so 12:30:45 is untouched.
  • ImageAtlas gains named images (addImageWithName / imageWithName) and a generation counter. Theme::addIcon registers an Image under a name and compiles the icon atlas; Theme::icon looks one up.
  • MVC_IconEscapeLength defines the escape syntax once, shared by Text, Font+Bitmap and TextView.
  • TrueType path: MVC_LayoutText resolves escapes into the layout string plus spans. An icon occupies a run of non-breaking placeholder characters at least a line height wide, measured with the same engine that lays out and renders the text, and is drawn as a square of the line height centered in that slot. Coda lacks U+00A0, so the placeholder codepoint is chosen per Font and cached on it.
  • Bitmap path: an icon token advances by whole cells and draws centered in its slot.
  • A Text tracks the atlas generation it was prepared against, so icons registered later, a Theme swap, or window attachment re-resolve it.
  • Text::sizeText measures arbitrary text as the Text would render it. TextView uses it for the caret (fixing a pre-existing bitmap-metrics mismatch) and treats a registered :icon: as one cursor unit.
  • HUD registers its icons by name and restores the heart on the health readout.

Test plan

  • make -j builds clean
  • make check passes (7 suites; new cases in ImageAtlas and Text)
  • Examples/HUD and Examples/Hello run without error
  • Visual: HUD heart centered in its whole-cell slot; :heart: on a Coda line stays glued to its word when narrowed; typing :heart: in an Input renders inline and Backspace removes it as one unit

🤖 Generated with Claude Code

A registered icon renders inline in any Text as :name:, on both the bitmap and TrueType paths,
with no opt-in, as color escapes have since 2.4.0. Unregistered names stay literal.

ImageAtlas gains named images (addImageWithName / imageWithName) and a generation counter;
Theme::addIcon registers an Image under a name and compiles the icon atlas, and Theme::icon
looks one up. MVC_IconEscapeLength defines the syntax once for Text, Font+Bitmap and TextView.

On the TrueType path, MVC_LayoutText resolves escapes into the string to lay out plus the spans
to draw it as; an icon occupies a run of non-breaking placeholder characters at least a line
height wide, measured with the same engine that lays out and renders the text, and is drawn as
a square of the line height centered in that slot. On the bitmap path an icon token advances by
whole cells. A Text tracks the atlas generation it was prepared against, so icons registered
later or a Theme swap re-resolve it. Text::sizeText measures arbitrary text as the Text would
render it; TextView uses it for the caret and treats a registered :icon: as one cursor unit.

HUD registers its icons by name and restores the heart on the health readout.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 6, 2026 01:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Icon rendering does not currently match the PR’s stated “matching color escapes” behavior and the TrueType wrap-fragment selection can misplace icons under narrow wrap widths.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds inline :icon: escape support across Text rendering and caret navigation by introducing named icons in ImageAtlas, Theme-level icon registration/lookup, and shared parsing/layout helpers so both TrueType and bitmap font paths can resolve and render icons consistently.

Changes:

  • Introduces named images + generation tracking in ImageAtlas, and Theme APIs addIcon / icon for registering and resolving icons.
  • Adds MVC_IconEscapeLength and MVC_LayoutText to parse icon escapes and produce layout spans (text + icon placeholders) for TrueType rendering.
  • Updates Text/TextView/bitmap font paths and examples/tests to measure, render, and edit icon escapes as single cursor units.
File summaries
File Description
Tests/ObjectivelyMVC/Text.c Adds tests for icon escape parsing, TrueType layout span generation, and bitmap cell advance behavior.
Tests/ObjectivelyMVC/ImageAtlas.c Adds tests for named image registration and atlas generation increments.
Tests/ObjectivelyMVC/Font+Bitmap.c Updates tests to match new bitmap sizing API signature (icons parameter).
Sources/ObjectivelyMVC/Theme.h Documents/exports Theme icon registration and lookup APIs; updates icon atlas documentation.
Sources/ObjectivelyMVC/Theme.c Implements Theme::addIcon and Theme::icon and wires them into the interface.
Sources/ObjectivelyMVC/TextView.c Uses Text::sizeText for caret measurement and treats :icon: as one cursor unit during editing/navigation.
Sources/ObjectivelyMVC/Text.h Adds icon escape parsing + layout APIs, span/run structures, Text icon-staleness tracking, and Text::sizeText.
Sources/ObjectivelyMVC/Text.c Implements icon parsing/layout, icon-aware invalidation, TrueType icon run rendering, and Text::sizeText.
Sources/ObjectivelyMVC/ImageAtlas.h Adds named image map and generation field; declares addImageWithName / imageWithName.
Sources/ObjectivelyMVC/ImageAtlas.c Implements named image registration/lookup, generation updates, and lifecycle management.
Sources/ObjectivelyMVC/Font+Bitmap.h Extends bitmap render/measure APIs to accept an icon atlas for :icon: resolution.
Sources/ObjectivelyMVC/Font+Bitmap.c Adds icon tokenization/rendering in bitmap font path and propagates atlas into render/measure.
Sources/ObjectivelyMVC/Font.h Adds per-Font cached icon placeholder and updates bitmap API signatures.
Sources/ObjectivelyMVC/Font.c Frees cached icon placeholder on Font deallocation.
Examples/HUD.c Registers HUD icons by name via Theme and demonstrates inline :heart: rendering in labels.
Review details
  • Files reviewed: 15/15 changed files
  • Comments generated: 5
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread Sources/ObjectivelyMVC/Font+Bitmap.c
Comment thread Sources/ObjectivelyMVC/Text.c
Comment thread Sources/ObjectivelyMVC/Text.c Outdated
Comment thread Sources/ObjectivelyMVC/Text.c Outdated
Comment thread Sources/ObjectivelyMVC/Text.c
jdolan and others added 6 commits September 5, 2026 21:34
renderToken offsets an icon by the bitmap's bearing to keep it in the glyph column, but walk
measured the icon as ending at its slot, so on a face with positive bearing an icon at the end
of a line drew past the reported width.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Keeps the icon where the escape occurs rather than wherever the widest fragment landed; the
split only happens when the wrap width is narrower than a line height, and the draw already
clamps the icon to its fragment.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@jdolan
jdolan merged commit 577de44 into main Sep 6, 2026
3 checks passed
@jdolan
jdolan deleted the feature/icon-escapes branch September 6, 2026 02:07
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