UI Frame finally doing things
This commit is contained in:
@@ -61,10 +61,10 @@ errorret_t engineUpdate(void) {
|
||||
inputUpdate();
|
||||
consoleUpdate();
|
||||
errorChain(rpgUpdate());
|
||||
errorChain(uiUpdate());
|
||||
errorChain(cutsceneUpdate());
|
||||
errorChain(sceneUpdate());
|
||||
errorChain(assetUpdate());
|
||||
errorChain(uiUpdate());
|
||||
|
||||
// Render
|
||||
errorChain(displayUpdate());
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
|
||||
#include "entity.h"
|
||||
#include "assert/assert.h"
|
||||
#include "console/console.h"
|
||||
#include "rpg/cutscene/cutscenesystem.h"
|
||||
#include "ui/rpg/uitextboxmain.h"
|
||||
|
||||
void entityInteractWith(entity_t *player, entity_t *target) {
|
||||
assertNotNull(player, "Player entity pointer cannot be NULL");
|
||||
@@ -24,7 +24,7 @@ void entityInteractWith(entity_t *player, entity_t *target) {
|
||||
return;
|
||||
|
||||
case ENTITY_INTERACT_PRINT:
|
||||
consolePrint(target->interact.data.message);
|
||||
uiTextboxMainSetText(target->interact.data.message);
|
||||
return;
|
||||
|
||||
case ENTITY_INTERACT_NULL:
|
||||
|
||||
@@ -7,6 +7,7 @@ add_subdirectory(debug)
|
||||
add_subdirectory(frame)
|
||||
add_subdirectory(focus)
|
||||
add_subdirectory(overlay)
|
||||
add_subdirectory(rpg)
|
||||
add_subdirectory(widget)
|
||||
|
||||
# Sources
|
||||
|
||||
+15
-19
@@ -49,6 +49,7 @@ uifocusitem_t * uiFocusPush(
|
||||
item->closed = closed;
|
||||
item->user = user;
|
||||
UI_FOCUS.count++;
|
||||
UI_FOCUS.pushedThisTick = true;
|
||||
if(item->changed != NULL) item->changed(item);
|
||||
return item;
|
||||
}
|
||||
@@ -120,36 +121,39 @@ void uiFocusMoveDirection(
|
||||
void uiFocusUpdate(void) {
|
||||
if(UI_FOCUS.count == 0) return;
|
||||
|
||||
// Current item
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
if(TIME.dynamicUpdate) return;
|
||||
#endif
|
||||
|
||||
if(UI_FOCUS.pushedThisTick) {
|
||||
UI_FOCUS.pushedThisTick = false;
|
||||
return;
|
||||
}
|
||||
|
||||
uifocusitem_t *item = &UI_FOCUS.items[UI_FOCUS.count - 1];
|
||||
|
||||
// Accept
|
||||
if(inputPressedIfDynamic(INPUT_ACTION_ACCEPT)) {
|
||||
if(inputPressed(INPUT_ACTION_ACCEPT)) {
|
||||
if(item->selected != NULL) item->selected(item);
|
||||
return;
|
||||
}
|
||||
|
||||
// Return
|
||||
if(inputPressedIfDynamic(INPUT_ACTION_CANCEL)) {
|
||||
if(inputPressed(INPUT_ACTION_CANCEL)) {
|
||||
uiFocusPop();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle pressed directions
|
||||
for(
|
||||
const uifocusdirmap_t *m = UI_FOCUS_DIR_MAP;
|
||||
m->action != INPUT_ACTION_NULL;
|
||||
m++
|
||||
) {
|
||||
if(!inputPressedIfDynamic(m->action)) continue;
|
||||
|
||||
if(!inputPressed(m->action)) continue;
|
||||
UI_FOCUS.direction = m->direction;
|
||||
UI_FOCUS.timeHeld = 0.0f;
|
||||
uiFocusMoveDirection(item, m->direction);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle held directions
|
||||
bool_t held = false;
|
||||
for(
|
||||
const uifocusdirmap_t *m = UI_FOCUS_DIR_MAP;
|
||||
@@ -157,26 +161,18 @@ void uiFocusUpdate(void) {
|
||||
m++
|
||||
) {
|
||||
if(m->direction != UI_FOCUS.direction) continue;
|
||||
|
||||
held = inputDownIfDynamic(m->action);
|
||||
held = inputIsDown(m->action);
|
||||
break;
|
||||
}
|
||||
|
||||
// Are we still holding?
|
||||
if(!held) {
|
||||
UI_FOCUS.direction = UI_FOCUS_DIRECTION_NONE;
|
||||
UI_FOCUS.timeHeld = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
UI_FOCUS.timeHeld += TIME.delta;
|
||||
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
UI_FOCUS.timeHeld += TIME.dynamicDelta;
|
||||
#else
|
||||
UI_FOCUS.timeHeld += TIME.delta;
|
||||
#endif
|
||||
|
||||
// Can tick?
|
||||
if(UI_FOCUS.timeHeld < UI_FOCUS_HOLD_DELAY) return;
|
||||
if(UI_FOCUS.timeHeld < UI_FOCUS_HOLD_DELAY + UI_FOCUS_HOLD_REPEAT) return;
|
||||
UI_FOCUS.timeHeld = UI_FOCUS_HOLD_DELAY;
|
||||
|
||||
@@ -53,6 +53,7 @@ typedef struct {
|
||||
uint8_t count;
|
||||
uifocusdirection_t direction;
|
||||
float_t timeHeld;
|
||||
bool_t pushedThisTick;
|
||||
} uifocus_t;
|
||||
|
||||
extern uifocus_t UI_FOCUS;
|
||||
|
||||
+18
-12
@@ -22,33 +22,39 @@ errorret_t uiFrameInit(void) {
|
||||
color_t border = color4b(0, 100, 220, 255);
|
||||
color_t center = color4b(0, 100, 220, 200);
|
||||
|
||||
for(uint8_t y = 0; y < UIFRAME_BORDER_HEIGHT; y++) {
|
||||
for(uint8_t x = 0; x < UIFRAME_BORDER_WIDTH; x++) {
|
||||
for(uint8_t y = 0; y < UI_FRAME_TEXTURE_HEIGHT_POW2; y++) {
|
||||
for(uint8_t x = 0; x < UI_FRAME_TEXTURE_WIDTH_POW2; x++) {
|
||||
color_t c;
|
||||
if(x >= 12 || y >= 12) {// Because power of two is required.
|
||||
if(x >= UI_FRAME_TEXTURE_WIDTH || y >= UI_FRAME_TEXTURE_HEIGHT) {
|
||||
c = COLOR_TRANSPARENT;
|
||||
} else if(y < 4 || y >= 8 || x < 4 || x >= 8) {
|
||||
} else if(
|
||||
y < UI_FRAME_TILE_HEIGHT || y >= UI_FRAME_TILE_HEIGHT * 2 ||
|
||||
x < UI_FRAME_TILE_WIDTH || x >= UI_FRAME_TILE_WIDTH * 2
|
||||
) {
|
||||
c = border;
|
||||
} else {
|
||||
c = center;
|
||||
}
|
||||
UI_FRAME.pixels[y * UIFRAME_BORDER_WIDTH + x] = c;
|
||||
UI_FRAME.pixels[y * UI_FRAME_TEXTURE_WIDTH_POW2 + x] = c;
|
||||
}
|
||||
}
|
||||
|
||||
errorChain(textureInit(
|
||||
&UI_FRAME.texture, UIFRAME_BORDER_WIDTH, UIFRAME_BORDER_HEIGHT,
|
||||
&UI_FRAME.texture,
|
||||
UI_FRAME_TEXTURE_WIDTH_POW2, UI_FRAME_TEXTURE_HEIGHT_POW2,
|
||||
TEXTURE_FORMAT_RGBA,
|
||||
(texturedata_t){ .rgbaColors = UI_FRAME.pixels }
|
||||
));
|
||||
|
||||
UI_FRAME.tileset.tileWidth = 4;
|
||||
UI_FRAME.tileset.tileHeight = 4;
|
||||
UI_FRAME.tileset.tileWidth = UI_FRAME_TILE_WIDTH;
|
||||
UI_FRAME.tileset.tileHeight = UI_FRAME_TILE_HEIGHT;
|
||||
UI_FRAME.tileset.columns = 3;
|
||||
UI_FRAME.tileset.rows = 3;
|
||||
UI_FRAME.tileset.tileCount = 9;
|
||||
UI_FRAME.tileset.uv[0] = 4.0f / UIFRAME_BORDER_WIDTH;
|
||||
UI_FRAME.tileset.uv[1] = 4.0f / UIFRAME_BORDER_HEIGHT;
|
||||
UI_FRAME.tileset.uv[0] =
|
||||
(float_t)UI_FRAME_TILE_WIDTH / UI_FRAME_TEXTURE_WIDTH_POW2;
|
||||
UI_FRAME.tileset.uv[1] =
|
||||
(float_t)UI_FRAME_TILE_HEIGHT / UI_FRAME_TEXTURE_HEIGHT_POW2;
|
||||
|
||||
errorOk();
|
||||
}
|
||||
@@ -66,8 +72,8 @@ errorret_t uiFrameDraw(
|
||||
}
|
||||
};
|
||||
spritebatchsprite_t sprites[9];
|
||||
float_t tileW = (float_t)UI_FRAME.tileset.tileWidth;
|
||||
float_t tileH = (float_t)UI_FRAME.tileset.tileHeight;
|
||||
float_t tileW = (float_t)UI_FRAME_BORDER_WIDTH;
|
||||
float_t tileH = (float_t)UI_FRAME_BORDER_HEIGHT;
|
||||
|
||||
sprites[0] = spriteBatchSpriteTilesetPosition(
|
||||
&UI_FRAME.tileset, 0, 0,
|
||||
|
||||
@@ -10,13 +10,19 @@
|
||||
#include "display/texture/texture.h"
|
||||
#include "display/texture/tileset.h"
|
||||
|
||||
#define UIFRAME_BORDER_WIDTH 16
|
||||
#define UIFRAME_BORDER_HEIGHT 16
|
||||
#define UI_FRAME_BORDER_WIDTH 6
|
||||
#define UI_FRAME_BORDER_HEIGHT 6
|
||||
#define UI_FRAME_TILE_WIDTH 1
|
||||
#define UI_FRAME_TILE_HEIGHT 1
|
||||
#define UI_FRAME_TEXTURE_WIDTH (UI_FRAME_TILE_WIDTH * 3)
|
||||
#define UI_FRAME_TEXTURE_HEIGHT (UI_FRAME_TILE_HEIGHT * 3)
|
||||
#define UI_FRAME_TEXTURE_WIDTH_POW2 4
|
||||
#define UI_FRAME_TEXTURE_HEIGHT_POW2 4
|
||||
|
||||
typedef struct {
|
||||
tileset_t tileset;
|
||||
texture_t texture;
|
||||
color_t pixels[UIFRAME_BORDER_WIDTH * UIFRAME_BORDER_HEIGHT];
|
||||
color_t pixels[UI_FRAME_BORDER_WIDTH * UI_FRAME_BORDER_HEIGHT];
|
||||
} uiframe_t;
|
||||
|
||||
extern uiframe_t UI_FRAME;
|
||||
|
||||
@@ -79,10 +79,10 @@ errorret_t uiSettingsDraw(void) {
|
||||
errorChain(uiFrameDraw(0.0f, 0.0f, 300.0f, 300));
|
||||
errorChain(uiMenuDraw(
|
||||
&UI_SETTINGS.menu,
|
||||
UIFRAME_BORDER_WIDTH,
|
||||
UIFRAME_BORDER_HEIGHT,
|
||||
300.0f - (UIFRAME_BORDER_WIDTH * 2),
|
||||
300.0f - (UIFRAME_BORDER_HEIGHT * 2)
|
||||
UI_FRAME_BORDER_WIDTH,
|
||||
UI_FRAME_BORDER_HEIGHT,
|
||||
300.0f - (UI_FRAME_BORDER_WIDTH * 2),
|
||||
300.0f - (UI_FRAME_BORDER_HEIGHT * 2)
|
||||
));
|
||||
|
||||
errorChain(spriteBatchFlush());
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# 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
|
||||
)
|
||||
@@ -0,0 +1,242 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "uitextbox.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/string.h"
|
||||
#include "time/time.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"
|
||||
|
||||
void uiTextboxInit(uitextbox_t *box) {
|
||||
assertNotNull(box, "Textbox cannot be NULL");
|
||||
memoryZero(box, sizeof(uitextbox_t));
|
||||
}
|
||||
|
||||
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);
|
||||
box->currentPage = 0;
|
||||
box->scroll = 0;
|
||||
box->layoutWidth = 0.0f;
|
||||
box->layoutHeight = 0.0f;
|
||||
}
|
||||
|
||||
void uiTextboxBuildLayout(
|
||||
uitextbox_t *box,
|
||||
const float_t width,
|
||||
const float_t height
|
||||
) {
|
||||
assertNotNull(box, "Textbox cannot be NULL");
|
||||
|
||||
box->layoutWidth = width;
|
||||
box->layoutHeight = height;
|
||||
box->lineCount = 0;
|
||||
box->pageCount = 1;
|
||||
|
||||
float_t fontW = (float_t)FONT_DEFAULT.tileset->tileWidth;
|
||||
float_t fontH = (float_t)FONT_DEFAULT.tileset->tileHeight;
|
||||
|
||||
if(fontW <= 0.0f || fontH <= 0.0f) return;
|
||||
|
||||
box->charsPerLine = (int32_t)(width / fontW);
|
||||
box->linesPerPage = (int32_t)(height / (fontH + UI_TEXTBOX_LINE_SPACING));
|
||||
if(box->linesPerPage > UI_TEXTBOX_LINES_PER_PAGE_MAX) {
|
||||
box->linesPerPage = UI_TEXTBOX_LINES_PER_PAGE_MAX;
|
||||
}
|
||||
|
||||
if(box->charsPerLine <= 0 || box->linesPerPage <= 0) return;
|
||||
if(box->text[0] == '\0') return;
|
||||
|
||||
char_t *src = box->text;
|
||||
int32_t i = 0;
|
||||
|
||||
while(src[i] != '\0' && box->lineCount < UI_TEXTBOX_LINES_MAX) {
|
||||
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) {
|
||||
box->lines[box->lineCount].start = i;
|
||||
box->lines[box->lineCount].count = 0;
|
||||
box->lineCount++;
|
||||
pad--;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
int32_t lineStart = i;
|
||||
int32_t lineWidth = 0;
|
||||
|
||||
while(src[i] != '\0') {
|
||||
char_t c = src[i];
|
||||
|
||||
if(c == '\n') { i++; break; }
|
||||
if(c == '\t') break;
|
||||
|
||||
if(c == ' ') {
|
||||
int32_t wordLen = 0;
|
||||
int32_t j = i + 1;
|
||||
while(
|
||||
src[j] != ' ' && src[j] != '\n' &&
|
||||
src[j] != '\t' && src[j] != '\0'
|
||||
) {
|
||||
wordLen++;
|
||||
j++;
|
||||
}
|
||||
|
||||
if(lineWidth > 0 && lineWidth + 1 + wordLen > box->charsPerLine) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
|
||||
lineWidth++;
|
||||
i++;
|
||||
} else {
|
||||
if(lineWidth >= box->charsPerLine) break;
|
||||
lineWidth++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
box->lines[box->lineCount].start = lineStart;
|
||||
box->lines[box->lineCount].count = lineWidth;
|
||||
box->lineCount++;
|
||||
}
|
||||
|
||||
if(box->lineCount == 0) {
|
||||
box->pageCount = 1;
|
||||
} else {
|
||||
box->pageCount =
|
||||
(box->lineCount + box->linesPerPage - 1) / box->linesPerPage;
|
||||
}
|
||||
}
|
||||
|
||||
errorret_t uiTextboxUpdate(uitextbox_t *box) {
|
||||
assertNotNull(box, "Textbox cannot be NULL");
|
||||
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
if(TIME.dynamicUpdate) errorOk();
|
||||
#endif
|
||||
|
||||
if(!uiTextboxPageIsComplete(box)) {
|
||||
box->scroll += UI_TEXTBOX_SCROLL_CHARS_PER_TICK;
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t uiTextboxDraw(
|
||||
uitextbox_t *box,
|
||||
const float_t x,
|
||||
const float_t y,
|
||||
const float_t width,
|
||||
const float_t height
|
||||
) {
|
||||
assertNotNull(box, "Textbox cannot be NULL");
|
||||
|
||||
float_t borderW = (float_t)UI_FRAME_BORDER_WIDTH;
|
||||
float_t borderH = (float_t)UI_FRAME_BORDER_HEIGHT;
|
||||
float_t contentX = x + borderW;
|
||||
float_t contentY = y + borderH;
|
||||
float_t contentW = width - 2.0f * borderW;
|
||||
float_t contentH = height - 2.0f * borderH;
|
||||
|
||||
if(contentW != box->layoutWidth || contentH != box->layoutHeight) {
|
||||
uiTextboxBuildLayout(box, contentW, contentH);
|
||||
}
|
||||
|
||||
errorChain(uiFrameDraw(x, y, width, height));
|
||||
errorChain(spriteBatchFlush());
|
||||
|
||||
if(box->lineCount == 0 || box->text[0] == '\0') errorOk();
|
||||
|
||||
float_t fontW = (float_t)FONT_DEFAULT.tileset->tileWidth;
|
||||
float_t fontH = (float_t)FONT_DEFAULT.tileset->tileHeight;
|
||||
|
||||
shadermaterial_t material = {
|
||||
.unlit = {
|
||||
.color = COLOR_WHITE,
|
||||
.texture = FONT_DEFAULT.texture
|
||||
}
|
||||
};
|
||||
|
||||
int32_t pageFirst = box->currentPage * box->linesPerPage;
|
||||
int32_t pageLast = pageFirst + box->linesPerPage;
|
||||
if(pageLast > box->lineCount) pageLast = box->lineCount;
|
||||
|
||||
int32_t charsLeft = box->scroll;
|
||||
|
||||
for(int32_t li = pageFirst; li < pageLast && charsLeft > 0; li++) {
|
||||
uitextboxline_t *line = &box->lines[li];
|
||||
int32_t visible = line->count < charsLeft ? line->count : charsLeft;
|
||||
float_t lineY = contentY +
|
||||
(float_t)(li - pageFirst) * (fontH + UI_TEXTBOX_LINE_SPACING);
|
||||
|
||||
for(int32_t ci = 0; ci < visible; ci++) {
|
||||
char_t c = box->text[line->start + ci];
|
||||
if(c == ' ') continue;
|
||||
spritebatchsprite_t sprite = textGetSprite(
|
||||
(vec2){ contentX + (float_t)ci * fontW, lineY },
|
||||
c,
|
||||
&FONT_DEFAULT
|
||||
);
|
||||
errorChain(spriteBatchBuffer(&sprite, 1, &SHADER_UNLIT, material));
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
int32_t uiTextboxGetPageCharCount(const uitextbox_t *box) {
|
||||
assertNotNull(box, "Textbox cannot be NULL");
|
||||
int32_t first = box->currentPage * box->linesPerPage;
|
||||
int32_t last = first + box->linesPerPage;
|
||||
if(last > box->lineCount) last = box->lineCount;
|
||||
int32_t total = 0;
|
||||
for(int32_t i = first; i < last; i++) {
|
||||
total += box->lines[i].count;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
bool_t uiTextboxPageIsComplete(const uitextbox_t *box) {
|
||||
assertNotNull(box, "Textbox cannot be NULL");
|
||||
return box->scroll >= uiTextboxGetPageCharCount(box);
|
||||
}
|
||||
|
||||
bool_t uiTextboxHasNextPage(const uitextbox_t *box) {
|
||||
assertNotNull(box, "Textbox cannot be NULL");
|
||||
return box->currentPage + 1 < box->pageCount;
|
||||
}
|
||||
|
||||
void uiTextboxNextPage(uitextbox_t *box) {
|
||||
assertNotNull(box, "Textbox cannot be NULL");
|
||||
if(!uiTextboxHasNextPage(box)) return;
|
||||
box->currentPage++;
|
||||
box->scroll = 0;
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#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
|
||||
|
||||
typedef struct {
|
||||
int32_t start;
|
||||
int32_t count;
|
||||
} uitextboxline_t;
|
||||
|
||||
typedef struct {
|
||||
char_t text[UI_TEXTBOX_TEXT_MAX];
|
||||
|
||||
uitextboxline_t lines[UI_TEXTBOX_LINES_MAX];
|
||||
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;
|
||||
} uitextbox_t;
|
||||
|
||||
/**
|
||||
* Initializes a textbox, zeroing all state.
|
||||
*
|
||||
* @param box The textbox to initialize.
|
||||
*/
|
||||
void uiTextboxInit(uitextbox_t *box);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* 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
|
||||
);
|
||||
}
|
||||
|
||||
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_BORDER_HEIGHT;
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "ui/rpg/uitextbox.h"
|
||||
#include "ui/focus/uifocusitem.h"
|
||||
|
||||
#define UI_TEXTBOX_MAIN_LINES 4
|
||||
|
||||
extern uitextbox_t UI_TEXTBOX_MAIN;
|
||||
|
||||
/**
|
||||
* Initializes UI_TEXTBOX_MAIN.
|
||||
*
|
||||
* @returns Any error that occurs.
|
||||
*/
|
||||
errorret_t uiTextboxMainInit(void);
|
||||
|
||||
/**
|
||||
* Copies text into UI_TEXTBOX_MAIN and resets page and scroll.
|
||||
*
|
||||
* @param text Null-terminated source string.
|
||||
*/
|
||||
void uiTextboxMainSetText(const char_t *text);
|
||||
|
||||
/**
|
||||
* Advances the typewriter scroll for UI_TEXTBOX_MAIN.
|
||||
*
|
||||
* @returns Any error that occurs.
|
||||
*/
|
||||
errorret_t uiTextboxMainUpdate(void);
|
||||
|
||||
/**
|
||||
* Draws UI_TEXTBOX_MAIN full-width at the bottom of the screen.
|
||||
* Position and size are derived from SCREEN each call.
|
||||
*
|
||||
* @returns Any error that occurs.
|
||||
*/
|
||||
errorret_t uiTextboxMainDraw(void);
|
||||
|
||||
/**
|
||||
* Returns true when the current page is fully scrolled in.
|
||||
*
|
||||
* @returns True if the current page is complete.
|
||||
*/
|
||||
bool_t uiTextboxMainPageIsComplete(void);
|
||||
|
||||
/**
|
||||
* Returns true when at least one more page follows the current one.
|
||||
*
|
||||
* @returns True if a next page exists.
|
||||
*/
|
||||
bool_t uiTextboxMainHasNextPage(void);
|
||||
|
||||
/**
|
||||
* Advances UI_TEXTBOX_MAIN to the next page and resets scroll.
|
||||
* Has no effect if already on the last page.
|
||||
*/
|
||||
void uiTextboxMainNextPage(void);
|
||||
|
||||
/**
|
||||
* Returns true when UI_TEXTBOX_MAIN has focus (is visible and active).
|
||||
*
|
||||
* @returns True if the textbox is currently active.
|
||||
*/
|
||||
bool_t uiTextboxMainIsActive(void);
|
||||
|
||||
/**
|
||||
* Internal focus callback - skip scroll or advance page or dismiss.
|
||||
*
|
||||
* @param item The active focus item.
|
||||
* @returns True.
|
||||
*/
|
||||
bool_t uiTextboxMainFocusSelected(const uifocusitem_t *item);
|
||||
|
||||
/**
|
||||
* Internal focus callback - clears the focus item pointer on dismiss.
|
||||
*
|
||||
* @param item The focus item being closed.
|
||||
* @returns True.
|
||||
*/
|
||||
bool_t uiTextboxMainFocusClosed(const uifocusitem_t *item);
|
||||
+6
-10
@@ -10,14 +10,13 @@
|
||||
#include "ui/frame/uiframe.h"
|
||||
#include "ui/debug/uifps.h"
|
||||
#include "engine/engine.h"
|
||||
#include "ui/uitextbox.h"
|
||||
#include "ui/overlay/uifullbox.h"
|
||||
#include "ui/overlay/uiloading.h"
|
||||
#include "ui/debug/uiplayerpos.h"
|
||||
#include "ui/overlay/uicrop.h"
|
||||
#include "ui/debug/uiconsole.h"
|
||||
#include "ui/frame/uiframe.h"
|
||||
#include "ui/frame/uisettings.h"
|
||||
#include "ui/rpg/uitextboxmain.h"
|
||||
|
||||
uielement_t UI_ELEMENTS[] = {
|
||||
{
|
||||
@@ -39,14 +38,11 @@ uielement_t UI_ELEMENTS[] = {
|
||||
.dispose = uiSettingsDispose
|
||||
},
|
||||
|
||||
// { .type = UI_ELEMENT_TYPE_SCRIPT, .script = { .script = "ui/test.js" } },
|
||||
|
||||
// {
|
||||
// .init = uiTextboxInit,
|
||||
// .update = uiTextboxUpdate,
|
||||
// .draw = uiTextboxDraw,
|
||||
// .dispose = uiTextboxDispose
|
||||
// },
|
||||
{
|
||||
.init = uiTextboxMainInit,
|
||||
.update = uiTextboxMainUpdate,
|
||||
.draw = uiTextboxMainDraw
|
||||
},
|
||||
|
||||
// Fullbox over: above absolutely everything.
|
||||
{
|
||||
|
||||
@@ -1,278 +0,0 @@
|
||||
// // Copyright (c) 2026 Dominic Masters
|
||||
// //
|
||||
// // This software is released under the MIT License.
|
||||
// // https://opensource.org/licenses/MIT
|
||||
|
||||
// #include "uitextbox.h"
|
||||
// #include "assert/assert.h"
|
||||
// #include "util/memory.h"
|
||||
// #include "util/string.h"
|
||||
// #include "time/time.h"
|
||||
// #include "input/input.h"
|
||||
// #include "event/event.h"
|
||||
// #include "display/screen/screen.h"
|
||||
// #include "display/texture/texture.h"
|
||||
// #include "display/text/text.h"
|
||||
// #include "display/spritebatch/spritebatch.h"
|
||||
// #include "display/shader/shaderunlit.h"
|
||||
|
||||
// uitextbox_t UI_TEXTBOX;
|
||||
|
||||
// errorret_t uiTextboxInit(void) {
|
||||
// memoryZero(&UI_TEXTBOX, sizeof(uitextbox_t));
|
||||
// UI_TEXTBOX.textColor = COLOR_WHITE;
|
||||
// UI_TEXTBOX.advanceAction = INPUT_ACTION_ACCEPT;
|
||||
|
||||
// UI_TEXTBOX.font = &FONT_DEFAULT;
|
||||
|
||||
// float_t fontW = (float_t)FONT_DEFAULT.tileset->tileWidth;
|
||||
// float_t fontH = (float_t)FONT_DEFAULT.tileset->tileHeight;
|
||||
// float_t tbHeight = (
|
||||
// (float_t)UI_TEXTBOX_LINES_PER_PAGE_MAX * fontH +
|
||||
// (float_t)(UI_TEXTBOX_LINES_PER_PAGE_MAX - 1) * UI_TEXTBOX_LINE_SPACING +
|
||||
// 2.0f * fontH
|
||||
// );
|
||||
// UI_TEXTBOX.x = (float_t)SCREEN.scanX + 10.0f;
|
||||
// UI_TEXTBOX.y = (float_t)(SCREEN.scanY + SCREEN.scanHeight) -
|
||||
// tbHeight - 10.0f;
|
||||
// UI_TEXTBOX.width = (float_t)SCREEN.scanWidth - 20.0f;
|
||||
// UI_TEXTBOX.height = tbHeight;
|
||||
|
||||
// UI_TEXTBOX.frame.tileset.columns = 3;
|
||||
// UI_TEXTBOX.frame.tileset.rows = 3;
|
||||
// UI_TEXTBOX.frame.tileset.tileCount = 9;
|
||||
// UI_TEXTBOX.frame.tileset.tileWidth = FONT_DEFAULT.tileset->tileWidth;
|
||||
// UI_TEXTBOX.frame.tileset.tileHeight = FONT_DEFAULT.tileset->tileHeight;
|
||||
// UI_TEXTBOX.frame.tileset.uv[0] = 1.0f / 3.0f;
|
||||
// UI_TEXTBOX.frame.tileset.uv[1] = 1.0f / 3.0f;
|
||||
// UI_TEXTBOX.frame.texture = &TEXTURE_WHITE;
|
||||
|
||||
// eventInit(
|
||||
// &UI_TEXTBOX.onPageComplete,
|
||||
// UI_TEXTBOX.onPageCompleteCallbacks,
|
||||
// UI_TEXTBOX.onPageCompleteUsers,
|
||||
// 4
|
||||
// );
|
||||
// eventInit(
|
||||
// &UI_TEXTBOX.onLastPage,
|
||||
// UI_TEXTBOX.onLastPageCallbacks,
|
||||
// UI_TEXTBOX.onLastPageUsers,
|
||||
// 4
|
||||
// );
|
||||
|
||||
// errorOk();
|
||||
// }
|
||||
|
||||
// void uiTextboxBuildLayout(void) {
|
||||
// UI_TEXTBOX.lineCount = 0;
|
||||
// UI_TEXTBOX.pageCount = 1;
|
||||
|
||||
// float_t frameTileW = (float_t)UI_TEXTBOX.frame.tileset.tileWidth;
|
||||
// float_t frameTileH = (float_t)UI_TEXTBOX.frame.tileset.tileHeight;
|
||||
// float_t fontW = (float_t)UI_TEXTBOX.font->tileset->tileWidth;
|
||||
// float_t fontH = (float_t)UI_TEXTBOX.font->tileset->tileHeight;
|
||||
|
||||
// if(fontW <= 0.0f || fontH <= 0.0f) return;
|
||||
|
||||
// float_t contentW = UI_TEXTBOX.width - 2.0f * frameTileW;
|
||||
// float_t contentH = UI_TEXTBOX.height - 2.0f * frameTileH;
|
||||
|
||||
// UI_TEXTBOX.charsPerLine = (int32_t)(contentW / fontW);
|
||||
// UI_TEXTBOX.linesPerPage = UI_TEXTBOX_LINES_PER_PAGE_MAX;
|
||||
|
||||
// if(UI_TEXTBOX.charsPerLine <= 0 || UI_TEXTBOX.linesPerPage <= 0) return;
|
||||
// if(UI_TEXTBOX.text[0] == '\0') return;
|
||||
|
||||
// char_t *src = UI_TEXTBOX.text;
|
||||
// int32_t i = 0;
|
||||
|
||||
// while(src[i] != '\0' && UI_TEXTBOX.lineCount < UI_TEXTBOX_LINES_MAX) {
|
||||
// if(src[i] == '\t') {
|
||||
// i++;
|
||||
// int32_t rem = UI_TEXTBOX.lineCount % UI_TEXTBOX.linesPerPage;
|
||||
// int32_t pad = rem > 0 ? UI_TEXTBOX.linesPerPage - rem : 0;
|
||||
// while(pad > 0 && UI_TEXTBOX.lineCount < UI_TEXTBOX_LINES_MAX) {
|
||||
// UI_TEXTBOX.lines[UI_TEXTBOX.lineCount].start = i;
|
||||
// UI_TEXTBOX.lines[UI_TEXTBOX.lineCount].count = 0;
|
||||
// UI_TEXTBOX.lineCount++;
|
||||
// pad--;
|
||||
// }
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// int32_t lineStart = i;
|
||||
// int32_t lineWidth = 0;
|
||||
|
||||
// while(src[i] != '\0') {
|
||||
// char_t c = src[i];
|
||||
|
||||
// if(c == '\n') {
|
||||
// i++;
|
||||
// break;
|
||||
// }
|
||||
|
||||
// if(c == '\t') break;
|
||||
|
||||
// if(c == ' ') {
|
||||
// int32_t wordLen = 0;
|
||||
// int32_t j = i + 1;
|
||||
// while(
|
||||
// src[j] != ' ' && src[j] != '\n' &&
|
||||
// src[j] != '\t' && src[j] != '\0'
|
||||
// ) {
|
||||
// wordLen++;
|
||||
// j++;
|
||||
// }
|
||||
|
||||
// if(
|
||||
// lineWidth > 0 &&
|
||||
// lineWidth + 1 + wordLen > UI_TEXTBOX.charsPerLine
|
||||
// ) {
|
||||
// i++;
|
||||
// break;
|
||||
// }
|
||||
|
||||
// lineWidth++;
|
||||
// i++;
|
||||
// } else {
|
||||
// if(lineWidth >= UI_TEXTBOX.charsPerLine) break;
|
||||
// lineWidth++;
|
||||
// i++;
|
||||
// }
|
||||
// }
|
||||
|
||||
// UI_TEXTBOX.lines[UI_TEXTBOX.lineCount].start = lineStart;
|
||||
// UI_TEXTBOX.lines[UI_TEXTBOX.lineCount].count = lineWidth;
|
||||
// UI_TEXTBOX.lineCount++;
|
||||
// }
|
||||
|
||||
// if(UI_TEXTBOX.lineCount == 0) {
|
||||
// UI_TEXTBOX.pageCount = 1;
|
||||
// } else {
|
||||
// int32_t div = UI_TEXTBOX.lineCount + UI_TEXTBOX.linesPerPage - 1;
|
||||
// UI_TEXTBOX.pageCount = div / UI_TEXTBOX.linesPerPage;
|
||||
// }
|
||||
// }
|
||||
|
||||
// void uiTextboxSetText(const char_t *text) {
|
||||
// assertNotNull(text, "text must not be NULL");
|
||||
|
||||
// stringCopy(UI_TEXTBOX.text, text, UI_TEXTBOX_TEXT_MAX);
|
||||
// UI_TEXTBOX.currentPage = 0;
|
||||
// UI_TEXTBOX.scroll = 0;
|
||||
// uiTextboxBuildLayout();
|
||||
// }
|
||||
|
||||
// errorret_t uiTextboxUpdate(void) {
|
||||
// #ifdef DUSK_TIME_DYNAMIC
|
||||
// if(TIME.dynamicUpdate) errorOk();
|
||||
// #endif
|
||||
|
||||
// bool_t wasComplete = uiTextboxPageIsComplete();
|
||||
|
||||
// if(!wasComplete) {
|
||||
// UI_TEXTBOX.scroll += UI_TEXTBOX_SCROLL_CHARS_PER_TICK;
|
||||
// }
|
||||
|
||||
// if(inputPressed(UI_TEXTBOX.advanceAction)) {
|
||||
// if(!uiTextboxPageIsComplete()) {
|
||||
// UI_TEXTBOX.scroll = uiTextboxGetPageCharCount();
|
||||
// } else if(uiTextboxHasNextPage()) {
|
||||
// uiTextboxNextPage();
|
||||
// }
|
||||
// }
|
||||
|
||||
// if(!wasComplete && uiTextboxPageIsComplete()) {
|
||||
// eventInvoke(&UI_TEXTBOX.onPageComplete, &UI_TEXTBOX);
|
||||
// if(!uiTextboxHasNextPage()) {
|
||||
// eventInvoke(&UI_TEXTBOX.onLastPage, &UI_TEXTBOX);
|
||||
// }
|
||||
// }
|
||||
|
||||
// errorOk();
|
||||
// }
|
||||
|
||||
// int32_t uiTextboxGetPageCharCount(void) {
|
||||
// int32_t first = UI_TEXTBOX.currentPage * UI_TEXTBOX.linesPerPage;
|
||||
// int32_t last = first + UI_TEXTBOX.linesPerPage;
|
||||
// if(last > UI_TEXTBOX.lineCount) last = UI_TEXTBOX.lineCount;
|
||||
// int32_t total = 0;
|
||||
// for(int32_t i = first; i < last; i++) {
|
||||
// total += UI_TEXTBOX.lines[i].count;
|
||||
// }
|
||||
// return total;
|
||||
// }
|
||||
|
||||
// bool_t uiTextboxPageIsComplete(void) {
|
||||
// return UI_TEXTBOX.scroll >= uiTextboxGetPageCharCount();
|
||||
// }
|
||||
|
||||
// bool_t uiTextboxHasNextPage(void) {
|
||||
// return UI_TEXTBOX.currentPage + 1 < UI_TEXTBOX.pageCount;
|
||||
// }
|
||||
|
||||
// void uiTextboxNextPage(void) {
|
||||
// if(!uiTextboxHasNextPage()) return;
|
||||
// UI_TEXTBOX.currentPage++;
|
||||
// UI_TEXTBOX.scroll = 0;
|
||||
// }
|
||||
|
||||
// errorret_t uiTextboxDraw(void) {
|
||||
// if(UI_TEXTBOX.lineCount == 0 || UI_TEXTBOX.text[0] == '\0') errorOk();
|
||||
|
||||
// errorChain(uiFrameDraw(
|
||||
// &UI_TEXTBOX.frame,
|
||||
// UI_TEXTBOX.x, UI_TEXTBOX.y,
|
||||
// UI_TEXTBOX.width, UI_TEXTBOX.height
|
||||
// ));
|
||||
// errorChain(spriteBatchFlush());
|
||||
|
||||
// shadermaterial_t textMaterial = {
|
||||
// .unlit = {
|
||||
// .color = UI_TEXTBOX.textColor,
|
||||
// .texture = UI_TEXTBOX.font->texture
|
||||
// }
|
||||
// };
|
||||
|
||||
// float_t frameTileW = (float_t)UI_TEXTBOX.frame.tileset.tileWidth;
|
||||
// float_t frameTileH = (float_t)UI_TEXTBOX.frame.tileset.tileHeight;
|
||||
// float_t fontW = (float_t)UI_TEXTBOX.font->tileset->tileWidth;
|
||||
// float_t fontH = (float_t)UI_TEXTBOX.font->tileset->tileHeight;
|
||||
// float_t contentX = UI_TEXTBOX.x + frameTileW;
|
||||
// float_t contentY = UI_TEXTBOX.y + frameTileH;
|
||||
|
||||
// int32_t pageFirst = UI_TEXTBOX.currentPage * UI_TEXTBOX.linesPerPage;
|
||||
// int32_t pageLast = pageFirst + UI_TEXTBOX.linesPerPage;
|
||||
// if(pageLast > UI_TEXTBOX.lineCount) pageLast = UI_TEXTBOX.lineCount;
|
||||
|
||||
// int32_t charsLeft = UI_TEXTBOX.scroll;
|
||||
|
||||
// for(int32_t li = pageFirst; li < pageLast && charsLeft > 0; li++) {
|
||||
// uitextboxline_t *line = &UI_TEXTBOX.lines[li];
|
||||
// int32_t visible = line->count < charsLeft ? line->count : charsLeft;
|
||||
// float_t lineY = contentY +
|
||||
// (float_t)(li - pageFirst) * (fontH + UI_TEXTBOX_LINE_SPACING);
|
||||
|
||||
// for(int32_t ci = 0; ci < visible; ci++) {
|
||||
// char_t c = UI_TEXTBOX.text[line->start + ci];
|
||||
// if(c == ' ') continue;
|
||||
// spritebatchsprite_t sprite = textGetSprite(
|
||||
// (vec2){ contentX + (float_t)ci * fontW, lineY },
|
||||
// c,
|
||||
// UI_TEXTBOX.font
|
||||
// );
|
||||
// errorChain(spriteBatchBuffer(&sprite, 1, &SHADER_UNLIT, textMaterial));
|
||||
// }
|
||||
|
||||
// charsLeft -= visible;
|
||||
// }
|
||||
|
||||
// errorChain(spriteBatchFlush());
|
||||
// errorOk();
|
||||
// }
|
||||
|
||||
// errorret_t uiTextboxDispose(void) {
|
||||
// uiFrameDispose(&UI_TEXTBOX.frame);
|
||||
// UI_TEXTBOX.font = NULL;
|
||||
// errorOk();
|
||||
// }
|
||||
@@ -1,121 +0,0 @@
|
||||
// /**
|
||||
// * Copyright (c) 2026 Dominic Masters
|
||||
// *
|
||||
// * This software is released under the MIT License.
|
||||
// * https://opensource.org/licenses/MIT
|
||||
// */
|
||||
|
||||
// #pragma once
|
||||
// #include "ui/frame/uiframe.h"
|
||||
// #include "display/text/text.h"
|
||||
// #include "input/inputaction.h"
|
||||
// #include "event/event.h"
|
||||
|
||||
// #define UI_TEXTBOX_TEXT_MAX 1024
|
||||
// #define UI_TEXTBOX_LINES_MAX 64
|
||||
// #define UI_TEXTBOX_SCROLL_CHARS_PER_TICK 1
|
||||
// #define UI_TEXTBOX_LINES_PER_PAGE_MAX 3
|
||||
// #define UI_TEXTBOX_LINE_SPACING 0.0f
|
||||
|
||||
// typedef struct {
|
||||
// int32_t start;
|
||||
// int32_t count;
|
||||
// } uitextboxline_t;
|
||||
|
||||
// typedef struct {
|
||||
// uiframe_t frame;
|
||||
|
||||
// font_t *font;
|
||||
// color_t textColor;
|
||||
|
||||
// float_t x, y, width, height;
|
||||
|
||||
// char_t text[UI_TEXTBOX_TEXT_MAX];
|
||||
|
||||
// uitextboxline_t lines[UI_TEXTBOX_LINES_MAX];
|
||||
// int32_t lineCount;
|
||||
// int32_t charsPerLine;
|
||||
// int32_t linesPerPage;
|
||||
// int32_t pageCount;
|
||||
|
||||
// int32_t currentPage;
|
||||
// int32_t scroll;
|
||||
// inputaction_t advanceAction;
|
||||
|
||||
// eventcallback_t onPageCompleteCallbacks[4];
|
||||
// void *onPageCompleteUsers[4];
|
||||
// event_t onPageComplete;
|
||||
// eventcallback_t onLastPageCallbacks[4];
|
||||
// void *onLastPageUsers[4];
|
||||
// event_t onLastPage;
|
||||
// } uitextbox_t;
|
||||
|
||||
// extern uitextbox_t UI_TEXTBOX;
|
||||
|
||||
// /**
|
||||
// * Initializes UI_TEXTBOX. Sets position and size from current screen
|
||||
// * dimensions and wires up the default font and frame textures.
|
||||
// * Call after displayInit().
|
||||
// *
|
||||
// * @return Any error that occurs.
|
||||
// */
|
||||
// errorret_t uiTextboxInit(void);
|
||||
|
||||
// /**
|
||||
// * Rebuilds the word-wrap and page layout from the current text and dimensions.
|
||||
// * Called automatically by uiTextboxSetText.
|
||||
// */
|
||||
// void uiTextboxBuildLayout(void);
|
||||
|
||||
// /**
|
||||
// * Copies text into UI_TEXTBOX and rebuilds the word-wrap / page layout.
|
||||
// * Resets currentPage and scroll to 0.
|
||||
// *
|
||||
// * @param text Null-terminated source string.
|
||||
// */
|
||||
// void uiTextboxSetText(const char_t *text);
|
||||
|
||||
// /**
|
||||
// * Advances the typewriter scroll by UI_TEXTBOX_SCROLL_CHARS_PER_TICK.
|
||||
// * Skipped on dynamic ticks when DUSK_TIME_DYNAMIC is defined.
|
||||
// *
|
||||
// * @return Any error that occurs.
|
||||
// */
|
||||
// errorret_t uiTextboxUpdate(void);
|
||||
|
||||
// /**
|
||||
// * Draws the frame and the currently visible text, including a final flush.
|
||||
// *
|
||||
// * @return Any error that occurs.
|
||||
// */
|
||||
// errorret_t uiTextboxDraw(void);
|
||||
|
||||
// /**
|
||||
// * Returns the total char count for the current page.
|
||||
// *
|
||||
// * @return Total chars on current page.
|
||||
// */
|
||||
// int32_t uiTextboxGetPageCharCount(void);
|
||||
|
||||
// /**
|
||||
// * Returns true when scroll has fully revealed the current page.
|
||||
// */
|
||||
// bool_t uiTextboxPageIsComplete(void);
|
||||
|
||||
// /**
|
||||
// * Returns true when there is at least one more page after the current one.
|
||||
// */
|
||||
// bool_t uiTextboxHasNextPage(void);
|
||||
|
||||
// /**
|
||||
// * Advances to the next page and resets scroll to 0.
|
||||
// * Has no effect if already on the last page.
|
||||
// */
|
||||
// void uiTextboxNextPage(void);
|
||||
|
||||
// /**
|
||||
// * Disposes of UI_TEXTBOX, nulling out texture pointers.
|
||||
// *
|
||||
// * @return Any error that occurs.
|
||||
// */
|
||||
// errorret_t uiTextboxDispose(void);
|
||||
Reference in New Issue
Block a user