feat(inventory-list-display): icon_atlas override + fit-to-bounds rendering

Two consumer-side fixes for ADR-0054 §3 (Item-Visual-Identity).

1) default_icon_resolver tries icon_atlas + icon_uv.* first; falls
   back to sprite_atlas + sprite_uv.* when the override is absent.
   Items inherit their world-render sprite as the UI icon for free
   (TC_Basics-style object sprites work directly as icons); dedicated
   low-res UI-icons can later land via icon_atlas without API churn.

2) Render path scales the icon uniformly to fit a (row_height - 4)
   box, centered. The world-sprite-as-icon path (300×300 native) no
   longer overflows the row; pre-baked small icons stay sharp because
   the scale is applied around the visual center of the UV rect, not
   the texture origin.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
calic
2026-06-21 11:43:34 +02:00
parent c0066830ea
commit ae394ec89f
2 changed files with 33 additions and 8 deletions

View File

@@ -54,9 +54,26 @@ local M = {}
-- -----------------------------------------------------------------------
-- default_icon_resolver(item) -> icon_table or nil
-- Reads sprite_atlas + sprite_uv.* from item properties.
-- Returns nil if sprite_atlas is not set (no icon for this item).
-- v0.2.0: per ADR-0054 §3 — additive UI-icon override on top of the
-- world-sprite. If the item declares `icon_atlas` (string) + `icon_uv.*`
-- (numbers), use those (e.g. for dedicated low-res UI icons baked
-- separately). Otherwise fall back to `sprite_atlas` + `sprite_uv.*`
-- (the world-render sprite, which today doubles as the UI icon for
-- TC_Basics-style item-shaped sprites).
-- Returns nil if neither chain yields a sprite (no icon for this item).
local function default_icon_resolver(item)
local icon_atlas = item:get_property("icon_atlas")
if icon_atlas and icon_atlas ~= "" then
return {
atlas = icon_atlas,
uv = {
x = item:get_property("icon_uv.x"),
y = item:get_property("icon_uv.y"),
w = item:get_property("icon_uv.w"),
h = item:get_property("icon_uv.h"),
},
}
end
local atlas = item:get_property("sprite_atlas")
if not atlas or atlas == "" then return nil end
return {
@@ -108,13 +125,21 @@ local function render_widget(widget_def, ctx)
widget_def.widget_id, icon.atlas)
if ok and tex then
local uv = icon.uv
-- draw_sprite_transform(tex, x, y, rot, scl_x, scl_y,
-- origin_x, origin_y, tint, u, v, uw, vh)
-- rot=0, scl=1, origin at top-left (0,0), tint=white
-- v0.2.0: fit-to-bounds. Compute uniform scale so the
-- larger UV dimension matches the icon box (row_height
-- minus a small margin), then center-anchor inside the
-- box. Lets world-sprites (e.g. TC_Basics 300×300 native)
-- and pre-baked icons share the same row layout without
-- per-item magic numbers.
local box = ctx.theme.row_height - 4
local max_uv = (uv.w > uv.h) and uv.w or uv.h
local s = box / max_uv
local draw_x = ctx.bounds.x + (box - uv.w * s) / 2
local draw_y = row_y + 2 + (box - uv.h * s) / 2
engine.render.draw_sprite_transform(
tex,
ctx.bounds.x, row_y,
0, 1, 1,
draw_x, draw_y,
0, s, s,
0, 0,
0xFFFFFFFF,
uv.x, uv.y, uv.w, uv.h)