The UI subsystem now draws. akgl_ui_frame_begin/frame_end bracket one frame's CLAY() declarations: begin clears the error stash and starts the clay layout, end computes it and walks the render commands through a backend -- RECTANGLE as (rounded) fills, BORDER as radius-shortened edge fills plus corner arcs, TEXT through akgl_text_rendertextat one wrapped line per command, IMAGE as an akgl_Sprite's first frame stretched to the bounding box, and the SCISSOR pair through akgl_draw_set_clip, cleared again on any exit so a failed frame cannot leave the world clipped. akgl_ui_font_register maps registry font names onto clay's uint16_t fontIds. The table keeps the *name* and resolves it per use -- fonts are not reference counted, and a cached TTF_Font* would dangle where a name reports "gone" honestly. Clay_TextElementConfig.fontSize is deliberately ignored: libakgl bakes the size into the handle at load, so one face at two sizes is two ids. The measure bridge passes clay's non-NUL-terminated slices straight to SDL_ttf's explicit-length TTF_GetStringSize -- no copy, no scratch. Its signature has no error channel, so failures report zero-by-zero and stash a message that frame_end raises, the same route clay's own void error handler uses; layout errors take precedence over drawing and one bad frame leaves the next one clean. Tests drive real CLAY() layouts through the software renderer and read pixels back: fill placement, border edges with an empty middle, a child clipped by its container, text landing in its colour, slice-vs-whole measurement agreement, fontId table dedupe/refusal/exhaustion, and the bracket's begin/begin, end-without-begin and failure-recovery contracts. Co-Authored-By: Claude Code (Claude Fable 5, claude-fable-5) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KzBDV2fqgnUAcqCKqKvc71
20 KiB
20 KiB