UI Frame
This commit is contained in:
@@ -4,16 +4,14 @@
|
|||||||
# https://opensource.org/licenses/MIT
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
add_subdirectory(debug)
|
add_subdirectory(debug)
|
||||||
add_subdirectory(uifocus)
|
add_subdirectory(frame)
|
||||||
|
add_subdirectory(focus)
|
||||||
|
add_subdirectory(overlay)
|
||||||
|
|
||||||
# Sources
|
# Sources
|
||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
PUBLIC
|
PUBLIC
|
||||||
ui.c
|
ui.c
|
||||||
uicrop.c
|
|
||||||
uielement.c
|
uielement.c
|
||||||
uiframe.c
|
# uitextbox.c
|
||||||
uifullbox.c
|
|
||||||
uiloading.c
|
|
||||||
uitextbox.c
|
|
||||||
)
|
)
|
||||||
@@ -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
|
||||||
|
uiframe.c
|
||||||
|
uisettings.c
|
||||||
|
)
|
||||||
@@ -1,43 +1,76 @@
|
|||||||
// Copyright (c) 2026 Dominic Masters
|
/**
|
||||||
//
|
* Copyright (c) 2026 Dominic Masters
|
||||||
// This software is released under the MIT License.
|
*
|
||||||
// https://opensource.org/licenses/MIT
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
#include "uiframe.h"
|
#include "uiframe.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
|
#include "display/texture/texture.h"
|
||||||
#include "display/spritebatch/spritebatch.h"
|
#include "display/spritebatch/spritebatch.h"
|
||||||
#include "display/shader/shaderunlit.h"
|
#include "display/shader/shaderunlit.h"
|
||||||
#include "display/color.h"
|
#include "display/color.h"
|
||||||
|
|
||||||
errorret_t uiFrameInit(uiframe_t *frame) {
|
uiframe_t UI_FRAME;
|
||||||
assertNotNull(frame, "frame must not be NULL");
|
|
||||||
memoryZero(frame, sizeof(uiframe_t));
|
errorret_t uiFrameInit(void) {
|
||||||
|
memoryZero(&UI_FRAME, sizeof(uiframe_t));
|
||||||
|
|
||||||
|
// Init texture.
|
||||||
|
color_t border = color4b(0, 100, 220, 200);
|
||||||
|
color_t center = color4b(0, 100, 220, 50);
|
||||||
|
|
||||||
|
for(uint8_t y = 0; y < UIFRAME_DUMMY_HEIGHT; y++) {
|
||||||
|
for(uint8_t x = 0; x < UIFRAME_DUMMY_WIDTH; x++) {
|
||||||
|
color_t c;
|
||||||
|
if(x >= 12 || y >= 12) {
|
||||||
|
c = COLOR_TRANSPARENT;
|
||||||
|
} else if(y < 4 || y >= 8 || x < 4 || x >= 8) {
|
||||||
|
c = border;
|
||||||
|
} else {
|
||||||
|
c = center;
|
||||||
|
}
|
||||||
|
UI_FRAME.pixels[y * UIFRAME_DUMMY_WIDTH + x] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errorChain(textureInit(
|
||||||
|
&UI_FRAME.texture, UIFRAME_DUMMY_WIDTH, UIFRAME_DUMMY_HEIGHT,
|
||||||
|
TEXTURE_FORMAT_RGBA,
|
||||||
|
(texturedata_t){ .rgbaColors = UI_FRAME.pixels }
|
||||||
|
));
|
||||||
|
|
||||||
|
UI_FRAME.tileset.tileWidth = 4;
|
||||||
|
UI_FRAME.tileset.tileHeight = 4;
|
||||||
|
UI_FRAME.tileset.columns = 3;
|
||||||
|
UI_FRAME.tileset.rows = 3;
|
||||||
|
UI_FRAME.tileset.tileCount = 9;
|
||||||
|
UI_FRAME.tileset.uv[0] = 4.0f / UIFRAME_DUMMY_WIDTH;
|
||||||
|
UI_FRAME.tileset.uv[1] = 4.0f / UIFRAME_DUMMY_HEIGHT;
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t uiFrameDraw(
|
errorret_t uiFrameDraw(
|
||||||
const uiframe_t *frame,
|
|
||||||
const float_t x,
|
const float_t x,
|
||||||
const float_t y,
|
const float_t y,
|
||||||
const float_t width,
|
const float_t width,
|
||||||
const float_t height
|
const float_t height
|
||||||
) {
|
) {
|
||||||
assertNotNull(frame, "frame must not be NULL");
|
|
||||||
assertNotNull(frame->texture, "frame texture must not be NULL");
|
|
||||||
|
|
||||||
shadermaterial_t material = {
|
shadermaterial_t material = {
|
||||||
.unlit = {
|
.unlit = {
|
||||||
.color = COLOR_WHITE,
|
.color = COLOR_WHITE,
|
||||||
.texture = frame->texture
|
.texture = &UI_FRAME.texture
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
spritebatchsprite_t sprites[9];
|
spritebatchsprite_t sprites[9];
|
||||||
float_t tileW = (float_t)frame->tileset.tileWidth;
|
float_t tileW = (float_t)UI_FRAME.tileset.tileWidth;
|
||||||
float_t tileH = (float_t)frame->tileset.tileHeight;
|
float_t tileH = (float_t)UI_FRAME.tileset.tileHeight;
|
||||||
vec4 uv;
|
vec4 uv;
|
||||||
|
|
||||||
tilesetPositionGetUV(&frame->tileset, 0, 0, uv);
|
tilesetPositionGetUV(&UI_FRAME.tileset, 0, 0, uv);
|
||||||
sprites[0].min[0] = x;
|
sprites[0].min[0] = x;
|
||||||
sprites[0].min[1] = y;
|
sprites[0].min[1] = y;
|
||||||
sprites[0].min[2] = 0.0f;
|
sprites[0].min[2] = 0.0f;
|
||||||
@@ -49,19 +82,19 @@ errorret_t uiFrameDraw(
|
|||||||
sprites[0].uvMax[0] = uv[2];
|
sprites[0].uvMax[0] = uv[2];
|
||||||
sprites[0].uvMax[1] = uv[3];
|
sprites[0].uvMax[1] = uv[3];
|
||||||
|
|
||||||
tilesetPositionGetUV(&frame->tileset, 1, 0, uv);
|
tilesetPositionGetUV(&UI_FRAME.tileset, 1, 0, uv);
|
||||||
sprites[1].min[0] = x + tileW;
|
sprites[1].min[0] = x + tileW;
|
||||||
sprites[1].min[1] = y;
|
sprites[1].min[1] = y;
|
||||||
sprites[1].min[2] = 0.0f;
|
sprites[1].min[2] = 0.0f;
|
||||||
sprites[1].max[0] = x + width - tileW;
|
sprites[1].max[0] = x + width - tileW;
|
||||||
sprites[1].max[1] = y + tileH;
|
sprites[1].max[1] = y + tileH;
|
||||||
sprites[1].max[2] = 0.0f;
|
sprites[1].max[2] = 0.0f;
|
||||||
sprites[1].uvMin[0] = uv[0];
|
sprites[1].uvMin[0] = uv[0];
|
||||||
sprites[1].uvMin[1] = uv[1];
|
sprites[1].uvMin[1] = uv[1];
|
||||||
sprites[1].uvMax[0] = uv[2];
|
sprites[1].uvMax[0] = uv[2];
|
||||||
sprites[1].uvMax[1] = uv[3];
|
sprites[1].uvMax[1] = uv[3];
|
||||||
|
|
||||||
tilesetPositionGetUV(&frame->tileset, 2, 0, uv);
|
tilesetPositionGetUV(&UI_FRAME.tileset, 2, 0, uv);
|
||||||
sprites[2].min[0] = x + width - tileW;
|
sprites[2].min[0] = x + width - tileW;
|
||||||
sprites[2].min[1] = y;
|
sprites[2].min[1] = y;
|
||||||
sprites[2].min[2] = 0.0f;
|
sprites[2].min[2] = 0.0f;
|
||||||
@@ -73,7 +106,7 @@ errorret_t uiFrameDraw(
|
|||||||
sprites[2].uvMax[0] = uv[2];
|
sprites[2].uvMax[0] = uv[2];
|
||||||
sprites[2].uvMax[1] = uv[3];
|
sprites[2].uvMax[1] = uv[3];
|
||||||
|
|
||||||
tilesetPositionGetUV(&frame->tileset, 0, 1, uv);
|
tilesetPositionGetUV(&UI_FRAME.tileset, 0, 1, uv);
|
||||||
sprites[3].min[0] = x;
|
sprites[3].min[0] = x;
|
||||||
sprites[3].min[1] = y + tileH;
|
sprites[3].min[1] = y + tileH;
|
||||||
sprites[3].min[2] = 0.0f;
|
sprites[3].min[2] = 0.0f;
|
||||||
@@ -85,7 +118,7 @@ errorret_t uiFrameDraw(
|
|||||||
sprites[3].uvMax[0] = uv[2];
|
sprites[3].uvMax[0] = uv[2];
|
||||||
sprites[3].uvMax[1] = uv[3];
|
sprites[3].uvMax[1] = uv[3];
|
||||||
|
|
||||||
tilesetPositionGetUV(&frame->tileset, 1, 1, uv);
|
tilesetPositionGetUV(&UI_FRAME.tileset, 1, 1, uv);
|
||||||
sprites[4].min[0] = x + tileW;
|
sprites[4].min[0] = x + tileW;
|
||||||
sprites[4].min[1] = y + tileH;
|
sprites[4].min[1] = y + tileH;
|
||||||
sprites[4].min[2] = 0.0f;
|
sprites[4].min[2] = 0.0f;
|
||||||
@@ -97,7 +130,7 @@ errorret_t uiFrameDraw(
|
|||||||
sprites[4].uvMax[0] = uv[2];
|
sprites[4].uvMax[0] = uv[2];
|
||||||
sprites[4].uvMax[1] = uv[3];
|
sprites[4].uvMax[1] = uv[3];
|
||||||
|
|
||||||
tilesetPositionGetUV(&frame->tileset, 2, 1, uv);
|
tilesetPositionGetUV(&UI_FRAME.tileset, 2, 1, uv);
|
||||||
sprites[5].min[0] = x + width - tileW;
|
sprites[5].min[0] = x + width - tileW;
|
||||||
sprites[5].min[1] = y + tileH;
|
sprites[5].min[1] = y + tileH;
|
||||||
sprites[5].min[2] = 0.0f;
|
sprites[5].min[2] = 0.0f;
|
||||||
@@ -109,7 +142,7 @@ errorret_t uiFrameDraw(
|
|||||||
sprites[5].uvMax[0] = uv[2];
|
sprites[5].uvMax[0] = uv[2];
|
||||||
sprites[5].uvMax[1] = uv[3];
|
sprites[5].uvMax[1] = uv[3];
|
||||||
|
|
||||||
tilesetPositionGetUV(&frame->tileset, 0, 2, uv);
|
tilesetPositionGetUV(&UI_FRAME.tileset, 0, 2, uv);
|
||||||
sprites[6].min[0] = x;
|
sprites[6].min[0] = x;
|
||||||
sprites[6].min[1] = y + height - tileH;
|
sprites[6].min[1] = y + height - tileH;
|
||||||
sprites[6].min[2] = 0.0f;
|
sprites[6].min[2] = 0.0f;
|
||||||
@@ -121,7 +154,7 @@ errorret_t uiFrameDraw(
|
|||||||
sprites[6].uvMax[0] = uv[2];
|
sprites[6].uvMax[0] = uv[2];
|
||||||
sprites[6].uvMax[1] = uv[3];
|
sprites[6].uvMax[1] = uv[3];
|
||||||
|
|
||||||
tilesetPositionGetUV(&frame->tileset, 1, 2, uv);
|
tilesetPositionGetUV(&UI_FRAME.tileset, 1, 2, uv);
|
||||||
sprites[7].min[0] = x + tileW;
|
sprites[7].min[0] = x + tileW;
|
||||||
sprites[7].min[1] = y + height - tileH;
|
sprites[7].min[1] = y + height - tileH;
|
||||||
sprites[7].min[2] = 0.0f;
|
sprites[7].min[2] = 0.0f;
|
||||||
@@ -133,7 +166,7 @@ errorret_t uiFrameDraw(
|
|||||||
sprites[7].uvMax[0] = uv[2];
|
sprites[7].uvMax[0] = uv[2];
|
||||||
sprites[7].uvMax[1] = uv[3];
|
sprites[7].uvMax[1] = uv[3];
|
||||||
|
|
||||||
tilesetPositionGetUV(&frame->tileset, 2, 2, uv);
|
tilesetPositionGetUV(&UI_FRAME.tileset, 2, 2, uv);
|
||||||
sprites[8].min[0] = x + width - tileW;
|
sprites[8].min[0] = x + width - tileW;
|
||||||
sprites[8].min[1] = y + height - tileH;
|
sprites[8].min[1] = y + height - tileH;
|
||||||
sprites[8].min[2] = 0.0f;
|
sprites[8].min[2] = 0.0f;
|
||||||
@@ -148,7 +181,6 @@ errorret_t uiFrameDraw(
|
|||||||
return spriteBatchBuffer(sprites, 9, &SHADER_UNLIT, material);
|
return spriteBatchBuffer(sprites, 9, &SHADER_UNLIT, material);
|
||||||
}
|
}
|
||||||
|
|
||||||
void uiFrameDispose(uiframe_t *frame) {
|
errorret_t uiFrameDispose(void) {
|
||||||
assertNotNull(frame, "frame must not be NULL");
|
return textureDispose(&UI_FRAME.texture);
|
||||||
frame->texture = NULL;
|
|
||||||
}
|
}
|
||||||
@@ -10,24 +10,29 @@
|
|||||||
#include "display/texture/texture.h"
|
#include "display/texture/texture.h"
|
||||||
#include "display/texture/tileset.h"
|
#include "display/texture/tileset.h"
|
||||||
|
|
||||||
|
#define UIFRAME_DUMMY_WIDTH 16
|
||||||
|
#define UIFRAME_DUMMY_HEIGHT 16
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
tileset_t tileset;
|
tileset_t tileset;
|
||||||
texture_t *texture;
|
texture_t texture;
|
||||||
|
color_t pixels[UIFRAME_DUMMY_WIDTH * UIFRAME_DUMMY_HEIGHT];
|
||||||
} uiframe_t;
|
} uiframe_t;
|
||||||
|
|
||||||
/**
|
extern uiframe_t UI_FRAME;
|
||||||
* Initializes a UI frame element.
|
|
||||||
*
|
|
||||||
* @param frame The frame to initialize.
|
|
||||||
* @return Any error that occurs.
|
|
||||||
*/
|
|
||||||
errorret_t uiFrameInit(uiframe_t *frame);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a UI frame using 9-slice rendering from a 3x3 tileset.
|
* Initializes the global UI_FRAME: builds the dummy texture and
|
||||||
|
* configures the tileset. Call once after displayInit().
|
||||||
|
*
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiFrameInit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws UI_FRAME using 9-slice rendering from its 3x3 tileset.
|
||||||
* Pushes quads to the sprite batch without flushing.
|
* Pushes quads to the sprite batch without flushing.
|
||||||
*
|
*
|
||||||
* @param frame The frame to draw.
|
|
||||||
* @param x Screen x position.
|
* @param x Screen x position.
|
||||||
* @param y Screen y position.
|
* @param y Screen y position.
|
||||||
* @param width Total width of the frame.
|
* @param width Total width of the frame.
|
||||||
@@ -35,7 +40,6 @@ errorret_t uiFrameInit(uiframe_t *frame);
|
|||||||
* @return Any error that occurs.
|
* @return Any error that occurs.
|
||||||
*/
|
*/
|
||||||
errorret_t uiFrameDraw(
|
errorret_t uiFrameDraw(
|
||||||
const uiframe_t *frame,
|
|
||||||
const float_t x,
|
const float_t x,
|
||||||
const float_t y,
|
const float_t y,
|
||||||
const float_t width,
|
const float_t width,
|
||||||
@@ -43,8 +47,8 @@ errorret_t uiFrameDraw(
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disposes of a UI frame element. Does not dispose the texture.
|
* Disposes of the global UI_FRAME, releasing its GPU texture.
|
||||||
*
|
*
|
||||||
* @param frame The frame to dispose.
|
* @return Any error that occurs.
|
||||||
*/
|
*/
|
||||||
void uiFrameDispose(uiframe_t *frame);
|
errorret_t uiFrameDispose(void);
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "uisettings.h"
|
||||||
|
#include "ui/frame/uiframe.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
|
||||||
|
uisettings_t UI_SETTINGS;
|
||||||
|
|
||||||
|
errorret_t uiSettingsInit(void) {
|
||||||
|
memoryZero(&UI_SETTINGS, sizeof(uisettings_t));
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiSettingsUpdate(void) {
|
||||||
|
// if(!UI_SETTINGS.visible) errorOk();
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiSettingsDraw(void) {
|
||||||
|
// if(!UI_SETTINGS.visible) errorOk();
|
||||||
|
errorChain(uiFrameDraw(
|
||||||
|
100.0f, 100.0f, 1.0f, 1.0f
|
||||||
|
));
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t uiSettingsDispose(void) {
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "error/error.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool_t visible;
|
||||||
|
} uisettings_t;
|
||||||
|
|
||||||
|
extern uisettings_t UI_SETTINGS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the settings panel.
|
||||||
|
*
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiSettingsInit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the settings panel. Handles input when visible.
|
||||||
|
*
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiSettingsUpdate(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the settings panel. No-op when not visible.
|
||||||
|
*
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiSettingsDraw(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes of the settings panel.
|
||||||
|
*
|
||||||
|
* @return Any error that occurs.
|
||||||
|
*/
|
||||||
|
errorret_t uiSettingsDispose(void);
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# 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
|
||||||
|
uicrop.c
|
||||||
|
uifullbox.c
|
||||||
|
uiloading.c
|
||||||
|
)
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
#include "display/text/text.h"
|
#include "display/text/text.h"
|
||||||
#include "display/screen/screen.h"
|
#include "display/screen/screen.h"
|
||||||
#include "display/spritebatch/spritebatch.h"
|
#include "display/spritebatch/spritebatch.h"
|
||||||
#include "ui/uifullbox.h"
|
#include "uifullbox.h"
|
||||||
|
|
||||||
#define UI_LOADING_TEXT "loading"
|
#define UI_LOADING_TEXT "loading"
|
||||||
|
|
||||||
+5
-5
@@ -11,7 +11,7 @@
|
|||||||
#include "display/spritebatch/spritebatch.h"
|
#include "display/spritebatch/spritebatch.h"
|
||||||
#include "display/screen/screen.h"
|
#include "display/screen/screen.h"
|
||||||
#include "ui/uielement.h"
|
#include "ui/uielement.h"
|
||||||
#include "ui/uifocus/uifocus.h"
|
#include "ui/focus/uifocus.h"
|
||||||
#include "log/log.h"
|
#include "log/log.h"
|
||||||
|
|
||||||
ui_t UI;
|
ui_t UI;
|
||||||
@@ -21,7 +21,7 @@ errorret_t uiInit(void) {
|
|||||||
uiFocusInit();
|
uiFocusInit();
|
||||||
|
|
||||||
uielement_t *element = &UI_ELEMENTS[0];
|
uielement_t *element = &UI_ELEMENTS[0];
|
||||||
while(element->draw != NULL) {
|
while(!uiElementIsNull(element)) {
|
||||||
errorChain(uiElementInit(element));
|
errorChain(uiElementInit(element));
|
||||||
element++;
|
element++;
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,7 @@ errorret_t uiUpdate(void) {
|
|||||||
uiFocusUpdate();
|
uiFocusUpdate();
|
||||||
|
|
||||||
uielement_t *element = &UI_ELEMENTS[0];
|
uielement_t *element = &UI_ELEMENTS[0];
|
||||||
while(element->draw != NULL) {
|
while(!uiElementIsNull(element)) {
|
||||||
errorChain(uiElementUpdate(element));
|
errorChain(uiElementUpdate(element));
|
||||||
element++;
|
element++;
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,7 @@ errorret_t uiUpdate(void) {
|
|||||||
|
|
||||||
errorret_t uiRender(void) {
|
errorret_t uiRender(void) {
|
||||||
const uielement_t *element = &UI_ELEMENTS[0];
|
const uielement_t *element = &UI_ELEMENTS[0];
|
||||||
while(element->draw != NULL) {
|
while(!uiElementIsNull(element)) {
|
||||||
errorChain(uiElementDraw(element));
|
errorChain(uiElementDraw(element));
|
||||||
|
|
||||||
if(SPRITEBATCH.spriteCount > 0) {
|
if(SPRITEBATCH.spriteCount > 0) {
|
||||||
@@ -58,7 +58,7 @@ errorret_t uiRender(void) {
|
|||||||
|
|
||||||
errorret_t uiDispose(void) {
|
errorret_t uiDispose(void) {
|
||||||
uielement_t *element = &UI_ELEMENTS[0];
|
uielement_t *element = &UI_ELEMENTS[0];
|
||||||
while(element->draw != NULL) {
|
while(!uiElementIsNull(element)) {
|
||||||
errorChain(uiElementDispose(element));
|
errorChain(uiElementDispose(element));
|
||||||
element++;
|
element++;
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-11
@@ -7,16 +7,24 @@
|
|||||||
|
|
||||||
#include "uielement.h"
|
#include "uielement.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
|
#include "ui/frame/uiframe.h"
|
||||||
#include "ui/debug/uifps.h"
|
#include "ui/debug/uifps.h"
|
||||||
#include "engine/engine.h"
|
#include "engine/engine.h"
|
||||||
#include "ui/uitextbox.h"
|
#include "ui/uitextbox.h"
|
||||||
#include "ui/uifullbox.h"
|
#include "ui/overlay/uifullbox.h"
|
||||||
#include "ui/uiloading.h"
|
#include "ui/overlay/uiloading.h"
|
||||||
#include "ui/debug/uiplayerpos.h"
|
#include "ui/debug/uiplayerpos.h"
|
||||||
#include "ui/uicrop.h"
|
#include "ui/overlay/uicrop.h"
|
||||||
#include "ui/debug/uiconsole.h"
|
#include "ui/debug/uiconsole.h"
|
||||||
|
#include "ui/frame/uiframe.h"
|
||||||
|
#include "ui/frame/uisettings.h"
|
||||||
|
|
||||||
uielement_t UI_ELEMENTS[] = {
|
uielement_t UI_ELEMENTS[] = {
|
||||||
|
{
|
||||||
|
.init = uiFrameInit,
|
||||||
|
.dispose = uiFrameDispose
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
// Fullbox under: above scene, below system UI.
|
// Fullbox under: above scene, below system UI.
|
||||||
{
|
{
|
||||||
@@ -25,14 +33,21 @@ uielement_t UI_ELEMENTS[] = {
|
|||||||
.draw = uiFullboxUnderDraw
|
.draw = uiFullboxUnderDraw
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
.init = uiSettingsInit,
|
||||||
|
.update = uiSettingsUpdate,
|
||||||
|
.draw = uiSettingsDraw,
|
||||||
|
.dispose = uiSettingsDispose
|
||||||
|
},
|
||||||
|
|
||||||
// { .type = UI_ELEMENT_TYPE_SCRIPT, .script = { .script = "ui/test.js" } },
|
// { .type = UI_ELEMENT_TYPE_SCRIPT, .script = { .script = "ui/test.js" } },
|
||||||
|
|
||||||
{
|
// {
|
||||||
.init = uiTextboxInit,
|
// .init = uiTextboxInit,
|
||||||
.update = uiTextboxUpdate,
|
// .update = uiTextboxUpdate,
|
||||||
.draw = uiTextboxDraw,
|
// .draw = uiTextboxDraw,
|
||||||
.dispose = uiTextboxDispose
|
// .dispose = uiTextboxDispose
|
||||||
},
|
// },
|
||||||
|
|
||||||
// Fullbox over: above absolutely everything.
|
// Fullbox over: above absolutely everything.
|
||||||
{
|
{
|
||||||
@@ -60,8 +75,16 @@ uielement_t UI_ELEMENTS[] = {
|
|||||||
{ 0 } // Null terminator
|
{ 0 } // Null terminator
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool_t uiElementIsNull(const uielement_t *element) {
|
||||||
|
return element->init == NULL &&
|
||||||
|
element->update == NULL &&
|
||||||
|
element->draw == NULL &&
|
||||||
|
element->dispose == NULL;
|
||||||
|
}
|
||||||
|
|
||||||
errorret_t uiElementInit(uielement_t *element) {
|
errorret_t uiElementInit(uielement_t *element) {
|
||||||
assertNotNull(element, "element must not be NULL");
|
assertNotNull(element, "element must not be NULL");
|
||||||
|
errorChain(uiFrameInit());
|
||||||
if(element->init != NULL) errorChain(element->init());
|
if(element->init != NULL) errorChain(element->init());
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
@@ -74,12 +97,13 @@ errorret_t uiElementUpdate(uielement_t *element) {
|
|||||||
|
|
||||||
errorret_t uiElementDraw(const uielement_t *element) {
|
errorret_t uiElementDraw(const uielement_t *element) {
|
||||||
assertNotNull(element, "element must not be NULL");
|
assertNotNull(element, "element must not be NULL");
|
||||||
assertNotNull(element->draw, "element draw callback must not be NULL");
|
if(element->draw != NULL) errorChain(element->draw());
|
||||||
return element->draw();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t uiElementDispose(uielement_t *element) {
|
errorret_t uiElementDispose(uielement_t *element) {
|
||||||
assertNotNull(element, "element must not be NULL");
|
assertNotNull(element, "element must not be NULL");
|
||||||
if(element->dispose != NULL) errorChain(element->dispose());
|
if(element->dispose != NULL) errorChain(element->dispose());
|
||||||
|
errorChain(uiFrameDispose());
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
+13
-4
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (c) 2026 Dominic Masters
|
* Copyright (c) 2026 Dominic Masters
|
||||||
*
|
*
|
||||||
* This software is released under the MIT License.
|
* This software is released under the MIT License.
|
||||||
* https://opensource.org/licenses/MIT
|
* https://opensource.org/licenses/MIT
|
||||||
*/
|
*/
|
||||||
@@ -18,7 +18,16 @@ typedef struct {
|
|||||||
extern uielement_t UI_ELEMENTS[];
|
extern uielement_t UI_ELEMENTS[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a UI element, calling its init callback if set.
|
* Returns true when all four callbacks on the element are NULL,
|
||||||
|
* which marks the end of the UI_ELEMENTS array.
|
||||||
|
*
|
||||||
|
* @param element The element to test.
|
||||||
|
* @returns True if the element is the null terminator.
|
||||||
|
*/
|
||||||
|
bool_t uiElementIsNull(const uielement_t *element);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a UI element, invoking its init callback if set.
|
||||||
*
|
*
|
||||||
* @param element The element to initialize.
|
* @param element The element to initialize.
|
||||||
* @return Any error that occurs.
|
* @return Any error that occurs.
|
||||||
@@ -42,9 +51,9 @@ errorret_t uiElementUpdate(uielement_t *element);
|
|||||||
errorret_t uiElementDraw(const uielement_t *element);
|
errorret_t uiElementDraw(const uielement_t *element);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disposes of a UI element, calling its dispose callback if set.
|
* Disposes of a UI element, invoking its dispose callback if set.
|
||||||
*
|
*
|
||||||
* @param element The element to dispose.
|
* @param element The element to dispose.
|
||||||
* @return Any error that occurs.
|
* @return Any error that occurs.
|
||||||
*/
|
*/
|
||||||
errorret_t uiElementDispose(uielement_t *element);
|
errorret_t uiElementDispose(uielement_t *element);
|
||||||
|
|||||||
+230
-230
@@ -1,278 +1,278 @@
|
|||||||
// Copyright (c) 2026 Dominic Masters
|
// // Copyright (c) 2026 Dominic Masters
|
||||||
//
|
// //
|
||||||
// This software is released under the MIT License.
|
// // This software is released under the MIT License.
|
||||||
// https://opensource.org/licenses/MIT
|
// // https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
#include "uitextbox.h"
|
// #include "uitextbox.h"
|
||||||
#include "assert/assert.h"
|
// #include "assert/assert.h"
|
||||||
#include "util/memory.h"
|
// #include "util/memory.h"
|
||||||
#include "util/string.h"
|
// #include "util/string.h"
|
||||||
#include "time/time.h"
|
// #include "time/time.h"
|
||||||
#include "input/input.h"
|
// #include "input/input.h"
|
||||||
#include "event/event.h"
|
// #include "event/event.h"
|
||||||
#include "display/screen/screen.h"
|
// #include "display/screen/screen.h"
|
||||||
#include "display/texture/texture.h"
|
// #include "display/texture/texture.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"
|
||||||
|
|
||||||
uitextbox_t UI_TEXTBOX;
|
// uitextbox_t UI_TEXTBOX;
|
||||||
|
|
||||||
errorret_t uiTextboxInit(void) {
|
// errorret_t uiTextboxInit(void) {
|
||||||
memoryZero(&UI_TEXTBOX, sizeof(uitextbox_t));
|
// memoryZero(&UI_TEXTBOX, sizeof(uitextbox_t));
|
||||||
UI_TEXTBOX.textColor = COLOR_WHITE;
|
// UI_TEXTBOX.textColor = COLOR_WHITE;
|
||||||
UI_TEXTBOX.advanceAction = INPUT_ACTION_ACCEPT;
|
// UI_TEXTBOX.advanceAction = INPUT_ACTION_ACCEPT;
|
||||||
|
|
||||||
UI_TEXTBOX.font = &FONT_DEFAULT;
|
// UI_TEXTBOX.font = &FONT_DEFAULT;
|
||||||
|
|
||||||
float_t fontW = (float_t)FONT_DEFAULT.tileset->tileWidth;
|
// float_t fontW = (float_t)FONT_DEFAULT.tileset->tileWidth;
|
||||||
float_t fontH = (float_t)FONT_DEFAULT.tileset->tileHeight;
|
// float_t fontH = (float_t)FONT_DEFAULT.tileset->tileHeight;
|
||||||
float_t tbHeight = (
|
// float_t tbHeight = (
|
||||||
(float_t)UI_TEXTBOX_LINES_PER_PAGE_MAX * fontH +
|
// (float_t)UI_TEXTBOX_LINES_PER_PAGE_MAX * fontH +
|
||||||
(float_t)(UI_TEXTBOX_LINES_PER_PAGE_MAX - 1) * UI_TEXTBOX_LINE_SPACING +
|
// (float_t)(UI_TEXTBOX_LINES_PER_PAGE_MAX - 1) * UI_TEXTBOX_LINE_SPACING +
|
||||||
2.0f * fontH
|
// 2.0f * fontH
|
||||||
);
|
// );
|
||||||
UI_TEXTBOX.x = (float_t)SCREEN.scanX + 10.0f;
|
// UI_TEXTBOX.x = (float_t)SCREEN.scanX + 10.0f;
|
||||||
UI_TEXTBOX.y = (float_t)(SCREEN.scanY + SCREEN.scanHeight) -
|
// UI_TEXTBOX.y = (float_t)(SCREEN.scanY + SCREEN.scanHeight) -
|
||||||
tbHeight - 10.0f;
|
// tbHeight - 10.0f;
|
||||||
UI_TEXTBOX.width = (float_t)SCREEN.scanWidth - 20.0f;
|
// UI_TEXTBOX.width = (float_t)SCREEN.scanWidth - 20.0f;
|
||||||
UI_TEXTBOX.height = tbHeight;
|
// UI_TEXTBOX.height = tbHeight;
|
||||||
|
|
||||||
UI_TEXTBOX.frame.tileset.columns = 3;
|
// UI_TEXTBOX.frame.tileset.columns = 3;
|
||||||
UI_TEXTBOX.frame.tileset.rows = 3;
|
// UI_TEXTBOX.frame.tileset.rows = 3;
|
||||||
UI_TEXTBOX.frame.tileset.tileCount = 9;
|
// UI_TEXTBOX.frame.tileset.tileCount = 9;
|
||||||
UI_TEXTBOX.frame.tileset.tileWidth = FONT_DEFAULT.tileset->tileWidth;
|
// UI_TEXTBOX.frame.tileset.tileWidth = FONT_DEFAULT.tileset->tileWidth;
|
||||||
UI_TEXTBOX.frame.tileset.tileHeight = FONT_DEFAULT.tileset->tileHeight;
|
// UI_TEXTBOX.frame.tileset.tileHeight = FONT_DEFAULT.tileset->tileHeight;
|
||||||
UI_TEXTBOX.frame.tileset.uv[0] = 1.0f / 3.0f;
|
// UI_TEXTBOX.frame.tileset.uv[0] = 1.0f / 3.0f;
|
||||||
UI_TEXTBOX.frame.tileset.uv[1] = 1.0f / 3.0f;
|
// UI_TEXTBOX.frame.tileset.uv[1] = 1.0f / 3.0f;
|
||||||
UI_TEXTBOX.frame.texture = &TEXTURE_WHITE;
|
// UI_TEXTBOX.frame.texture = &TEXTURE_WHITE;
|
||||||
|
|
||||||
eventInit(
|
// eventInit(
|
||||||
&UI_TEXTBOX.onPageComplete,
|
// &UI_TEXTBOX.onPageComplete,
|
||||||
UI_TEXTBOX.onPageCompleteCallbacks,
|
// UI_TEXTBOX.onPageCompleteCallbacks,
|
||||||
UI_TEXTBOX.onPageCompleteUsers,
|
// UI_TEXTBOX.onPageCompleteUsers,
|
||||||
4
|
// 4
|
||||||
);
|
// );
|
||||||
eventInit(
|
// eventInit(
|
||||||
&UI_TEXTBOX.onLastPage,
|
// &UI_TEXTBOX.onLastPage,
|
||||||
UI_TEXTBOX.onLastPageCallbacks,
|
// UI_TEXTBOX.onLastPageCallbacks,
|
||||||
UI_TEXTBOX.onLastPageUsers,
|
// UI_TEXTBOX.onLastPageUsers,
|
||||||
4
|
// 4
|
||||||
);
|
// );
|
||||||
|
|
||||||
errorOk();
|
// errorOk();
|
||||||
}
|
// }
|
||||||
|
|
||||||
void uiTextboxBuildLayout(void) {
|
// void uiTextboxBuildLayout(void) {
|
||||||
UI_TEXTBOX.lineCount = 0;
|
// UI_TEXTBOX.lineCount = 0;
|
||||||
UI_TEXTBOX.pageCount = 1;
|
// UI_TEXTBOX.pageCount = 1;
|
||||||
|
|
||||||
float_t frameTileW = (float_t)UI_TEXTBOX.frame.tileset.tileWidth;
|
// float_t frameTileW = (float_t)UI_TEXTBOX.frame.tileset.tileWidth;
|
||||||
float_t frameTileH = (float_t)UI_TEXTBOX.frame.tileset.tileHeight;
|
// float_t frameTileH = (float_t)UI_TEXTBOX.frame.tileset.tileHeight;
|
||||||
float_t fontW = (float_t)UI_TEXTBOX.font->tileset->tileWidth;
|
// float_t fontW = (float_t)UI_TEXTBOX.font->tileset->tileWidth;
|
||||||
float_t fontH = (float_t)UI_TEXTBOX.font->tileset->tileHeight;
|
// float_t fontH = (float_t)UI_TEXTBOX.font->tileset->tileHeight;
|
||||||
|
|
||||||
if(fontW <= 0.0f || fontH <= 0.0f) return;
|
// if(fontW <= 0.0f || fontH <= 0.0f) return;
|
||||||
|
|
||||||
float_t contentW = UI_TEXTBOX.width - 2.0f * frameTileW;
|
// float_t contentW = UI_TEXTBOX.width - 2.0f * frameTileW;
|
||||||
float_t contentH = UI_TEXTBOX.height - 2.0f * frameTileH;
|
// float_t contentH = UI_TEXTBOX.height - 2.0f * frameTileH;
|
||||||
|
|
||||||
UI_TEXTBOX.charsPerLine = (int32_t)(contentW / fontW);
|
// UI_TEXTBOX.charsPerLine = (int32_t)(contentW / fontW);
|
||||||
UI_TEXTBOX.linesPerPage = UI_TEXTBOX_LINES_PER_PAGE_MAX;
|
// UI_TEXTBOX.linesPerPage = UI_TEXTBOX_LINES_PER_PAGE_MAX;
|
||||||
|
|
||||||
if(UI_TEXTBOX.charsPerLine <= 0 || UI_TEXTBOX.linesPerPage <= 0) return;
|
// if(UI_TEXTBOX.charsPerLine <= 0 || UI_TEXTBOX.linesPerPage <= 0) return;
|
||||||
if(UI_TEXTBOX.text[0] == '\0') return;
|
// if(UI_TEXTBOX.text[0] == '\0') return;
|
||||||
|
|
||||||
char_t *src = UI_TEXTBOX.text;
|
// char_t *src = UI_TEXTBOX.text;
|
||||||
int32_t i = 0;
|
// int32_t i = 0;
|
||||||
|
|
||||||
while(src[i] != '\0' && UI_TEXTBOX.lineCount < UI_TEXTBOX_LINES_MAX) {
|
// while(src[i] != '\0' && UI_TEXTBOX.lineCount < UI_TEXTBOX_LINES_MAX) {
|
||||||
if(src[i] == '\t') {
|
// if(src[i] == '\t') {
|
||||||
i++;
|
// i++;
|
||||||
int32_t rem = UI_TEXTBOX.lineCount % UI_TEXTBOX.linesPerPage;
|
// int32_t rem = UI_TEXTBOX.lineCount % UI_TEXTBOX.linesPerPage;
|
||||||
int32_t pad = rem > 0 ? UI_TEXTBOX.linesPerPage - rem : 0;
|
// int32_t pad = rem > 0 ? UI_TEXTBOX.linesPerPage - rem : 0;
|
||||||
while(pad > 0 && UI_TEXTBOX.lineCount < UI_TEXTBOX_LINES_MAX) {
|
// while(pad > 0 && UI_TEXTBOX.lineCount < UI_TEXTBOX_LINES_MAX) {
|
||||||
UI_TEXTBOX.lines[UI_TEXTBOX.lineCount].start = i;
|
// UI_TEXTBOX.lines[UI_TEXTBOX.lineCount].start = i;
|
||||||
UI_TEXTBOX.lines[UI_TEXTBOX.lineCount].count = 0;
|
// UI_TEXTBOX.lines[UI_TEXTBOX.lineCount].count = 0;
|
||||||
UI_TEXTBOX.lineCount++;
|
// UI_TEXTBOX.lineCount++;
|
||||||
pad--;
|
// pad--;
|
||||||
}
|
// }
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
|
|
||||||
int32_t lineStart = i;
|
// int32_t lineStart = i;
|
||||||
int32_t lineWidth = 0;
|
// int32_t lineWidth = 0;
|
||||||
|
|
||||||
while(src[i] != '\0') {
|
// while(src[i] != '\0') {
|
||||||
char_t c = src[i];
|
// char_t c = src[i];
|
||||||
|
|
||||||
if(c == '\n') {
|
// if(c == '\n') {
|
||||||
i++;
|
// i++;
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if(c == '\t') break;
|
// if(c == '\t') break;
|
||||||
|
|
||||||
if(c == ' ') {
|
// if(c == ' ') {
|
||||||
int32_t wordLen = 0;
|
// int32_t wordLen = 0;
|
||||||
int32_t j = i + 1;
|
// int32_t j = i + 1;
|
||||||
while(
|
// while(
|
||||||
src[j] != ' ' && src[j] != '\n' &&
|
// src[j] != ' ' && src[j] != '\n' &&
|
||||||
src[j] != '\t' && src[j] != '\0'
|
// src[j] != '\t' && src[j] != '\0'
|
||||||
) {
|
// ) {
|
||||||
wordLen++;
|
// wordLen++;
|
||||||
j++;
|
// j++;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if(
|
// if(
|
||||||
lineWidth > 0 &&
|
// lineWidth > 0 &&
|
||||||
lineWidth + 1 + wordLen > UI_TEXTBOX.charsPerLine
|
// lineWidth + 1 + wordLen > UI_TEXTBOX.charsPerLine
|
||||||
) {
|
// ) {
|
||||||
i++;
|
// i++;
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
lineWidth++;
|
// lineWidth++;
|
||||||
i++;
|
// i++;
|
||||||
} else {
|
// } else {
|
||||||
if(lineWidth >= UI_TEXTBOX.charsPerLine) break;
|
// if(lineWidth >= UI_TEXTBOX.charsPerLine) break;
|
||||||
lineWidth++;
|
// lineWidth++;
|
||||||
i++;
|
// i++;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
UI_TEXTBOX.lines[UI_TEXTBOX.lineCount].start = lineStart;
|
// UI_TEXTBOX.lines[UI_TEXTBOX.lineCount].start = lineStart;
|
||||||
UI_TEXTBOX.lines[UI_TEXTBOX.lineCount].count = lineWidth;
|
// UI_TEXTBOX.lines[UI_TEXTBOX.lineCount].count = lineWidth;
|
||||||
UI_TEXTBOX.lineCount++;
|
// UI_TEXTBOX.lineCount++;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if(UI_TEXTBOX.lineCount == 0) {
|
// if(UI_TEXTBOX.lineCount == 0) {
|
||||||
UI_TEXTBOX.pageCount = 1;
|
// UI_TEXTBOX.pageCount = 1;
|
||||||
} else {
|
// } else {
|
||||||
int32_t div = UI_TEXTBOX.lineCount + UI_TEXTBOX.linesPerPage - 1;
|
// int32_t div = UI_TEXTBOX.lineCount + UI_TEXTBOX.linesPerPage - 1;
|
||||||
UI_TEXTBOX.pageCount = div / UI_TEXTBOX.linesPerPage;
|
// UI_TEXTBOX.pageCount = div / UI_TEXTBOX.linesPerPage;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
void uiTextboxSetText(const char_t *text) {
|
// void uiTextboxSetText(const char_t *text) {
|
||||||
assertNotNull(text, "text must not be NULL");
|
// assertNotNull(text, "text must not be NULL");
|
||||||
|
|
||||||
stringCopy(UI_TEXTBOX.text, text, UI_TEXTBOX_TEXT_MAX);
|
// stringCopy(UI_TEXTBOX.text, text, UI_TEXTBOX_TEXT_MAX);
|
||||||
UI_TEXTBOX.currentPage = 0;
|
// UI_TEXTBOX.currentPage = 0;
|
||||||
UI_TEXTBOX.scroll = 0;
|
// UI_TEXTBOX.scroll = 0;
|
||||||
uiTextboxBuildLayout();
|
// uiTextboxBuildLayout();
|
||||||
}
|
// }
|
||||||
|
|
||||||
errorret_t uiTextboxUpdate(void) {
|
// errorret_t uiTextboxUpdate(void) {
|
||||||
#ifdef DUSK_TIME_DYNAMIC
|
// #ifdef DUSK_TIME_DYNAMIC
|
||||||
if(TIME.dynamicUpdate) errorOk();
|
// if(TIME.dynamicUpdate) errorOk();
|
||||||
#endif
|
// #endif
|
||||||
|
|
||||||
bool_t wasComplete = uiTextboxPageIsComplete();
|
// bool_t wasComplete = uiTextboxPageIsComplete();
|
||||||
|
|
||||||
if(!wasComplete) {
|
// if(!wasComplete) {
|
||||||
UI_TEXTBOX.scroll += UI_TEXTBOX_SCROLL_CHARS_PER_TICK;
|
// UI_TEXTBOX.scroll += UI_TEXTBOX_SCROLL_CHARS_PER_TICK;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if(inputPressed(UI_TEXTBOX.advanceAction)) {
|
// if(inputPressed(UI_TEXTBOX.advanceAction)) {
|
||||||
if(!uiTextboxPageIsComplete()) {
|
// if(!uiTextboxPageIsComplete()) {
|
||||||
UI_TEXTBOX.scroll = uiTextboxGetPageCharCount();
|
// UI_TEXTBOX.scroll = uiTextboxGetPageCharCount();
|
||||||
} else if(uiTextboxHasNextPage()) {
|
// } else if(uiTextboxHasNextPage()) {
|
||||||
uiTextboxNextPage();
|
// uiTextboxNextPage();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
if(!wasComplete && uiTextboxPageIsComplete()) {
|
// if(!wasComplete && uiTextboxPageIsComplete()) {
|
||||||
eventInvoke(&UI_TEXTBOX.onPageComplete, &UI_TEXTBOX);
|
// eventInvoke(&UI_TEXTBOX.onPageComplete, &UI_TEXTBOX);
|
||||||
if(!uiTextboxHasNextPage()) {
|
// if(!uiTextboxHasNextPage()) {
|
||||||
eventInvoke(&UI_TEXTBOX.onLastPage, &UI_TEXTBOX);
|
// eventInvoke(&UI_TEXTBOX.onLastPage, &UI_TEXTBOX);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
errorOk();
|
// errorOk();
|
||||||
}
|
// }
|
||||||
|
|
||||||
int32_t uiTextboxGetPageCharCount(void) {
|
// int32_t uiTextboxGetPageCharCount(void) {
|
||||||
int32_t first = UI_TEXTBOX.currentPage * UI_TEXTBOX.linesPerPage;
|
// int32_t first = UI_TEXTBOX.currentPage * UI_TEXTBOX.linesPerPage;
|
||||||
int32_t last = first + UI_TEXTBOX.linesPerPage;
|
// int32_t last = first + UI_TEXTBOX.linesPerPage;
|
||||||
if(last > UI_TEXTBOX.lineCount) last = UI_TEXTBOX.lineCount;
|
// if(last > UI_TEXTBOX.lineCount) last = UI_TEXTBOX.lineCount;
|
||||||
int32_t total = 0;
|
// int32_t total = 0;
|
||||||
for(int32_t i = first; i < last; i++) {
|
// for(int32_t i = first; i < last; i++) {
|
||||||
total += UI_TEXTBOX.lines[i].count;
|
// total += UI_TEXTBOX.lines[i].count;
|
||||||
}
|
// }
|
||||||
return total;
|
// return total;
|
||||||
}
|
// }
|
||||||
|
|
||||||
bool_t uiTextboxPageIsComplete(void) {
|
// bool_t uiTextboxPageIsComplete(void) {
|
||||||
return UI_TEXTBOX.scroll >= uiTextboxGetPageCharCount();
|
// return UI_TEXTBOX.scroll >= uiTextboxGetPageCharCount();
|
||||||
}
|
// }
|
||||||
|
|
||||||
bool_t uiTextboxHasNextPage(void) {
|
// bool_t uiTextboxHasNextPage(void) {
|
||||||
return UI_TEXTBOX.currentPage + 1 < UI_TEXTBOX.pageCount;
|
// return UI_TEXTBOX.currentPage + 1 < UI_TEXTBOX.pageCount;
|
||||||
}
|
// }
|
||||||
|
|
||||||
void uiTextboxNextPage(void) {
|
// void uiTextboxNextPage(void) {
|
||||||
if(!uiTextboxHasNextPage()) return;
|
// if(!uiTextboxHasNextPage()) return;
|
||||||
UI_TEXTBOX.currentPage++;
|
// UI_TEXTBOX.currentPage++;
|
||||||
UI_TEXTBOX.scroll = 0;
|
// UI_TEXTBOX.scroll = 0;
|
||||||
}
|
// }
|
||||||
|
|
||||||
errorret_t uiTextboxDraw(void) {
|
// errorret_t uiTextboxDraw(void) {
|
||||||
if(UI_TEXTBOX.lineCount == 0 || UI_TEXTBOX.text[0] == '\0') errorOk();
|
// if(UI_TEXTBOX.lineCount == 0 || UI_TEXTBOX.text[0] == '\0') errorOk();
|
||||||
|
|
||||||
errorChain(uiFrameDraw(
|
// errorChain(uiFrameDraw(
|
||||||
&UI_TEXTBOX.frame,
|
// &UI_TEXTBOX.frame,
|
||||||
UI_TEXTBOX.x, UI_TEXTBOX.y,
|
// UI_TEXTBOX.x, UI_TEXTBOX.y,
|
||||||
UI_TEXTBOX.width, UI_TEXTBOX.height
|
// UI_TEXTBOX.width, UI_TEXTBOX.height
|
||||||
));
|
// ));
|
||||||
errorChain(spriteBatchFlush());
|
// errorChain(spriteBatchFlush());
|
||||||
|
|
||||||
shadermaterial_t textMaterial = {
|
// shadermaterial_t textMaterial = {
|
||||||
.unlit = {
|
// .unlit = {
|
||||||
.color = UI_TEXTBOX.textColor,
|
// .color = UI_TEXTBOX.textColor,
|
||||||
.texture = UI_TEXTBOX.font->texture
|
// .texture = UI_TEXTBOX.font->texture
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
float_t frameTileW = (float_t)UI_TEXTBOX.frame.tileset.tileWidth;
|
// float_t frameTileW = (float_t)UI_TEXTBOX.frame.tileset.tileWidth;
|
||||||
float_t frameTileH = (float_t)UI_TEXTBOX.frame.tileset.tileHeight;
|
// float_t frameTileH = (float_t)UI_TEXTBOX.frame.tileset.tileHeight;
|
||||||
float_t fontW = (float_t)UI_TEXTBOX.font->tileset->tileWidth;
|
// float_t fontW = (float_t)UI_TEXTBOX.font->tileset->tileWidth;
|
||||||
float_t fontH = (float_t)UI_TEXTBOX.font->tileset->tileHeight;
|
// float_t fontH = (float_t)UI_TEXTBOX.font->tileset->tileHeight;
|
||||||
float_t contentX = UI_TEXTBOX.x + frameTileW;
|
// float_t contentX = UI_TEXTBOX.x + frameTileW;
|
||||||
float_t contentY = UI_TEXTBOX.y + frameTileH;
|
// float_t contentY = UI_TEXTBOX.y + frameTileH;
|
||||||
|
|
||||||
int32_t pageFirst = UI_TEXTBOX.currentPage * UI_TEXTBOX.linesPerPage;
|
// int32_t pageFirst = UI_TEXTBOX.currentPage * UI_TEXTBOX.linesPerPage;
|
||||||
int32_t pageLast = pageFirst + UI_TEXTBOX.linesPerPage;
|
// int32_t pageLast = pageFirst + UI_TEXTBOX.linesPerPage;
|
||||||
if(pageLast > UI_TEXTBOX.lineCount) pageLast = UI_TEXTBOX.lineCount;
|
// if(pageLast > UI_TEXTBOX.lineCount) pageLast = UI_TEXTBOX.lineCount;
|
||||||
|
|
||||||
int32_t charsLeft = UI_TEXTBOX.scroll;
|
// int32_t charsLeft = UI_TEXTBOX.scroll;
|
||||||
|
|
||||||
for(int32_t li = pageFirst; li < pageLast && charsLeft > 0; li++) {
|
// for(int32_t li = pageFirst; li < pageLast && charsLeft > 0; li++) {
|
||||||
uitextboxline_t *line = &UI_TEXTBOX.lines[li];
|
// uitextboxline_t *line = &UI_TEXTBOX.lines[li];
|
||||||
int32_t visible = line->count < charsLeft ? line->count : charsLeft;
|
// int32_t visible = line->count < charsLeft ? line->count : charsLeft;
|
||||||
float_t lineY = contentY +
|
// float_t lineY = contentY +
|
||||||
(float_t)(li - pageFirst) * (fontH + UI_TEXTBOX_LINE_SPACING);
|
// (float_t)(li - pageFirst) * (fontH + UI_TEXTBOX_LINE_SPACING);
|
||||||
|
|
||||||
for(int32_t ci = 0; ci < visible; ci++) {
|
// for(int32_t ci = 0; ci < visible; ci++) {
|
||||||
char_t c = UI_TEXTBOX.text[line->start + ci];
|
// char_t c = UI_TEXTBOX.text[line->start + ci];
|
||||||
if(c == ' ') continue;
|
// if(c == ' ') continue;
|
||||||
spritebatchsprite_t sprite = textGetSprite(
|
// spritebatchsprite_t sprite = textGetSprite(
|
||||||
(vec2){ contentX + (float_t)ci * fontW, lineY },
|
// (vec2){ contentX + (float_t)ci * fontW, lineY },
|
||||||
c,
|
// c,
|
||||||
UI_TEXTBOX.font
|
// UI_TEXTBOX.font
|
||||||
);
|
// );
|
||||||
errorChain(spriteBatchBuffer(&sprite, 1, &SHADER_UNLIT, textMaterial));
|
// errorChain(spriteBatchBuffer(&sprite, 1, &SHADER_UNLIT, textMaterial));
|
||||||
}
|
// }
|
||||||
|
|
||||||
charsLeft -= visible;
|
// charsLeft -= visible;
|
||||||
}
|
// }
|
||||||
|
|
||||||
errorChain(spriteBatchFlush());
|
// errorChain(spriteBatchFlush());
|
||||||
errorOk();
|
// errorOk();
|
||||||
}
|
// }
|
||||||
|
|
||||||
errorret_t uiTextboxDispose(void) {
|
// errorret_t uiTextboxDispose(void) {
|
||||||
uiFrameDispose(&UI_TEXTBOX.frame);
|
// uiFrameDispose(&UI_TEXTBOX.frame);
|
||||||
UI_TEXTBOX.font = NULL;
|
// UI_TEXTBOX.font = NULL;
|
||||||
errorOk();
|
// errorOk();
|
||||||
}
|
// }
|
||||||
|
|||||||
+100
-100
@@ -1,121 +1,121 @@
|
|||||||
/**
|
// /**
|
||||||
* Copyright (c) 2026 Dominic Masters
|
// * Copyright (c) 2026 Dominic Masters
|
||||||
*
|
// *
|
||||||
* This software is released under the MIT License.
|
// * This software is released under the MIT License.
|
||||||
* https://opensource.org/licenses/MIT
|
// * https://opensource.org/licenses/MIT
|
||||||
*/
|
// */
|
||||||
|
|
||||||
#pragma once
|
// #pragma once
|
||||||
#include "uiframe.h"
|
// #include "ui/frame/uiframe.h"
|
||||||
#include "display/text/text.h"
|
// #include "display/text/text.h"
|
||||||
#include "input/inputaction.h"
|
// #include "input/inputaction.h"
|
||||||
#include "event/event.h"
|
// #include "event/event.h"
|
||||||
|
|
||||||
#define UI_TEXTBOX_TEXT_MAX 1024
|
// #define UI_TEXTBOX_TEXT_MAX 1024
|
||||||
#define UI_TEXTBOX_LINES_MAX 64
|
// #define UI_TEXTBOX_LINES_MAX 64
|
||||||
#define UI_TEXTBOX_SCROLL_CHARS_PER_TICK 1
|
// #define UI_TEXTBOX_SCROLL_CHARS_PER_TICK 1
|
||||||
#define UI_TEXTBOX_LINES_PER_PAGE_MAX 3
|
// #define UI_TEXTBOX_LINES_PER_PAGE_MAX 3
|
||||||
#define UI_TEXTBOX_LINE_SPACING 0.0f
|
// #define UI_TEXTBOX_LINE_SPACING 0.0f
|
||||||
|
|
||||||
typedef struct {
|
// typedef struct {
|
||||||
int32_t start;
|
// int32_t start;
|
||||||
int32_t count;
|
// int32_t count;
|
||||||
} uitextboxline_t;
|
// } uitextboxline_t;
|
||||||
|
|
||||||
typedef struct {
|
// typedef struct {
|
||||||
uiframe_t frame;
|
// uiframe_t frame;
|
||||||
|
|
||||||
font_t *font;
|
// font_t *font;
|
||||||
color_t textColor;
|
// color_t textColor;
|
||||||
|
|
||||||
float_t x, y, width, height;
|
// float_t x, y, width, height;
|
||||||
|
|
||||||
char_t text[UI_TEXTBOX_TEXT_MAX];
|
// char_t text[UI_TEXTBOX_TEXT_MAX];
|
||||||
|
|
||||||
uitextboxline_t lines[UI_TEXTBOX_LINES_MAX];
|
// uitextboxline_t lines[UI_TEXTBOX_LINES_MAX];
|
||||||
int32_t lineCount;
|
// int32_t lineCount;
|
||||||
int32_t charsPerLine;
|
// int32_t charsPerLine;
|
||||||
int32_t linesPerPage;
|
// int32_t linesPerPage;
|
||||||
int32_t pageCount;
|
// int32_t pageCount;
|
||||||
|
|
||||||
int32_t currentPage;
|
// int32_t currentPage;
|
||||||
int32_t scroll;
|
// int32_t scroll;
|
||||||
inputaction_t advanceAction;
|
// inputaction_t advanceAction;
|
||||||
|
|
||||||
eventcallback_t onPageCompleteCallbacks[4];
|
// eventcallback_t onPageCompleteCallbacks[4];
|
||||||
void *onPageCompleteUsers[4];
|
// void *onPageCompleteUsers[4];
|
||||||
event_t onPageComplete;
|
// event_t onPageComplete;
|
||||||
eventcallback_t onLastPageCallbacks[4];
|
// eventcallback_t onLastPageCallbacks[4];
|
||||||
void *onLastPageUsers[4];
|
// void *onLastPageUsers[4];
|
||||||
event_t onLastPage;
|
// event_t onLastPage;
|
||||||
} uitextbox_t;
|
// } uitextbox_t;
|
||||||
|
|
||||||
extern uitextbox_t UI_TEXTBOX;
|
// extern uitextbox_t UI_TEXTBOX;
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Initializes UI_TEXTBOX. Sets position and size from current screen
|
// * Initializes UI_TEXTBOX. Sets position and size from current screen
|
||||||
* dimensions and wires up the default font and frame textures.
|
// * dimensions and wires up the default font and frame textures.
|
||||||
* Call after displayInit().
|
// * Call after displayInit().
|
||||||
*
|
// *
|
||||||
* @return Any error that occurs.
|
// * @return Any error that occurs.
|
||||||
*/
|
// */
|
||||||
errorret_t uiTextboxInit(void);
|
// errorret_t uiTextboxInit(void);
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Rebuilds the word-wrap and page layout from the current text and dimensions.
|
// * Rebuilds the word-wrap and page layout from the current text and dimensions.
|
||||||
* Called automatically by uiTextboxSetText.
|
// * Called automatically by uiTextboxSetText.
|
||||||
*/
|
// */
|
||||||
void uiTextboxBuildLayout(void);
|
// void uiTextboxBuildLayout(void);
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Copies text into UI_TEXTBOX and rebuilds the word-wrap / page layout.
|
// * Copies text into UI_TEXTBOX and rebuilds the word-wrap / page layout.
|
||||||
* Resets currentPage and scroll to 0.
|
// * Resets currentPage and scroll to 0.
|
||||||
*
|
// *
|
||||||
* @param text Null-terminated source string.
|
// * @param text Null-terminated source string.
|
||||||
*/
|
// */
|
||||||
void uiTextboxSetText(const char_t *text);
|
// void uiTextboxSetText(const char_t *text);
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Advances the typewriter scroll by UI_TEXTBOX_SCROLL_CHARS_PER_TICK.
|
// * Advances the typewriter scroll by UI_TEXTBOX_SCROLL_CHARS_PER_TICK.
|
||||||
* Skipped on dynamic ticks when DUSK_TIME_DYNAMIC is defined.
|
// * Skipped on dynamic ticks when DUSK_TIME_DYNAMIC is defined.
|
||||||
*
|
// *
|
||||||
* @return Any error that occurs.
|
// * @return Any error that occurs.
|
||||||
*/
|
// */
|
||||||
errorret_t uiTextboxUpdate(void);
|
// errorret_t uiTextboxUpdate(void);
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Draws the frame and the currently visible text, including a final flush.
|
// * Draws the frame and the currently visible text, including a final flush.
|
||||||
*
|
// *
|
||||||
* @return Any error that occurs.
|
// * @return Any error that occurs.
|
||||||
*/
|
// */
|
||||||
errorret_t uiTextboxDraw(void);
|
// errorret_t uiTextboxDraw(void);
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Returns the total char count for the current page.
|
// * Returns the total char count for the current page.
|
||||||
*
|
// *
|
||||||
* @return Total chars on current page.
|
// * @return Total chars on current page.
|
||||||
*/
|
// */
|
||||||
int32_t uiTextboxGetPageCharCount(void);
|
// int32_t uiTextboxGetPageCharCount(void);
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Returns true when scroll has fully revealed the current page.
|
// * Returns true when scroll has fully revealed the current page.
|
||||||
*/
|
// */
|
||||||
bool_t uiTextboxPageIsComplete(void);
|
// bool_t uiTextboxPageIsComplete(void);
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Returns true when there is at least one more page after the current one.
|
// * Returns true when there is at least one more page after the current one.
|
||||||
*/
|
// */
|
||||||
bool_t uiTextboxHasNextPage(void);
|
// bool_t uiTextboxHasNextPage(void);
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Advances to the next page and resets scroll to 0.
|
// * Advances to the next page and resets scroll to 0.
|
||||||
* Has no effect if already on the last page.
|
// * Has no effect if already on the last page.
|
||||||
*/
|
// */
|
||||||
void uiTextboxNextPage(void);
|
// void uiTextboxNextPage(void);
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Disposes of UI_TEXTBOX, nulling out texture pointers.
|
// * Disposes of UI_TEXTBOX, nulling out texture pointers.
|
||||||
*
|
// *
|
||||||
* @return Any error that occurs.
|
// * @return Any error that occurs.
|
||||||
*/
|
// */
|
||||||
errorret_t uiTextboxDispose(void);
|
// errorret_t uiTextboxDispose(void);
|
||||||
|
|||||||
Reference in New Issue
Block a user