Add :icon: escapes, resolved through Theme::icons, on every Font - #58
Merged
Conversation
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>
Contributor
There was a problem hiding this comment.
🟡 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 +
generationtracking inImageAtlas, and Theme APIsaddIcon/iconfor registering and resolving icons. - Adds
MVC_IconEscapeLengthandMVC_LayoutTextto 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.
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
:name:, on both the bitmap and TrueType paths, with no opt-in, matching color escapes. Unregistered names stay literal, so12:30:45is untouched.ImageAtlasgains named images (addImageWithName/imageWithName) and agenerationcounter.Theme::addIconregisters an Image under a name and compiles the icon atlas;Theme::iconlooks one up.MVC_IconEscapeLengthdefines the escape syntax once, shared by Text, Font+Bitmap and TextView.MVC_LayoutTextresolves 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.Text::sizeTextmeasures 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.Test plan
make -jbuilds cleanmake checkpasses (7 suites; new cases in ImageAtlas and Text)Examples/HUDandExamples/Hellorun without error: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