Fix uilabel dirty bug and add word-wrap support
uiLabelInit now marks a fresh label dirty itself, instead of every call site having to remember to do it. Adds uiLabelSetWrap (NULL buffer disables wrapping, otherwise requires a positive width) built on the existing textWrap(), and wires the fatal error overlay's message label up to wrap at 80% of screen width. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
@@ -48,7 +48,13 @@ void uiFatalErrorOpen(const char_t *message) {
|
||||
stringCopy(
|
||||
UI_FATAL_ERROR.messageText, message, UI_FATAL_ERROR_MESSAGE_MAX - 1
|
||||
);
|
||||
UI_FATAL_ERROR.messageLabel.dirty = true;
|
||||
|
||||
const float_t wrapWidth =
|
||||
(float_t)SCREEN.scanWidth * UI_FATAL_ERROR_MESSAGE_WRAP_RATIO;
|
||||
uiLabelSetWrap(
|
||||
&UI_FATAL_ERROR.messageLabel, UI_FATAL_ERROR.messageWrapText,
|
||||
UI_FATAL_ERROR_MESSAGE_MAX, wrapWidth
|
||||
);
|
||||
uiLabelRebuffer(&UI_FATAL_ERROR.messageLabel);
|
||||
|
||||
UI_FATAL_ERROR.open = true;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#define UI_FATAL_ERROR_MESSAGE_MAX 256
|
||||
#define UI_FATAL_ERROR_MESSAGE_SPRITES_MAX UI_FATAL_ERROR_MESSAGE_MAX
|
||||
#define UI_FATAL_ERROR_MESSAGE_WRAP_RATIO 0.8f
|
||||
#define UI_FATAL_ERROR_ITEM_COUNT 1
|
||||
#define UI_FATAL_ERROR_QUIT_LABEL "QUIT"
|
||||
#define UI_FATAL_ERROR_GENERIC_MESSAGE \
|
||||
@@ -23,6 +24,7 @@ typedef struct {
|
||||
|
||||
uilabel_t messageLabel;
|
||||
char_t messageText[UI_FATAL_ERROR_MESSAGE_MAX];
|
||||
char_t messageWrapText[UI_FATAL_ERROR_MESSAGE_MAX];
|
||||
spritebatchsprite_t messageSprites[UI_FATAL_ERROR_MESSAGE_SPRITES_MAX];
|
||||
|
||||
uimenu_t menu;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "uilabel.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/string.h"
|
||||
#include "display/text/text.h"
|
||||
#include "display/spritebatch/spritebatch.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
@@ -25,6 +26,30 @@ void uiLabelInit(
|
||||
label->text = text;
|
||||
label->sprites = sprites;
|
||||
label->spritesMax = spritesMax;
|
||||
label->dirty = true;
|
||||
}
|
||||
|
||||
void uiLabelSetWrap(
|
||||
uilabel_t *label,
|
||||
char_t *wrapBuffer,
|
||||
const uint32_t wrapBufferMax,
|
||||
const float_t wrapWidth
|
||||
) {
|
||||
assertNotNull(label, "Label cannot be NULL");
|
||||
|
||||
// A NULL wrapBuffer always disables wrapping outright, regardless of
|
||||
// wrapWidth - so callers can unconditionally pass NULL to turn wrapping
|
||||
// off without also having to zero out whatever width they were using.
|
||||
// Enabling it (a non-NULL buffer) always requires a positive width.
|
||||
if(wrapBuffer != NULL) {
|
||||
assertTrue(wrapWidth > 0.0f, "Wrap width must be greater than 0");
|
||||
assertTrue(wrapBufferMax >= 1, "Wrap buffer max must be at least 1");
|
||||
}
|
||||
|
||||
label->wrapBuffer = wrapBuffer;
|
||||
label->wrapBufferMax = wrapBufferMax;
|
||||
label->wrapWidth = wrapBuffer == NULL ? 0.0f : wrapWidth;
|
||||
label->dirty = true;
|
||||
}
|
||||
|
||||
void uiLabelSetX(uilabel_t *label, const float_t x) {
|
||||
@@ -50,13 +75,23 @@ float_t uiLabelGetY(const uilabel_t *label) {
|
||||
void uiLabelRebuffer(uilabel_t *label) {
|
||||
if(!label->dirty) return;
|
||||
|
||||
textMeasure(label->text, &FONT_DEFAULT, &label->width, &label->height);
|
||||
const char_t *renderText = label->text;
|
||||
if(label->wrapWidth > 0.0f) {
|
||||
assertNotNull(
|
||||
label->wrapBuffer, "Wrap buffer cannot be NULL when wrapping is enabled"
|
||||
);
|
||||
stringCopy(label->wrapBuffer, label->text, label->wrapBufferMax);
|
||||
textWrap(label->wrapBuffer, &FONT_DEFAULT, label->wrapWidth);
|
||||
renderText = label->wrapBuffer;
|
||||
}
|
||||
|
||||
textMeasure(renderText, &FONT_DEFAULT, &label->width, &label->height);
|
||||
|
||||
int32_t charIndex = 0;
|
||||
float_t posX = label->x;
|
||||
float_t posY = label->y;
|
||||
label->spriteCount = textBuffer(
|
||||
label->x, label->y, label->text, &FONT_DEFAULT,
|
||||
label->x, label->y, renderText, &FONT_DEFAULT,
|
||||
label->sprites, label->spritesMax,
|
||||
&charIndex, &posX, &posY
|
||||
);
|
||||
|
||||
@@ -20,6 +20,15 @@ typedef struct {
|
||||
float_t x;
|
||||
float_t y;
|
||||
bool_t dirty;
|
||||
|
||||
// Wrapping - disabled while wrapWidth is 0 (the default). Enabling it
|
||||
// requires wrapBuffer, a caller-owned scratch buffer at least as large as
|
||||
// text: label->text is never written to directly (see uiLabelInit), so
|
||||
// uiLabelRebuffer instead copies it into wrapBuffer and word-wraps that
|
||||
// copy in place (via textWrap) before measuring/buffering it.
|
||||
char_t *wrapBuffer;
|
||||
uint32_t wrapBufferMax;
|
||||
float_t wrapWidth;
|
||||
} uilabel_t;
|
||||
|
||||
/**
|
||||
@@ -73,6 +82,27 @@ void uiLabelSetY(uilabel_t *label, const float_t y);
|
||||
*/
|
||||
float_t uiLabelGetY(const uilabel_t *label);
|
||||
|
||||
/**
|
||||
* Enables or disables word-wrap on the label and marks it dirty. Pass
|
||||
* wrapBuffer NULL to disable wrapping outright (wrapWidth is then
|
||||
* ignored); enabling it requires both a non-NULL wrapBuffer and a
|
||||
* wrapWidth greater than 0.
|
||||
*
|
||||
* @param label The label to configure.
|
||||
* @param wrapBuffer Caller-owned scratch buffer, at least as large as the
|
||||
* label's text, or NULL to disable wrapping.
|
||||
* @param wrapBufferMax Capacity of wrapBuffer, including the null
|
||||
* terminator.
|
||||
* @param wrapWidth Maximum line width in pixels; must be greater than 0
|
||||
* when wrapBuffer is non-NULL.
|
||||
*/
|
||||
void uiLabelSetWrap(
|
||||
uilabel_t *label,
|
||||
char_t *wrapBuffer,
|
||||
const uint32_t wrapBufferMax,
|
||||
const float_t wrapWidth
|
||||
);
|
||||
|
||||
/**
|
||||
* Rebuffers the label's cached sprites from its current text at its
|
||||
* current x/y position. No-op unless the label is dirty. Called
|
||||
|
||||
Reference in New Issue
Block a user