Mini textboxes

This commit is contained in:
2026-07-08 10:53:53 -05:00
parent b693ea4102
commit 46e2a924d3
23 changed files with 558 additions and 147 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ console_t CONSOLE;
void consoleInit(void) {
memoryZero(&CONSOLE, sizeof(console_t));
CONSOLE.visible = true;
CONSOLE.visible = false;
#ifdef DUSK_CONSOLE_POSIX
threadMutexInit(&CONSOLE.printMutex);
@@ -7,7 +7,7 @@
#include "rpg/cutscene/item/cutsceneitem.h"
#include "rpg/item/itemgive.h"
#include "ui/rpg/uitextboxmain.h"
#include "ui/rpg/textbox/uitextboxmain.h"
void cutsceneItemGiveStart(
const cutsceneitem_t *item,
+1 -1
View File
@@ -6,7 +6,7 @@
*/
#include "rpg/cutscene/item/cutsceneitem.h"
#include "ui/rpg/uitextboxmain.h"
#include "ui/rpg/textbox/uitextboxmain.h"
void cutsceneTextStart(
const cutsceneitem_t *item,
@@ -8,7 +8,7 @@
#include "rpg/entity/entity.h"
#include "assert/assert.h"
#include "rpg/cutscene/cutscenesystem.h"
#include "ui/rpg/uitextboxmain.h"
#include "ui/rpg/textbox/uitextboxmain.h"
void entityInteractWith(entity_t *player, entity_t *target) {
assertNotNull(player, "Player entity pointer cannot be NULL");
+1 -1
View File
@@ -9,7 +9,7 @@
#include "rpg/entity/entity.h"
#include "assert/assert.h"
#include "rpg/item/itemgive.h"
#include "ui/rpg/uitextboxmain.h"
#include "ui/rpg/textbox/uitextboxmain.h"
void entityItemInit(entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL");
+1 -1
View File
@@ -7,7 +7,7 @@
#include "itemgive.h"
#include "rpg/item/backpack.h"
#include "ui/rpg/uitextboxmain.h"
#include "ui/rpg/textbox/uitextboxmain.h"
#include "util/string.h"
#include "error/error.h"
+11
View File
@@ -15,6 +15,7 @@
#include "rpg/cutscene/scene/testcutscene.h"
#include "rpg/item/backpack.h"
#include "rpg/battle/party.h"
#include "ui/rpg/textbox/uitextboxminilist.h"
#include "time/time.h"
#include "rpgcamera.h"
#include "util/memory.h"
@@ -64,6 +65,16 @@ errorret_t rpgInit(void) {
backpackAdd(ITEM_ID_POTATO, 3);
backpackAdd(ITEM_ID_APPLE, 8);
// TEST: Show a mini textbox at the world origin.
uiTextboxMiniShow(
&UI_TEXTBOX_MINI_LIST[0],
"Hello, World!",
(vec3){ 0.0f, 0.0f, 0.0f },
100.0f,
NULL,
NULL
);
// TEST: Create a test map area.
uint8_t areaIndex = mapAreaAdd(
(worldpos_t){ 11, 3, 0 },
+33
View File
@@ -56,6 +56,39 @@ void rpgCameraUpdateProjection(void) {
);
}
void rpgCameraUpdateEye(void) {
float_t fov = glm_rad(RPG_CAMERA_FOV);
float_t pixelsPerUnit = TILE_SIZE_PIXELS;
float_t worldH = (float_t)(SCREEN.height / SCREEN.scale3d) / pixelsPerUnit;
float_t z = (worldH * 0.5f) / tanf(fov * 0.5f);
float_t offset = -24.0f * (worldH / TILE_SIZE_PIXELS);
vec3 target;
rpgCameraGetPosition(target);
glm_vec3_add(target, (vec3){ 0.5f, 0.5f, 0.5f }, target);
glm_lookat(
(vec3){ target[0], target[1] + offset, target[2] + z },
target,
(vec3){ 0, 1, 0 }, // up
RPG_CAMERA.eye
);
}
void rpgCameraToScreen(vec3 worldPos, vec2 out) {
mat4 viewProj;
glm_mat4_mul(RPG_CAMERA.projection, RPG_CAMERA.eye, viewProj);
vec4 viewport = {
0.0f, 0.0f, (float_t)SCREEN.width, (float_t)SCREEN.height
};
vec3 window;
glm_project(worldPos, viewProj, viewport, window);
out[0] = window[0];
out[1] = (float_t)SCREEN.height - window[1];
}
errorret_t rpgCameraUpdate(void) {
if(!mapIsLoaded()) errorOk();
+18 -1
View File
@@ -59,4 +59,21 @@ errorret_t rpgCameraUpdate(void);
* matrix is computed once and cached; on dynamic-display platforms it
* is recomputed every call.
*/
void rpgCameraUpdateProjection(void);
void rpgCameraUpdateProjection(void);
/**
* Recomputes the camera eye/view matrix from the camera's current mode
* and position, and stores it in RPG_CAMERA.eye. Unlike the projection
* matrix this is never cached, since the camera position can change
* every frame.
*/
void rpgCameraUpdateEye(void);
/**
* Converts a world-space position to screen-space pixel coordinates,
* using the camera's current eye and projection matrices.
*
* @param worldPos The world-space position to convert.
* @param out Output vec2 filled with the screen-space pixel position.
*/
void rpgCameraToScreen(vec3 worldPos, vec2 out);
+5 -22
View File
@@ -44,7 +44,7 @@ errorret_t sceneOverworldRender(scenedata_t *sceneData) {
.flags = DISPLAY_STATE_FLAG_DEPTH_TEST | DISPLAY_STATE_FLAG_CULL
}));
mat4 model, eye;
mat4 model;
errorChain(shaderBind(&SHADER_UNLIT));
@@ -59,27 +59,10 @@ errorret_t sceneOverworldRender(scenedata_t *sceneData) {
));
// Camera Eye
float_t fov = glm_rad(RPG_CAMERA_FOV);
float_t pixelsPerUnit = TILE_SIZE_PIXELS;
float_t worldH = (float_t)(SCREEN.height / SCREEN.scale3d) / pixelsPerUnit;
float_t z = (worldH * 0.5f) / tanf(fov * 0.5f);
vec3 worldPosVec;
rpgCameraGetPosition(worldPosVec);
float_t offset = -24.0f * (worldH / TILE_SIZE_PIXELS);
glm_vec3_add(worldPosVec, (vec3){ 0.5f, 0.5f, 0.5f }, worldPosVec);
glm_lookat(
(vec3){
worldPosVec[0],
worldPosVec[1] + offset,
worldPosVec[2] + z
},
worldPosVec,
(vec3){ 0, 1, 0 }, // up
eye
);
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, eye));
rpgCameraUpdateEye();
errorChain(shaderSetMatrix(
&SHADER_UNLIT, SHADER_UNLIT_VIEW, RPG_CAMERA.eye
));
// Base terrain meshes, drawn with normal depth testing.
errorChain(sceneOverworldDrawChunksBase());
+2 -2
View File
@@ -12,8 +12,8 @@
#define UI_FRAME_BORDER_WIDTH 6
#define UI_FRAME_BORDER_HEIGHT 6
#define UI_FRAME_PADDING_X 4
#define UI_FRAME_PADDING_Y 4
#define UI_FRAME_PADDING_X 2
#define UI_FRAME_PADDING_Y 2
#define UI_FRAME_START_X (UI_FRAME_BORDER_WIDTH + UI_FRAME_PADDING_X)
#define UI_FRAME_START_Y (UI_FRAME_BORDER_HEIGHT + UI_FRAME_PADDING_Y)
#define UI_FRAME_TILE_WIDTH 1
+1 -5
View File
@@ -3,8 +3,4 @@
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
uitextbox.c
uitextboxmain.c
)
add_subdirectory(textbox)
+12
View File
@@ -0,0 +1,12 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
uitextbox.c
uitextboxmain.c
uitextboxmini.c
uitextboxminilist.c
)
@@ -16,15 +16,29 @@
#include "display/shader/shaderunlit.h"
#include "ui/frame/uiframe.h"
void uiTextboxInit(uitextbox_t *box) {
void uiTextboxInit(
uitextbox_t *box,
char_t *text,
const uint32_t maxLength,
uitextboxline_t *lines,
const uint32_t linesMax
) {
assertNotNull(box, "Textbox cannot be NULL");
assertNotNull(text, "Text buffer cannot be NULL");
assertTrue(maxLength >= 1, "maxLength must be at least 1");
assertNotNull(lines, "Lines buffer cannot be NULL");
assertTrue(linesMax >= 1, "linesMax must be at least 1");
memoryZero(box, sizeof(uitextbox_t));
box->text = text;
box->maxLength = maxLength;
box->lines = lines;
box->linesMax = linesMax;
}
void uiTextboxSetText(uitextbox_t *box, const char_t *text) {
assertNotNull(box, "Textbox cannot be NULL");
assertNotNull(text, "Text cannot be NULL");
stringCopy(box->text, text, UI_TEXTBOX_TEXT_MAX);
stringCopy(box->text, text, box->maxLength);
box->currentPage = 0;
box->scroll = 0;
box->layoutWidth = 0.0f;
@@ -60,12 +74,12 @@ void uiTextboxBuildLayout(
char_t *src = box->text;
int32_t i = 0;
while(src[i] != '\0' && box->lineCount < UI_TEXTBOX_LINES_MAX) {
while(src[i] != '\0' && box->lineCount < (int32_t)box->linesMax) {
if(src[i] == '\t') {
i++;
int32_t rem = box->lineCount % box->linesPerPage;
int32_t pad = rem > 0 ? box->linesPerPage - rem : 0;
while(pad > 0 && box->lineCount < UI_TEXTBOX_LINES_MAX) {
while(pad > 0 && box->lineCount < (int32_t)box->linesMax) {
box->lines[box->lineCount].start = i;
box->lines[box->lineCount].count = 0;
box->lineCount++;
@@ -196,18 +210,6 @@ errorret_t uiTextboxDraw(
charsLeft -= visible;
}
if(uiTextboxPageIsComplete(box)) {
spritebatchsprite_t caret = textGetSprite(
(vec2){
contentX + contentW - fontW,
contentY + contentH - fontH
},
'v',
&FONT_DEFAULT
);
errorChain(spriteBatchBuffer(&caret, 1, &SHADER_UNLIT, material));
}
errorChain(spriteBatchFlush());
errorOk();
}
@@ -8,8 +8,6 @@
#pragma once
#include "error/error.h"
#define UI_TEXTBOX_TEXT_MAX 1024
#define UI_TEXTBOX_LINES_MAX 64
#define UI_TEXTBOX_LINES_PER_PAGE_MAX 4
#define UI_TEXTBOX_SCROLL_CHARS_PER_TICK 1
#define UI_TEXTBOX_LINE_SPACING 0.0f
@@ -20,9 +18,11 @@ typedef struct {
} uitextboxline_t;
typedef struct {
char_t text[UI_TEXTBOX_TEXT_MAX];
char_t *text;
uint32_t maxLength;
uitextboxline_t lines[UI_TEXTBOX_LINES_MAX];
uitextboxline_t *lines;
uint32_t linesMax;
int32_t lineCount;
int32_t charsPerLine;
int32_t linesPerPage;
@@ -37,11 +37,22 @@ typedef struct {
} uitextbox_t;
/**
* Initializes a textbox, zeroing all state.
* 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);
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.
+122
View File
@@ -0,0 +1,122 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "uitextboxmain.h"
#include "ui/focus/uifocus.h"
#include "display/screen/screen.h"
#include "display/text/text.h"
#include "display/color.h"
#include "display/spritebatch/spritebatch.h"
#include "display/shader/shaderunlit.h"
#include "ui/frame/uiframe.h"
uitextboxmain_t UI_TEXTBOX_MAIN;
static uifocusitem_t *focusItem = NULL;
errorret_t uiTextboxMainInit(void) {
uiTextboxInit(
&UI_TEXTBOX_MAIN.box,
UI_TEXTBOX_MAIN.text, UI_TEXTBOX_MAIN_TEXT_MAX,
UI_TEXTBOX_MAIN.lines, UI_TEXTBOX_MAIN_LINES_MAX
);
errorOk();
}
void uiTextboxMainSetText(const char_t *text) {
uiTextboxSetText(&UI_TEXTBOX_MAIN.box, text);
if(focusItem != NULL) return;
focusItem = uiFocusPush(
1, 1,
uiTextboxMainFocusSelected,
NULL,
uiTextboxMainFocusClosed,
NULL,
NULL
);
}
errorret_t uiTextboxMainUpdate(void) {
if(focusItem == NULL) errorOk();
return uiTextboxUpdate(&UI_TEXTBOX_MAIN.box);
}
errorret_t uiTextboxMainDraw(void) {
if(focusItem == NULL) errorOk();
float_t fontH = (float_t)FONT_DEFAULT.tileset->tileHeight;
float_t h = (float_t)UI_TEXTBOX_MAIN_LINES * fontH +
(float_t)(UI_TEXTBOX_MAIN_LINES - 1) * UI_TEXTBOX_LINE_SPACING +
2.0f * (float_t)UI_FRAME_START_Y;
float_t w = (float_t)SCREEN.scanWidth;
float_t x = (float_t)SCREEN.scanX;
float_t y = (float_t)(SCREEN.scanY + SCREEN.scanHeight) - h;
errorChain(uiTextboxDraw(&UI_TEXTBOX_MAIN.box, x, y, w, h));
if(!uiTextboxPageIsComplete(&UI_TEXTBOX_MAIN.box)) errorOk();
float_t fontW = (float_t)FONT_DEFAULT.tileset->tileWidth;
float_t contentX = x + (float_t)UI_FRAME_START_X;
float_t contentY = y + (float_t)UI_FRAME_START_Y;
float_t contentW = w - 2.0f * (float_t)UI_FRAME_START_X;
float_t contentH = h - 2.0f * (float_t)UI_FRAME_START_Y;
shadermaterial_t material = {
.unlit = {
.color = COLOR_WHITE,
.texture = FONT_DEFAULT.texture
}
};
spritebatchsprite_t caret = textGetSprite(
(vec2){
contentX + contentW - fontW,
contentY + contentH - fontH
},
'v',
&FONT_DEFAULT
);
errorChain(spriteBatchBuffer(&caret, 1, &SHADER_UNLIT, material));
errorChain(spriteBatchFlush());
errorOk();
}
bool_t uiTextboxMainPageIsComplete(void) {
return uiTextboxPageIsComplete(&UI_TEXTBOX_MAIN.box);
}
bool_t uiTextboxMainHasNextPage(void) {
return uiTextboxHasNextPage(&UI_TEXTBOX_MAIN.box);
}
void uiTextboxMainNextPage(void) {
uiTextboxNextPage(&UI_TEXTBOX_MAIN.box);
}
bool_t uiTextboxMainIsActive(void) {
return focusItem != NULL;
}
bool_t uiTextboxMainFocusSelected(const uifocusitem_t *item) {
if(!uiTextboxPageIsComplete(&UI_TEXTBOX_MAIN.box)) {
UI_TEXTBOX_MAIN.box.scroll =
uiTextboxGetPageCharCount(&UI_TEXTBOX_MAIN.box);
return true;
}
if(uiTextboxHasNextPage(&UI_TEXTBOX_MAIN.box)) {
uiTextboxNextPage(&UI_TEXTBOX_MAIN.box);
return true;
}
uiFocusPopItem(focusItem);
return true;
}
bool_t uiTextboxMainFocusClosed(const uifocusitem_t *item) {
focusItem = NULL;
return true;
}
@@ -6,12 +6,20 @@
*/
#pragma once
#include "ui/rpg/uitextbox.h"
#include "ui/rpg/textbox/uitextbox.h"
#include "ui/focus/uifocusitem.h"
#define UI_TEXTBOX_MAIN_LINES 4
#define UI_TEXTBOX_MAIN_TEXT_MAX 1024
#define UI_TEXTBOX_MAIN_LINES_MAX 64
extern uitextbox_t UI_TEXTBOX_MAIN;
typedef struct {
uitextbox_t box;
char_t text[UI_TEXTBOX_MAIN_TEXT_MAX];
uitextboxline_t lines[UI_TEXTBOX_MAIN_LINES_MAX];
} uitextboxmain_t;
extern uitextboxmain_t UI_TEXTBOX_MAIN;
/**
* Initializes UI_TEXTBOX_MAIN.
+120
View File
@@ -0,0 +1,120 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "uitextboxmini.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "time/time.h"
#include "rpg/rpgcamera.h"
#include "display/text/text.h"
#include "display/screen/screen.h"
#include "ui/frame/uiframe.h"
void uiTextboxMiniInit(uitextboxmini_t *mini) {
assertNotNull(mini, "Mini textbox cannot be NULL");
memoryZero(mini, sizeof(uitextboxmini_t));
uiTextboxInit(
&mini->box,
mini->text, UI_TEXTBOX_MINI_TEXT_MAX,
mini->lines, UI_TEXTBOX_MINI_LINES_MAX
);
}
void uiTextboxMiniShow(
uitextboxmini_t *mini,
const char_t *text,
vec3 position,
const float_t duration,
uitextboxminiclosedcallback_t closed,
void *user
) {
assertNotNull(mini, "Mini textbox cannot be NULL");
assertNotNull(text, "Text cannot be NULL");
uiTextboxSetText(&mini->box, text);
glm_vec3_copy(position, mini->position);
int32_t textWidth, textHeight;
textMeasure(text, &FONT_DEFAULT, &textWidth, &textHeight);
float_t width = (float_t)textWidth + 2.0f * (float_t)UI_FRAME_START_X;
mini->width = width < UI_TEXTBOX_MINI_WIDTH_MAX
? width : UI_TEXTBOX_MINI_WIDTH_MAX;
float_t fontH = (float_t)FONT_DEFAULT.tileset->tileHeight;
float_t maxHeight = (float_t)UI_TEXTBOX_MINI_LINES_MAX * fontH +
(float_t)(UI_TEXTBOX_MINI_LINES_MAX - 1) * UI_TEXTBOX_LINE_SPACING +
2.0f * (float_t)UI_FRAME_START_Y;
uiTextboxBuildLayout(
&mini->box,
mini->width - 2.0f * (float_t)UI_FRAME_START_X,
maxHeight - 2.0f * (float_t)UI_FRAME_START_Y
);
int32_t lineCount = mini->box.lineCount > 0 ? mini->box.lineCount : 1;
mini->height = (float_t)lineCount * fontH +
(float_t)(lineCount - 1) * UI_TEXTBOX_LINE_SPACING +
2.0f * (float_t)UI_FRAME_START_Y;
mini->active = true;
mini->timer = duration;
mini->closed = closed;
mini->user = user;
}
errorret_t uiTextboxMiniUpdate(uitextboxmini_t *mini) {
assertNotNull(mini, "Mini textbox cannot be NULL");
if(!mini->active) errorOk();
errorChain(uiTextboxUpdate(&mini->box));
mini->timer -= TIME.delta;
if(mini->timer <= 0.0f) uiTextboxMiniClose(mini);
errorOk();
}
errorret_t uiTextboxMiniDraw(uitextboxmini_t *mini) {
assertNotNull(mini, "Mini textbox cannot be NULL");
if(!mini->active) errorOk();
vec2 screenPos;
rpgCameraToScreen(mini->position, screenPos);
if(
mini->clamp == UI_TEXTBOX_MINI_CLAMP_X ||
mini->clamp == UI_TEXTBOX_MINI_CLAMP_BOTH
) {
float_t minX = (float_t)SCREEN.scanX;
float_t maxX = (float_t)(SCREEN.scanX + SCREEN.scanWidth) - mini->width;
if(screenPos[0] < minX) screenPos[0] = minX;
if(screenPos[0] > maxX) screenPos[0] = maxX;
}
if(
mini->clamp == UI_TEXTBOX_MINI_CLAMP_Y ||
mini->clamp == UI_TEXTBOX_MINI_CLAMP_BOTH
) {
float_t minY = (float_t)SCREEN.scanY;
float_t maxY = (float_t)(SCREEN.scanY + SCREEN.scanHeight) - mini->height;
if(screenPos[1] < minY) screenPos[1] = minY;
if(screenPos[1] > maxY) screenPos[1] = maxY;
}
return uiTextboxDraw(
&mini->box, screenPos[0], screenPos[1], mini->width, mini->height
);
}
bool_t uiTextboxMiniIsActive(const uitextboxmini_t *mini) {
assertNotNull(mini, "Mini textbox cannot be NULL");
return mini->active;
}
void uiTextboxMiniClose(uitextboxmini_t *mini) {
assertNotNull(mini, "Mini textbox cannot be NULL");
if(!mini->active) return;
mini->active = false;
if(mini->closed != NULL) mini->closed(mini);
}
+110
View File
@@ -0,0 +1,110 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "ui/rpg/textbox/uitextbox.h"
#define UI_TEXTBOX_MINI_TEXT_MAX 128
#define UI_TEXTBOX_MINI_LINES_MAX 2
#define UI_TEXTBOX_MINI_WIDTH_MAX 160.0f
typedef struct uitextboxmini_s uitextboxmini_t;
typedef void (*uitextboxminiclosedcallback_t)(const uitextboxmini_t *mini);
/**
* Determines whether a mini textbox's screen position is clamped to stay
* fully on screen. Defaults to UI_TEXTBOX_MINI_CLAMP_NONE.
*/
typedef enum {
UI_TEXTBOX_MINI_CLAMP_NONE,
UI_TEXTBOX_MINI_CLAMP_X,
UI_TEXTBOX_MINI_CLAMP_Y,
UI_TEXTBOX_MINI_CLAMP_BOTH
} uitextboxminiclamp_t;
typedef struct uitextboxmini_s {
uitextbox_t box;
char_t text[UI_TEXTBOX_MINI_TEXT_MAX];
uitextboxline_t lines[UI_TEXTBOX_MINI_LINES_MAX];
vec3 position;
float_t width;
float_t height;
uitextboxminiclamp_t clamp;
bool_t active;
float_t timer;
uitextboxminiclosedcallback_t closed;
void *user;
} uitextboxmini_t;
/**
* Initializes a mini textbox, zeroing all state and binding its internal
* uitextbox_t to its own fixed-size text and line storage.
*
* @param mini The mini textbox to initialize.
*/
void uiTextboxMiniInit(uitextboxmini_t *mini);
/**
* Shows a mini textbox with the given text at the given world position for
* the given duration. Resets the typewriter scroll, measures the text to
* size the box (width capped at UI_TEXTBOX_MINI_WIDTH_MAX, height derived
* from the resulting wrapped line count), and starts the visibility timer.
*
* @param mini The mini textbox to show.
* @param text Null-terminated source string.
* @param position World-space position the box is anchored to on screen.
* @param duration How long the mini textbox stays visible, in seconds.
* @param closed Called once the timer elapses and the box closes. May be
* NULL.
* @param user Opaque pointer passed back through the closed callback.
*/
void uiTextboxMiniShow(
uitextboxmini_t *mini,
const char_t *text,
vec3 position,
const float_t duration,
uitextboxminiclosedcallback_t closed,
void *user
);
/**
* Advances the typewriter scroll and counts down the visibility timer.
* Closes the mini textbox and fires its closed callback once the timer
* elapses. Has no effect if not active.
*
* @param mini The mini textbox to update.
* @returns Any error that occurs.
*/
errorret_t uiTextboxMiniUpdate(uitextboxmini_t *mini);
/**
* Draws the mini textbox at its text-measured size, anchored on screen to
* its world-space position via the RPG camera. Has no effect if not
* active.
*
* @param mini The mini textbox to draw.
* @returns Any error that occurs.
*/
errorret_t uiTextboxMiniDraw(uitextboxmini_t *mini);
/**
* Returns true when the mini textbox is currently visible.
*
* @param mini The mini textbox to query.
* @returns True if active.
*/
bool_t uiTextboxMiniIsActive(const uitextboxmini_t *mini);
/**
* Immediately closes the mini textbox and fires its closed callback.
* Has no effect if not active.
*
* @param mini The mini textbox to close.
*/
void uiTextboxMiniClose(uitextboxmini_t *mini);
@@ -0,0 +1,31 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "uitextboxminilist.h"
uitextboxmini_t UI_TEXTBOX_MINI_LIST[UI_TEXTBOX_MINI_LIST_COUNT];
errorret_t uiTextboxMiniListInit(void) {
for(uint8_t i = 0; i < UI_TEXTBOX_MINI_LIST_COUNT; i++) {
uiTextboxMiniInit(&UI_TEXTBOX_MINI_LIST[i]);
}
errorOk();
}
errorret_t uiTextboxMiniListUpdate(void) {
for(uint8_t i = 0; i < UI_TEXTBOX_MINI_LIST_COUNT; i++) {
errorChain(uiTextboxMiniUpdate(&UI_TEXTBOX_MINI_LIST[i]));
}
errorOk();
}
errorret_t uiTextboxMiniListDraw(void) {
for(uint8_t i = 0; i < UI_TEXTBOX_MINI_LIST_COUNT; i++) {
errorChain(uiTextboxMiniDraw(&UI_TEXTBOX_MINI_LIST[i]));
}
errorOk();
}
@@ -0,0 +1,34 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "ui/rpg/textbox/uitextboxmini.h"
#define UI_TEXTBOX_MINI_LIST_COUNT 4
extern uitextboxmini_t UI_TEXTBOX_MINI_LIST[UI_TEXTBOX_MINI_LIST_COUNT];
/**
* Initializes all UI_TEXTBOX_MINI_LIST slots.
*
* @returns Any error that occurs.
*/
errorret_t uiTextboxMiniListInit(void);
/**
* Updates all UI_TEXTBOX_MINI_LIST slots.
*
* @returns Any error that occurs.
*/
errorret_t uiTextboxMiniListUpdate(void);
/**
* Draws all active UI_TEXTBOX_MINI_LIST slots.
*
* @returns Any error that occurs.
*/
errorret_t uiTextboxMiniListDraw(void);
-86
View File
@@ -1,86 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "uitextboxmain.h"
#include "ui/focus/uifocus.h"
#include "display/screen/screen.h"
#include "display/text/text.h"
#include "ui/frame/uiframe.h"
uitextbox_t UI_TEXTBOX_MAIN;
static uifocusitem_t *focusItem = NULL;
errorret_t uiTextboxMainInit(void) {
uiTextboxInit(&UI_TEXTBOX_MAIN);
errorOk();
}
void uiTextboxMainSetText(const char_t *text) {
uiTextboxSetText(&UI_TEXTBOX_MAIN, text);
if(focusItem != NULL) return;
focusItem = uiFocusPush(
1, 1,
uiTextboxMainFocusSelected,
NULL,
uiTextboxMainFocusClosed,
NULL,
NULL
);
}
errorret_t uiTextboxMainUpdate(void) {
if(focusItem == NULL) errorOk();
return uiTextboxUpdate(&UI_TEXTBOX_MAIN);
}
errorret_t uiTextboxMainDraw(void) {
if(focusItem == NULL) errorOk();
float_t fontH = (float_t)FONT_DEFAULT.tileset->tileHeight;
float_t h = (float_t)UI_TEXTBOX_MAIN_LINES * fontH +
(float_t)(UI_TEXTBOX_MAIN_LINES - 1) * UI_TEXTBOX_LINE_SPACING +
2.0f * (float_t)UI_FRAME_START_Y;
float_t w = (float_t)SCREEN.scanWidth;
float_t x = (float_t)SCREEN.scanX;
float_t y = (float_t)(SCREEN.scanY + SCREEN.scanHeight) - h;
return uiTextboxDraw(&UI_TEXTBOX_MAIN, x, y, w, h);
}
bool_t uiTextboxMainPageIsComplete(void) {
return uiTextboxPageIsComplete(&UI_TEXTBOX_MAIN);
}
bool_t uiTextboxMainHasNextPage(void) {
return uiTextboxHasNextPage(&UI_TEXTBOX_MAIN);
}
void uiTextboxMainNextPage(void) {
uiTextboxNextPage(&UI_TEXTBOX_MAIN);
}
bool_t uiTextboxMainIsActive(void) {
return focusItem != NULL;
}
bool_t uiTextboxMainFocusSelected(const uifocusitem_t *item) {
if(!uiTextboxPageIsComplete(&UI_TEXTBOX_MAIN)) {
UI_TEXTBOX_MAIN.scroll = uiTextboxGetPageCharCount(&UI_TEXTBOX_MAIN);
return true;
}
if(uiTextboxHasNextPage(&UI_TEXTBOX_MAIN)) {
uiTextboxNextPage(&UI_TEXTBOX_MAIN);
return true;
}
uiFocusPopItem(focusItem);
return true;
}
bool_t uiTextboxMainFocusClosed(const uifocusitem_t *item) {
focusItem = NULL;
return true;
}
+8 -1
View File
@@ -21,7 +21,8 @@
#include "ui/frame/battle/uibattlemenu.h"
#include "ui/frame/backpack/uibackpack.h"
#include "ui/frame/uiconfirm.h"
#include "ui/rpg/uitextboxmain.h"
#include "ui/rpg/textbox/uitextboxmain.h"
#include "ui/rpg/textbox/uitextboxminilist.h"
uielement_t UI_ELEMENTS[] = {
{
@@ -74,6 +75,12 @@ uielement_t UI_ELEMENTS[] = {
.draw = uiTextboxMainDraw
},
{
.init = uiTextboxMiniListInit,
.update = uiTextboxMiniListUpdate,
.draw = uiTextboxMiniListDraw
},
{
.init = uiTransitionInit,
.update = uiTransitionUpdate,