InlineBox and InlineImage - #25710
Merged
Merged
Conversation
…rator fields now expect a `TextItem` enum that can either be text or an inline box. Both are added to the layout using Parley's ranged builder.
These are updated in `update_text_layout_info` from the parley generated layout.
…tem to `bevy_ui`. Options for sizing and flipping etc can be added later, this is just a very basic implemenation that allows us to test that inline image widgets work.
…extWriter` traversal traits. Added traversal test for text2d. Updated UI accessibility.
…nline_images` to the extract schedule.
…ssetChanged` in `extract_uinode_changes`.
17 tasks
Improved doc comments. Fixed failing `inline_boxes_are_accessible_through_text_access` test, was just misusing the new API.
github-merge-queue
Bot
removed this pull request from the merge queue due to a conflict with the base branch
Sep 8, 2026
viridia
added a commit
to viridia/bevy_action_map
that referenced
this pull request
Sep 9, 2026
The phase headings had stopped carrying information. Phase VII had prose and no chunks, its prose described a tier of docs/issues.md that is now empty and retired, and everything else was either "Phase IX" or "Unscheduled by phase". Six semantic categories replace them, ordered roughly by priority, with the gated table still last. Every chunk body is byte-identical to its previous version; the only content changes are the headings, their intro prose, and the Glyph ids row. That row loses two of its three gates. The generic tier's blank, unlabeled buttons become a content task rather than a design question, since the stamp can be authored onto the icons by hand. And the presentation shape stops being forced: an icon prompt had to be block-level because PromptSpan is TextSpan-based and Bevy had no inline image in a text run, which made falling back from an icon to a word a change of layout kind rather than of content. bevyengine/bevy#25710 removes that once it merges. "Never built" said identifiers are defined. They are not — R18.4 specifies one and no code declares it, the only occurrence of "glyph" in src/ being a doc comment. LLM Usage Disclosure: implementation, tests and documentation written by Claude Opus 5; design decisions, review and acceptance by the author.
viridia
added a commit
to viridia/bevy_action_map
that referenced
this pull request
Sep 9, 2026
Its gate is met. The row deferred on an identifier scheme that Kenney's real
file names were the way to falsify, and the import did that: all 164 entries in
assets/input_prompts/ resolve as
format!("input_prompts/{tier}/{}.png", control.name()), with no lookup table.
Scoped so the three things that could have made it large are stated omissions.
Icons go only where PromptSpan already renders, not into the settings table's
capture cells, which have no width to spare and want the authoritative name
rather than a glyph. The generic tier's stamped art is a content task and stays
out. And bevyengine/bevy#25710 gates the inline form only — the identifier, the
resolution and the manifest do not wait on it, so the chunk can land with a
block-level prompt if the PR has not merged.
Resolution reads the manifest rather than probing for a file, so a gap steps to
the next tier and a missing path is never a load error.
LLM Usage Disclosure: implementation, tests and documentation written by
Claude Opus 5; design decisions, review and acceptance by the author.
viridia
added a commit
to viridia/bevy_action_map
that referenced
this pull request
Sep 13, 2026
R18.4's identifier lands as `Glyph`/`GlyphTier` in present.rs — a brand's own tier tried before its generic fallback, `#[non_exhaustive]` for R18.9's foreign shapes rather than guessing at one before a backend exists to shape it against. `IconPromptSpan` resolves and renders it, falling back to `PromptSpan`'s own text, bracketed, wherever nothing has art. `InlineImage` (bevyengine/bevy#25710) turned out to size itself from the loaded image's own pixels with no resize hook, so the 64px source pack towered over a button's own text. `assets/input_prompts_inline/` is the fix: a pre-scaled second copy of every entry, generated by import_input_prompts.py rather than sized at runtime. Getting the resize itself right took two more rounds: a naive `-resize` bled the source's white-under-transparency into the scaled edge as a halo, and a palette small enough to compress well flattened the antialiasing into visible octagonal bands. Compositing over black and back, in full RGBA, is what actually reads clean at 25px. Verified on Disasteroids' Cancel/Confirm buttons, which also dropped their hardcoded parentheses — a caller no longer wraps either outcome in its own punctuation, since an icon reads as a control on its own and bracketing is `IconPromptSpan`'s fallback to add, not the caller's. Split Friction's `Join` moves off `ControlClass::AnyButton` onto `GamepadButton::South` and `KeyCode::Enter` directly, both because a class binding has no Steam expression (docs/steam.md) and because it gives the join screen's caption something concrete to name instead of a hardcoded "press any button." That caption now reads live off `Join`'s own bindings. The one thing this traded away: `Fired`'s value is device-agnostic by design, so knowing which device actually pressed `Join` now means reading `Gamepad`/`ButtonInput` directly in `pair_on_join`, which does not survive a Steam authority — flagged in Roadmap's deferred table rather than left to be rediscovered. Not doing: the block `IconPrompt` component. `IconPromptSpan` covers the inline case this chunk verified; a block one waits for a screen that needs it. LLM Usage Disclosure: implementation, tests and documentation written by Claude Sonnet 5; design decisions, review and acceptance by the author.
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.
Objective
Allow space to be reserved for non-text items in text layouts.
Solution
New
InlineBoxcomponent withsizeandkindfields,kindcontrols whether the box should take up space in the layout or be zero sized (so kind of equivalent to absolute positioning).InlineBoxisn't intended to be a user facing API. Instead, primary widget components should requireInlineBoxand update it as needed (for instance if its image asset changes size).The traversal traits in
text_accessnow supportInlineBox.InlineBoxentities are always leaf nodes, if they haveTextSpanchildren, those children are ignored. This is so thatInlineBoxcan have children to support more complex widgets composed from multiple entities.TextPipeline::update_bufferadds the inline boxes yielded by the text reader to the Parley layout context'sRangedBuilder. Andupdate_text_layout_infoadds a list of the laid out boxes to each text entity'sTextLayoutInfo.Also includes a very basic
InlineImagecomponent and accompanying systems that allows images to be interspersed with UI text. Support for resizing, flipping and atlas options can be added in a follow up PR.InlineBoxes pickable, left out to keep this PR a manageable size.InlineImageactually works. It should be extended once the completeInlineImageapi is added.Testing
Includes a basic example:
A UI testbed scene,
Scene::InlineImage:And a traversal test,
inline_boxes_are_accessible_through_text_accessinbevy_sprite::text2d.Showcase