/** * Copyright (c) 2026 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #pragma once #include "error/error.h" #include "ui/frame/uiframe.h" #include "display/spritebatch/spritebatchsprite.h" #define UI_TEXTBOX_LINES_PER_PAGE_MAX 4 #define UI_TEXTBOX_SCROLL_CHARS_PER_TICK 1 #define UI_TEXTBOX_LINE_SPACING 0.0f // Fixed capacity for a page's cached glyph sprites (see uitextboxglyph_t). // Sized generously above UI_TEXTBOX_LINES_PER_PAGE_MAX worth of glyphs at // typical textbox widths; uiTextboxBuildPageGlyphs asserts if a page // somehow produces more than this. #define UI_TEXTBOX_PAGE_GLYPHS_MAX 512 typedef struct { int32_t start; int32_t count; } uitextboxline_t; // A single visible glyph's cached sprite (relative to the textbox's // content origin, i.e. (0,0)) plus the scroll value at/after which it // becomes visible -- lets uiTextboxDraw turn the typewriter scroll into // a simple prefix-count instead of recomputing glyph geometry every // frame. typedef struct { spritebatchsprite_t sprite; int32_t revealAt; } uitextboxglyph_t; typedef struct { char_t *text; uint32_t maxLength; uitextboxline_t *lines; uint32_t linesMax; int32_t lineCount; int32_t charsPerLine; int32_t linesPerPage; int32_t pageCount; // last dimensions used for layout; rebuild triggers when these change float_t layoutWidth; float_t layoutHeight; int32_t currentPage; int32_t scroll; // Cached glyph sprites for the current page (see uitextboxglyph_t), // rebuilt only when currentPage no longer matches // glyphsBuiltForPage -- not every frame/scroll tick. uitextboxglyph_t glyphs[UI_TEXTBOX_PAGE_GLYPHS_MAX]; int32_t glyphCount; int32_t glyphsBuiltForPage; uiframecache_t frameCache; } uitextbox_t; /** * Initializes a textbox, zeroing all state and binding it to caller-owned * text and line storage. * * @param box The textbox to initialize. * @param text Caller-owned buffer the textbox copies its text into. * @param maxLength Capacity of text, in characters. * @param lines Caller-owned buffer the textbox lays lines out into. * @param linesMax Capacity of lines, in entries. */ void uiTextboxInit( uitextbox_t *box, char_t *text, const uint32_t maxLength, uitextboxline_t *lines, const uint32_t linesMax ); /** * Copies text into the textbox and marks layout as dirty. * Resets currentPage and scroll to 0. * * @param box The textbox to update. * @param text Null-terminated source string. */ void uiTextboxSetText(uitextbox_t *box, const char_t *text); /** * Rebuilds word-wrap and page layout for the given draw dimensions. * Called automatically by uiTextboxDraw when width or height changes. * * @param box The textbox to rebuild. * @param width Available content width in pixels. * @param height Available content height in pixels. */ void uiTextboxBuildLayout( uitextbox_t *box, const float_t width, const float_t height ); /** * Advances the typewriter scroll by UI_TEXTBOX_SCROLL_CHARS_PER_TICK. * Skipped on dynamic ticks. * * @param box The textbox to update. * @returns Any error that occurs. */ errorret_t uiTextboxUpdate(uitextbox_t *box); /** * Draws the textbox frame and visible text. Rebuilds layout automatically * if width or height differs from the last draw call. * * @param box The textbox to draw. * @param x Screen x position. * @param y Screen y position. * @param width Draw width in pixels. * @param height Draw height in pixels. * @returns Any error that occurs. */ errorret_t uiTextboxDraw( uitextbox_t *box, const float_t x, const float_t y, const float_t width, const float_t height ); /** * Returns the total visible char count for the current page. * * @param box The textbox to query. * @returns Total chars on the current page. */ int32_t uiTextboxGetPageCharCount(const uitextbox_t *box); /** * Returns true when scroll has fully revealed the current page. * * @param box The textbox to query. * @returns True if the current page is fully visible. */ bool_t uiTextboxPageIsComplete(const uitextbox_t *box); /** * Returns true when there is at least one more page after the current one. * * @param box The textbox to query. * @returns True if a next page exists. */ bool_t uiTextboxHasNextPage(const uitextbox_t *box); /** * Advances to the next page and resets scroll to 0. * Has no effect if already on the last page. * * @param box The textbox to advance. */ void uiTextboxNextPage(uitextbox_t *box); /** * Rebuilds the cached glyph sprites (see uitextboxglyph_t) for the * current page from its line layout. Called automatically by * uiTextboxDraw whenever currentPage no longer matches the page the * cache was last built for. * * @param box The textbox to rebuild. */ void uiTextboxBuildPageGlyphs(uitextbox_t *box);