3 Commits

Author SHA1 Message Date
YourWishes 774c8ad0f8 Add uimodal dialog; uibutton reuses uilabel
uibutton now embeds a uilabel instead of drawing its text directly,
caching glyph sprites since a button's label never changes after
init. uimenu.c's item pointer in uiMenuDraw drops its unnecessary
const so uiButtonDraw can update the button's cache.

Also fixes remaining #include "ui/frame/uiframe.h" references left
over from uiframe's move to ui/widget/.

uimodal is a new generalized dialog (title + message + up to
UI_MODAL_OPTIONS_MAX option buttons) built the same way uiconfirm is,
but driven by a caller-supplied option list instead of a fixed
confirm/cancel pair.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 12:44:01 -05:00
YourWishes 52d1e7414d Convert debug UI overlays to uilabel
uiconsole, uifps, and uiplayerpos now render through uilabel instead
of calling textDraw per character every frame. Each owns its label(s)
plus backing text/sprite buffers and marks them dirty only when their
content or position actually changes. Also drops console.h's per-line
buffer from 512 to 128 chars, since the sprite cache backing each
console line label scales with it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 12:43:43 -05:00
YourWishes af4cb53e5f Simplify uilabel to caller-owned buffers
uilabel no longer owns fixed-size text/sprite arrays or copies text
internally - callers pass in their own buffers and write text
directly, then mark the label dirty. This drops SetText/GetText and
the textMax bound in favor of a single Rebuffer entry point, and lets
widgets like uibutton alias an existing (possibly immutable) label
string instead of duplicating it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 12:43:02 -05:00
28 changed files with 512 additions and 112 deletions
+1 -1
View File
@@ -10,7 +10,7 @@
#include "dusk.h"
#include "thread/thread.h"
#define CONSOLE_LINE_MAX 512
#define CONSOLE_LINE_MAX 128
#define CONSOLE_HISTORY_MAX 16
#define CONSOLE_EXEC_BUFFER_MAX 32
+20 -9
View File
@@ -6,23 +6,34 @@
*/
#include "uiconsole.h"
#include "console/console.h"
#include "display/screen/screen.h"
#include "display/text/text.h"
#include "display/spritebatch/spritebatch.h"
uiconsole_t UICONSOLE;
errorret_t uiConsoleInit(void) {
float_t lineH = (float_t)FONT_DEFAULT.tileset->tileHeight;
for(uint32_t i = 0; i < CONSOLE_HISTORY_MAX; i++) {
uiLabelInit(
&UICONSOLE.labels[i],
CONSOLE.line[i],
UICONSOLE.sprites[i], CONSOLE_LINE_MAX
);
uiLabelSetX(&UICONSOLE.labels[i], (float_t)SCREEN.scanX);
uiLabelSetY(&UICONSOLE.labels[i], (float_t)SCREEN.scanY + lineH * (float_t)i);
}
errorOk();
}
errorret_t uiConsoleDraw(void) {
if(!CONSOLE.visible) errorOk();
float_t lineH = (float_t)FONT_DEFAULT.tileset->tileHeight;
for(uint32_t i = 0; i < CONSOLE_HISTORY_MAX; i++) {
errorChain(textDraw(
(float_t)SCREEN.scanX,
(float_t)SCREEN.scanY + lineH * (float_t)i,
CONSOLE.line[i],
COLOR_RED,
&FONT_DEFAULT
));
UICONSOLE.labels[i].dirty = true;
errorChain(uiLabelRender(&UICONSOLE.labels[i], COLOR_RED));
}
return spriteBatchFlush();
}
+16
View File
@@ -7,6 +7,22 @@
#pragma once
#include "error/error.h"
#include "console/console.h"
#include "ui/widget/uilabel.h"
typedef struct {
uilabel_t labels[CONSOLE_HISTORY_MAX];
spritebatchsprite_t sprites[CONSOLE_HISTORY_MAX][CONSOLE_LINE_MAX];
} uiconsole_t;
extern uiconsole_t UICONSOLE;
/**
* Initializes the console's history labels.
*
* @return Any error that occurs.
*/
errorret_t uiConsoleInit(void);
/**
* Renders the console history into the scan-safe area.
+35 -21
View File
@@ -7,16 +7,42 @@
#include "uifps.h"
#include "time/time.h"
#include "util/string.h"
#include "display/spritebatch/spritebatch.h"
#include "display/text/text.h"
#include "display/color.h"
#include "display/screen/screen.h"
#include "engine/engine.h"
uifps_t UIFPS;
errorret_t uiFPSDraw() {
char_t fpsText[32];
errorret_t uiFPSInit() {
uiLabelInit(
&UIFPS.fpsLabel,
UIFPS.fpsText,
UIFPS.fpsSprites, UI_FPS_SPRITES_MAX
);
uiLabelSetX(&UIFPS.fpsLabel, (float_t)SCREEN.scanX);
uiLabelSetY(&UIFPS.fpsLabel, (float_t)SCREEN.scanY);
uiLabelInit(
&UIFPS.versionLabel,
UIFPS.versionText,
UIFPS.versionSprites, UI_FPS_VERSION_SPRITES_MAX
);
stringCopy(UIFPS.versionText, ENGINE.version, UI_FPS_VERSION_TEXT_MAX - 1);
UIFPS.versionLabel.dirty = true;
uiLabelRebuffer(&UIFPS.versionLabel);
uiLabelSetX(&UIFPS.versionLabel, (float_t)(
SCREEN.scanX + SCREEN.scanWidth - UIFPS.versionLabel.width
));
uiLabelSetY(&UIFPS.versionLabel, (float_t)(
SCREEN.scanY + SCREEN.scanHeight - UIFPS.versionLabel.height
));
errorOk();
}
errorret_t uiFPSDraw() {
// Get now.
dusktimeepoch_t now = timeGetEpoch();
double_t delta = now.time - UIFPS.lastTick.time;
@@ -33,13 +59,14 @@ errorret_t uiFPSDraw() {
UIFPS.fpsAverage = alpha * fps + (1.0f - alpha) * UIFPS.fpsAverage;
}
snprintf(
fpsText,
sizeof(fpsText),
stringFormat(
UIFPS.fpsText,
UI_FPS_TEXT_MAX - 1,
"%.1f/%.1fms",
UIFPS.fpsAverage,
delta * 1000.0f
);
UIFPS.fpsLabel.dirty = true;
color_t textColor;
if(fps >= 55.0f) {
@@ -50,23 +77,10 @@ errorret_t uiFPSDraw() {
textColor = COLOR_RED;
}
errorChain(textDraw(
(float_t)SCREEN.scanX,
(float_t)SCREEN.scanY,
fpsText, textColor,
&FONT_DEFAULT
));
errorChain(uiLabelRender(&UIFPS.fpsLabel, textColor));
errorChain(spriteBatchFlush());
int32_t versionWidth, versionHeight;
textMeasure(ENGINE.version, &FONT_DEFAULT, &versionWidth, &versionHeight);
errorChain(textDraw(
(float_t)(SCREEN.scanX + SCREEN.scanWidth - versionWidth),
(float_t)(SCREEN.scanY + SCREEN.scanHeight - versionHeight),
ENGINE.version,
color(255, 255, 255, 128),
&FONT_DEFAULT
));
errorChain(uiLabelRender(&UIFPS.versionLabel, color(255, 255, 255, 128)));
return spriteBatchFlush();
}
+21
View File
@@ -8,14 +8,35 @@
#pragma once
#include "error/error.h"
#include "time/timeepoch.h"
#include "ui/widget/uilabel.h"
#define UI_FPS_TEXT_MAX 32
#define UI_FPS_SPRITES_MAX UI_FPS_TEXT_MAX
#define UI_FPS_VERSION_TEXT_MAX 32
#define UI_FPS_VERSION_SPRITES_MAX UI_FPS_VERSION_TEXT_MAX
typedef struct {
dusktimeepoch_t lastTick;
float_t fpsAverage;
uilabel_t fpsLabel;
char_t fpsText[UI_FPS_TEXT_MAX];
spritebatchsprite_t fpsSprites[UI_FPS_SPRITES_MAX];
uilabel_t versionLabel;
char_t versionText[UI_FPS_VERSION_TEXT_MAX];
spritebatchsprite_t versionSprites[UI_FPS_VERSION_SPRITES_MAX];
} uifps_t;
extern uifps_t UIFPS;
/**
* Initializes the FPS counter's labels.
*
* @return Any error that occurs.
*/
errorret_t uiFPSInit();
/**
* Draws the FPS counter on the screen, and also does the update (for now).
*
+22 -9
View File
@@ -6,6 +6,7 @@
*/
#include "uiplayerpos.h"
#include "util/string.h"
#include "display/screen/screen.h"
#include "display/text/text.h"
#include "display/spritebatch/spritebatch.h"
@@ -13,6 +14,22 @@
#include "rpg/entity/entitytype.h"
#include "rpg/overworld/worldpos.h"
uiplayerpos_t UIPLAYERPOS;
errorret_t uiPlayerPosInit() {
uiLabelInit(
&UIPLAYERPOS.label,
UIPLAYERPOS.text,
UIPLAYERPOS.sprites, UI_PLAYERPOS_SPRITES_MAX
);
uiLabelSetX(&UIPLAYERPOS.label, (float_t)SCREEN.scanX);
uiLabelSetY(&UIPLAYERPOS.label, (float_t)SCREEN.scanY +
(float_t)FONT_DEFAULT.tileset->tileHeight
);
errorOk();
}
errorret_t uiPlayerPosDraw() {
entity_t *player = NULL;
for(uint8_t i = 0; i < ENTITY_COUNT; i++) {
@@ -26,10 +43,9 @@ errorret_t uiPlayerPosDraw() {
chunkpos_t chunkPos;
worldPosToChunkPos(&player->position, &chunkPos);
char_t text[64];
snprintf(
text,
sizeof(text),
stringFormat(
UIPLAYERPOS.text,
UI_PLAYERPOS_TEXT_MAX - 1,
"%d,%d,%d[%d,%d,%d]",
(int_t)player->position.x,
(int_t)player->position.y,
@@ -38,11 +54,8 @@ errorret_t uiPlayerPosDraw() {
(int_t)chunkPos.y,
(int_t)chunkPos.z
);
UIPLAYERPOS.label.dirty = true;
float_t y = (float_t)SCREEN.scanY +
(float_t)FONT_DEFAULT.tileset->tileHeight;
errorChain(textDraw(
(float_t)SCREEN.scanX, y, text, COLOR_GREEN, &FONT_DEFAULT
));
errorChain(uiLabelRender(&UIPLAYERPOS.label, COLOR_GREEN));
return spriteBatchFlush();
}
+19
View File
@@ -7,6 +7,25 @@
#pragma once
#include "error/error.h"
#include "ui/widget/uilabel.h"
#define UI_PLAYERPOS_TEXT_MAX 64
#define UI_PLAYERPOS_SPRITES_MAX UI_PLAYERPOS_TEXT_MAX
typedef struct {
uilabel_t label;
char_t text[UI_PLAYERPOS_TEXT_MAX];
spritebatchsprite_t sprites[UI_PLAYERPOS_SPRITES_MAX];
} uiplayerpos_t;
extern uiplayerpos_t UIPLAYERPOS;
/**
* Initializes the player position label.
*
* @return Any error that occurs.
*/
errorret_t uiPlayerPosInit();
/**
* Draws the player world and chunk position on screen.
+1 -1
View File
@@ -5,8 +5,8 @@
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
uiframe.c
uiconfirm.c
uimodal.c
)
add_subdirectory(game)
+1 -1
View File
@@ -6,7 +6,7 @@
*/
#include "uibackpack.h"
#include "ui/frame/uiframe.h"
#include "ui/widget/uiframe.h"
#include "rpg/item/backpack.h"
#include "util/memory.h"
#include "util/string.h"
+1 -1
View File
@@ -6,7 +6,7 @@
*/
#include "uibattlemenu.h"
#include "ui/frame/uiframe.h"
#include "ui/widget/uiframe.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
+1 -1
View File
@@ -6,7 +6,7 @@
*/
#include "uigamemenu.h"
#include "ui/frame/uiframe.h"
#include "ui/widget/uiframe.h"
#include "ui/frame/uiconfirm.h"
#include "ui/frame/backpack/uibackpack.h"
#include "ui/rpg/textbox/uitextboxmain.h"
+1 -1
View File
@@ -6,7 +6,7 @@
*/
#include "uimainmenu.h"
#include "ui/frame/uiframe.h"
#include "ui/widget/uiframe.h"
#include "scene/scene.h"
#include "engine/engine.h"
#include "rpg/battle/testbattle/testbattle.h"
+1 -1
View File
@@ -6,7 +6,7 @@
*/
#include "uiconfirm.h"
#include "uiframe.h"
#include "ui/widget/uiframe.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
+171
View File
@@ -0,0 +1,171 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "uimodal.h"
#include "ui/widget/uiframe.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
#include "util/math.h"
#include "display/screen/screen.h"
#include "display/text/text.h"
#include "display/color.h"
#include "display/spritebatch/spritebatch.h"
#include "display/texture/texture.h"
#include "display/shader/shaderunlit.h"
#define UI_MODAL_BACKDROP_COLOR color4b(0, 0, 0, 160)
uimodal_t UI_MODAL;
void uiModalSelected(
const uimenu_t *menu,
const uint8_t index,
const uimenuitem_t *item
) {
UI_MODAL.result = index;
uiModalClose();
}
void uiModalClosed(const uimenu_t *menu) {
if(UI_MODAL.callback != NULL) {
UI_MODAL.callback(UI_MODAL.result, UI_MODAL.user);
}
}
errorret_t uiModalInit(void) {
memoryZero(&UI_MODAL, sizeof(uimodal_t));
uiLabelInit(
&UI_MODAL.titleLabel, UI_MODAL.titleText,
UI_MODAL.titleSprites, UI_MODAL_TITLE_SPRITES_MAX
);
uiLabelInit(
&UI_MODAL.messageLabel, UI_MODAL.messageText,
UI_MODAL.messageSprites, UI_MODAL_MESSAGE_SPRITES_MAX
);
errorOk();
}
errorret_t uiModalDraw(void) {
if(!uiMenuIsActive(&UI_MODAL.menu)) errorOk();
spritebatchsprite_t backdropSprite = {
.min = { 0.0f, 0.0f, 0.0f },
.max = { (float_t)SCREEN.width, (float_t)SCREEN.height, 0.0f },
.uvMin = { 0.0f, 0.0f },
.uvMax = { 1.0f, 1.0f }
};
shadermaterial_t backdropMaterial = {
.unlit = {
.color = UI_MODAL_BACKDROP_COLOR,
.texture = &TEXTURE_WHITE
}
};
errorChain(
spriteBatchBuffer(&backdropSprite, 1, &SHADER_UNLIT, backdropMaterial)
);
errorChain(spriteBatchFlush());
float_t rowHeight = (float_t)FONT_DEFAULT.tileset->tileHeight;
float_t contentWidth = mathMax(
mathMax(
(float_t)UI_MODAL.titleLabel.width, (float_t)UI_MODAL.messageLabel.width
),
UI_MODAL_MIN_WIDTH - (UI_FRAME_START_X * 2)
);
float_t width = contentWidth + (UI_FRAME_START_X * 2);
float_t height = (UI_FRAME_START_Y * 2)
+ (float_t)UI_MODAL.titleLabel.height + UI_FRAME_PADDING_Y
+ (float_t)UI_MODAL.messageLabel.height + UI_FRAME_PADDING_Y
+ rowHeight;
float_t x = (float_t)SCREEN.scanX +
((float_t)SCREEN.scanWidth - width) * 0.5f;
float_t y = (float_t)SCREEN.scanY +
((float_t)SCREEN.scanHeight - height) * 0.5f;
errorChain(uiFrameDraw(x, y, width, height));
float_t contentX = x + UI_FRAME_START_X;
float_t contentY = y + UI_FRAME_START_Y;
uiLabelSetX(&UI_MODAL.titleLabel, contentX);
uiLabelSetY(&UI_MODAL.titleLabel, contentY);
errorChain(uiLabelRender(&UI_MODAL.titleLabel, COLOR_WHITE));
float_t messageY = contentY + (float_t)UI_MODAL.titleLabel.height +
UI_FRAME_PADDING_Y;
uiLabelSetX(&UI_MODAL.messageLabel, contentX);
uiLabelSetY(&UI_MODAL.messageLabel, messageY);
errorChain(uiLabelRender(&UI_MODAL.messageLabel, COLOR_WHITE));
float_t buttonsY = messageY + (float_t)UI_MODAL.messageLabel.height +
UI_FRAME_PADDING_Y;
errorChain(
uiMenuDraw(&UI_MODAL.menu, contentX, buttonsY, contentWidth, rowHeight)
);
errorChain(spriteBatchFlush());
errorOk();
}
bool_t uiModalIsOpen(void) {
return uiMenuIsActive(&UI_MODAL.menu);
}
uint8_t uiModalGetResult(void) {
return UI_MODAL.result;
}
void uiModalOpen(
const char_t *title,
const char_t *message,
const char_t **options,
const uint8_t optionCount,
uimodalcallback_t callback,
void *user
) {
assertNotNull(title, "Title cannot be NULL");
assertNotNull(message, "Message cannot be NULL");
assertNotNull(options, "Options cannot be NULL");
assertTrue(optionCount > 0, "Must have at least one option");
assertTrue(
optionCount <= UI_MODAL_OPTIONS_MAX, "Too many options for modal"
);
stringCopy(UI_MODAL.titleText, title, UI_MODAL_TITLE_TEXT_MAX - 1);
UI_MODAL.titleLabel.dirty = true;
uiLabelRebuffer(&UI_MODAL.titleLabel);
stringCopy(UI_MODAL.messageText, message, UI_MODAL_MESSAGE_TEXT_MAX - 1);
UI_MODAL.messageLabel.dirty = true;
uiLabelRebuffer(&UI_MODAL.messageLabel);
UI_MODAL.callback = callback;
UI_MODAL.user = user;
UI_MODAL.result = UI_MODAL_RESULT_NONE;
MENU_BEGIN(
&UI_MODAL.menu, UI_MODAL.options, uiModalSelected, uiModalClosed, NULL
);
for(uint8_t i = 0; i < optionCount; i++) {
MENU_BUTTON(options[i]);
}
MENU_END(UI_MODAL.options, menuIndex);
uiMenuOpen(&UI_MODAL.menu);
}
void uiModalClose(void) {
uiMenuClose(&UI_MODAL.menu);
}
errorret_t uiModalDispose(void) {
errorOk();
}
+116
View File
@@ -0,0 +1,116 @@
/**
* 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/widget/uilabel.h"
#include "ui/widget/uimenu.h"
#define UI_MODAL_TITLE_TEXT_MAX 64
#define UI_MODAL_TITLE_SPRITES_MAX UI_MODAL_TITLE_TEXT_MAX
#define UI_MODAL_MESSAGE_TEXT_MAX 256
#define UI_MODAL_MESSAGE_SPRITES_MAX UI_MODAL_MESSAGE_TEXT_MAX
#define UI_MODAL_OPTIONS_MAX 4
#define UI_MODAL_MIN_WIDTH 160.0f
#define UI_MODAL_RESULT_NONE 0xFF
/**
* Callback invoked once a modal is dismissed.
*
* @param optionIndex Index into the options array passed to uiModalOpen
* that was selected, or UI_MODAL_RESULT_NONE if backed out of without
* selecting an option.
* @param user Arbitrary pointer passed to uiModalOpen.
*/
typedef void (*uimodalcallback_t)(const uint8_t optionIndex, void *user);
typedef struct {
uilabel_t titleLabel;
char_t titleText[UI_MODAL_TITLE_TEXT_MAX];
spritebatchsprite_t titleSprites[UI_MODAL_TITLE_SPRITES_MAX];
uilabel_t messageLabel;
char_t messageText[UI_MODAL_MESSAGE_TEXT_MAX];
spritebatchsprite_t messageSprites[UI_MODAL_MESSAGE_SPRITES_MAX];
uimenu_t menu;
uimenuitem_t options[UI_MODAL_OPTIONS_MAX];
uimodalcallback_t callback;
void *user;
uint8_t result;
} uimodal_t;
extern uimodal_t UI_MODAL;
/**
* Initializes the modal dialog.
*
* @return Any error that occurs.
*/
errorret_t uiModalInit(void);
/**
* Draws the modal dialog: a semi-transparent black backdrop covering the
* whole screen, then its own centered frame with the title, message, and
* option buttons. No-op when not open.
*
* @return Any error that occurs.
*/
errorret_t uiModalDraw(void);
/**
* Returns true when the modal dialog is currently open.
*
* @returns True if open.
*/
bool_t uiModalIsOpen(void);
/**
* Returns the result of the most recently dismissed modal.
*
* @returns The selected option index, or UI_MODAL_RESULT_NONE.
*/
uint8_t uiModalGetResult(void);
/**
* Opens the modal dialog with the given title, message, and options.
* callback is invoked exactly once with the result, whether the dialog
* was dismissed by selecting an option or by pressing cancel/back.
*
* @param title Display title; copied internally, safe to be transient.
* @param message Display message; copied internally, safe to be
* transient.
* @param options Array of option label strings; NOT copied internally,
* the pointers are stored directly by the underlying buttons, so they
* must remain valid for as long as the modal is open (e.g. string
* literals or locale-owned strings).
* @param optionCount Number of options, from 1 to UI_MODAL_OPTIONS_MAX.
* @param callback Called with the result once the dialog closes. May be
* NULL.
* @param user Arbitrary pointer passed through to callback.
*/
void uiModalOpen(
const char_t *title,
const char_t *message,
const char_t **options,
const uint8_t optionCount,
uimodalcallback_t callback,
void *user
);
/**
* Closes the modal dialog. No-op when already closed.
*/
void uiModalClose(void);
/**
* Disposes of the modal dialog.
*
* @return Any error that occurs.
*/
errorret_t uiModalDispose(void);
+1 -1
View File
@@ -14,7 +14,7 @@
#include "display/color.h"
#include "display/spritebatch/spritebatch.h"
#include "display/shader/shaderunlit.h"
#include "ui/frame/uiframe.h"
#include "ui/widget/uiframe.h"
void uiTextboxInit(
uitextbox_t *box,
+1 -1
View File
@@ -12,7 +12,7 @@
#include "display/color.h"
#include "display/spritebatch/spritebatch.h"
#include "display/shader/shaderunlit.h"
#include "ui/frame/uiframe.h"
#include "ui/widget/uiframe.h"
uitextboxmain_t UI_TEXTBOX_MAIN;
static uifocusitem_t *focusItem = NULL;
+1 -1
View File
@@ -12,7 +12,7 @@
#include "rpg/rpgcamera.h"
#include "display/text/text.h"
#include "display/screen/screen.h"
#include "ui/frame/uiframe.h"
#include "ui/widget/uiframe.h"
void uiTextboxMiniInit(uitextboxmini_t *mini) {
assertNotNull(mini, "Mini textbox cannot be NULL");
+1 -1
View File
@@ -7,7 +7,7 @@
#include "uielement.h"
#include "assert/assert.h"
#include "ui/frame/uiframe.h"
#include "ui/widget/uiframe.h"
#include "engine/engine.h"
bool_t uiElementIsNull(const uielement_t *element) {
+10 -4
View File
@@ -6,7 +6,7 @@
*/
#include "uielementlist.h"
#include "ui/frame/uiframe.h"
#include "ui/widget/uiframe.h"
#include "ui/debug/uifps.h"
#include "ui/overlay/uifullbox.h"
#include "ui/overlay/uiloading.h"
@@ -20,6 +20,7 @@
#include "ui/frame/battle/uibattlehud.h"
#include "ui/frame/backpack/uibackpack.h"
#include "ui/frame/uiconfirm.h"
#include "ui/frame/uimodal.h"
#include "ui/rpg/textbox/uitextboxmain.h"
#include "ui/rpg/textbox/uitextboxminilist.h"
#include "ui/rpg/uiemoji.h"
@@ -89,6 +90,11 @@ uielement_t UI_ELEMENTS[] = {
.draw = uiConfirmDraw,
.dispose = uiConfirmDispose
},
{
.init = uiModalInit,
.draw = uiModalDraw,
.dispose = uiModalDispose
},
{
.init = uiTextboxMainInit,
@@ -133,9 +139,9 @@ uielement_t UI_ELEMENTS[] = {
// ===========================================================================
// Debug components, disregards everything else.
// ===========================================================================
{ .draw = uiConsoleDraw },
{ .draw = uiFPSDraw },
{ .draw = uiPlayerPosDraw },
{ .init = uiConsoleInit, .draw = uiConsoleDraw },
{ .init = uiFPSInit, .draw = uiFPSDraw },
{ .init = uiPlayerPosInit, .draw = uiPlayerPosDraw },
// ===========================================================================
// Null terminator
+1
View File
@@ -7,6 +7,7 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
uibutton.c
uicheckbox.c
uiframe.c
uilabel.c
uitab.c
uislider.c
+11 -9
View File
@@ -8,14 +8,15 @@
#include "uibutton.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "display/text/text.h"
#include "display/color.h"
void uiButtonInit(uibutton_t *button, const char_t *label) {
assertNotNull(button, "Button cannot be NULL");
assertNotNull(label, "Label cannot be NULL");
memoryZero(button, sizeof(uibutton_t));
button->label = label;
uiLabelInit(&button->label, label, button->sprites, UI_BUTTON_SPRITES_MAX);
button->label.dirty = true;
}
bool_t uiButtonIsHighlighted(const uibutton_t *button) {
@@ -29,15 +30,16 @@ void uiButtonSetHighlighted(uibutton_t *button, const bool_t highlighted) {
}
errorret_t uiButtonDraw(
const uibutton_t *button,
uibutton_t *button,
const float_t x,
const float_t y
) {
assertNotNull(button, "Button cannot be NULL");
errorChain(textDraw(
x, y, button->label,
button->highlighted ? COLOR_RED : COLOR_WHITE,
&FONT_DEFAULT
));
errorOk();
uiLabelSetX(&button->label, x);
uiLabelSetY(&button->label, y);
return uiLabelRender(
&button->label, button->highlighted ? COLOR_RED : COLOR_WHITE
);
}
+8 -3
View File
@@ -7,9 +7,13 @@
#pragma once
#include "error/error.h"
#include "ui/widget/uilabel.h"
#define UI_BUTTON_SPRITES_MAX 32
typedef struct {
const char_t *label;
uilabel_t label;
spritebatchsprite_t sprites[UI_BUTTON_SPRITES_MAX];
bool_t highlighted;
} uibutton_t;
@@ -17,7 +21,8 @@ typedef struct {
* Initializes a button.
*
* @param button The button to initialize.
* @param label Display label.
* @param label Display label; never written to, safe to be a caller-
* owned or immutable string.
*/
void uiButtonInit(uibutton_t *button, const char_t *label);
@@ -46,7 +51,7 @@ void uiButtonSetHighlighted(uibutton_t *button, const bool_t highlighted);
* @return Any error that occurs.
*/
errorret_t uiButtonDraw(
const uibutton_t *button,
uibutton_t *button,
const float_t x,
const float_t y
);
+27 -14
View File
@@ -6,25 +6,25 @@
*/
#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"
void uiLabelInit(uilabel_t *label, const char_t *text) {
void uiLabelInit(
uilabel_t *label,
const char_t *text,
spritebatchsprite_t *sprites,
const int32_t spritesMax
) {
assertNotNull(text, "Text buffer cannot be NULL");
assertNotNull(sprites, "Sprites buffer cannot be NULL");
memoryZero(label, sizeof(uilabel_t));
uiLabelSetText(label, text);
}
void uiLabelSetText(uilabel_t *label, const char_t *text) {
if(stringCompare(label->text, text) == 0) return;
stringCopy(label->text, text, UI_LABEL_TEXT_MAX);
label->dirty = true;
}
const char_t * uiLabelGetText(const uilabel_t *label) {
return label->text;
label->text = text;
label->sprites = sprites;
label->spritesMax = spritesMax;
}
void uiLabelSetX(uilabel_t *label, const float_t x) {
@@ -49,6 +49,19 @@ 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);
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->sprites, label->spritesMax,
&charIndex, &posX, &posY
);
label->dirty = false;
}
errorret_t uiLabelRender(uilabel_t *label, const color_t color) {
@@ -62,7 +75,7 @@ errorret_t uiLabelRender(uilabel_t *label, const color_t color) {
}
};
errorChain(spriteBatchBuffer(
label->buffer, label->spriteCount, &SHADER_UNLIT, material
label->sprites, label->spriteCount, &SHADER_UNLIT, material
));
errorOk();
+20 -28
View File
@@ -10,12 +10,10 @@
#include "display/spritebatch/spritebatchsprite.h"
#include "display/color.h"
#define UI_LABEL_TEXT_MAX 128
typedef struct {
char_t text[UI_LABEL_TEXT_MAX];
spritebatchsprite_t sprites[UI_LABEL_TEXT_MAX];
spritebatchsprite_t buffer[UI_LABEL_TEXT_MAX];
const char_t *text;
spritebatchsprite_t *sprites;
int32_t spritesMax;
uint32_t spriteCount;
int32_t width;
int32_t height;
@@ -25,28 +23,23 @@ typedef struct {
} uilabel_t;
/**
* Initializes a label.
* Initializes a label. The caller owns the text buffer and writes its
* contents directly (setting label->dirty afterwards); call
* uiLabelRebuffer or uiLabelRender to rebuild the cached sprites.
*
* @param label The label to initialize.
* @param text Display text; copied internally, safe to be transient.
* @param text Caller-owned, null-terminated string the label reads from
* directly; never written to by the label. Cannot be NULL.
* @param sprites Caller-owned sprite buffer the label writes cached
* glyph sprites into. Cannot be NULL.
* @param spritesMax Number of sprites the sprites buffer can hold.
*/
void uiLabelInit(uilabel_t *label, const char_t *text);
/**
* Sets the display text of the label, rebuilding its cached sprites.
*
* @param label The label to update.
* @param text Display text; copied internally, safe to be transient.
*/
void uiLabelSetText(uilabel_t *label, const char_t *text);
/**
* Returns the display text of the label.
*
* @param label The label to query.
* @returns The label's text.
*/
const char_t * uiLabelGetText(const uilabel_t *label);
void uiLabelInit(
uilabel_t *label,
const char_t *text,
spritebatchsprite_t *sprites,
const int32_t spritesMax
);
/**
* Sets the screen x position of the label.
@@ -81,10 +74,9 @@ void uiLabelSetY(uilabel_t *label, const float_t y);
float_t uiLabelGetY(const uilabel_t *label);
/**
* Rebuffers the label: rebuilds its cached local-space sprites if the
* text has changed, then re-translates them into its render buffer at
* its current x/y position. Called internally by uiLabelRender when
* either dirty flag is set.
* Rebuffers the label's cached sprites from its current text at its
* current x/y position. No-op unless the label is dirty. Called
* internally by uiLabelRender.
*
* @param label The label to rebuffer.
*/
+1 -1
View File
@@ -98,7 +98,7 @@ errorret_t uiMenuDraw(
uint8_t row = 0;
for(uint8_t i = 0; i < menu->itemCount; i++) {
const uimenuitem_t *item = &menu->items[i];
uimenuitem_t *item = &menu->items[i];
if(item->type == UI_MENU_WIDGET_TYPE_LABEL) {
if(col > 0) { row++; col = 0; }