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(
|
stringCopy(
|
||||||
UI_FATAL_ERROR.messageText, message, UI_FATAL_ERROR_MESSAGE_MAX - 1
|
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);
|
uiLabelRebuffer(&UI_FATAL_ERROR.messageLabel);
|
||||||
|
|
||||||
UI_FATAL_ERROR.open = true;
|
UI_FATAL_ERROR.open = true;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#define UI_FATAL_ERROR_MESSAGE_MAX 256
|
#define UI_FATAL_ERROR_MESSAGE_MAX 256
|
||||||
#define UI_FATAL_ERROR_MESSAGE_SPRITES_MAX UI_FATAL_ERROR_MESSAGE_MAX
|
#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_ITEM_COUNT 1
|
||||||
#define UI_FATAL_ERROR_QUIT_LABEL "QUIT"
|
#define UI_FATAL_ERROR_QUIT_LABEL "QUIT"
|
||||||
#define UI_FATAL_ERROR_GENERIC_MESSAGE \
|
#define UI_FATAL_ERROR_GENERIC_MESSAGE \
|
||||||
@@ -23,6 +24,7 @@ typedef struct {
|
|||||||
|
|
||||||
uilabel_t messageLabel;
|
uilabel_t messageLabel;
|
||||||
char_t messageText[UI_FATAL_ERROR_MESSAGE_MAX];
|
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];
|
spritebatchsprite_t messageSprites[UI_FATAL_ERROR_MESSAGE_SPRITES_MAX];
|
||||||
|
|
||||||
uimenu_t menu;
|
uimenu_t menu;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "uilabel.h"
|
#include "uilabel.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
|
#include "util/string.h"
|
||||||
#include "display/text/text.h"
|
#include "display/text/text.h"
|
||||||
#include "display/spritebatch/spritebatch.h"
|
#include "display/spritebatch/spritebatch.h"
|
||||||
#include "display/shader/shaderunlit.h"
|
#include "display/shader/shaderunlit.h"
|
||||||
@@ -25,6 +26,30 @@ void uiLabelInit(
|
|||||||
label->text = text;
|
label->text = text;
|
||||||
label->sprites = sprites;
|
label->sprites = sprites;
|
||||||
label->spritesMax = spritesMax;
|
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) {
|
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) {
|
void uiLabelRebuffer(uilabel_t *label) {
|
||||||
if(!label->dirty) return;
|
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;
|
int32_t charIndex = 0;
|
||||||
float_t posX = label->x;
|
float_t posX = label->x;
|
||||||
float_t posY = label->y;
|
float_t posY = label->y;
|
||||||
label->spriteCount = textBuffer(
|
label->spriteCount = textBuffer(
|
||||||
label->x, label->y, label->text, &FONT_DEFAULT,
|
label->x, label->y, renderText, &FONT_DEFAULT,
|
||||||
label->sprites, label->spritesMax,
|
label->sprites, label->spritesMax,
|
||||||
&charIndex, &posX, &posY
|
&charIndex, &posX, &posY
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -20,6 +20,15 @@ typedef struct {
|
|||||||
float_t x;
|
float_t x;
|
||||||
float_t y;
|
float_t y;
|
||||||
bool_t dirty;
|
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;
|
} uilabel_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,6 +82,27 @@ void uiLabelSetY(uilabel_t *label, const float_t y);
|
|||||||
*/
|
*/
|
||||||
float_t uiLabelGetY(const uilabel_t *label);
|
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
|
* Rebuffers the label's cached sprites from its current text at its
|
||||||
* current x/y position. No-op unless the label is dirty. Called
|
* current x/y position. No-op unless the label is dirty. Called
|
||||||
|
|||||||
Reference in New Issue
Block a user