Compare commits
10 Commits
ac2
..
aa0180571e
| Author | SHA1 | Date | |
|---|---|---|---|
| aa0180571e | |||
| 7f7be39230 | |||
| 4d95415232 | |||
| 2cbd80a004 | |||
| 9abf8101da | |||
| 24badd06a5 | |||
| 7a03ef8eaf | |||
| f3ea507313 | |||
| 4b0388a0e1 | |||
| a84137b5ff |
@@ -33,74 +33,3 @@ jobs:
|
|||||||
libssl-dev
|
libssl-dev
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: ./scripts/test-linux.sh
|
run: ./scripts/test-linux.sh
|
||||||
|
|
||||||
# Emulator smoke tests: boot the built disc/EBOOT for a fixed window and
|
|
||||||
# confirm the emulator doesn't crash. Not yet verified against a real
|
|
||||||
# runner (no CI run has exercised these) -- continue-on-error so a
|
|
||||||
# flaky/broken emulator step doesn't block the required Linux test job.
|
|
||||||
run-tests-gamecube-dolphin:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
continue-on-error: true
|
|
||||||
container:
|
|
||||||
image: ghcr.io/extremscorner/libogc2:latest
|
|
||||||
steps:
|
|
||||||
- name: Install Node.js
|
|
||||||
run: apt-get update && apt-get install -y nodejs
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Install additional dependencies
|
|
||||||
run: |
|
|
||||||
apt-get install -y \
|
|
||||||
python3-pip python3-polib python3-pil \
|
|
||||||
python3-dotenv python3-pyqt5 python3-opengl xorriso \
|
|
||||||
dolphin-emu xvfb
|
|
||||||
dkp-pacman -Syu --noconfirm
|
|
||||||
dkp-pacman -S --needed --noconfirm \
|
|
||||||
gamecube-sdl2 ppc-liblzma ppc-libzip \
|
|
||||||
gamecube-tools ppc-libmad ppc-zlib-ng ppc-bzip2 ppc-zstd
|
|
||||||
- name: Build GameCube ISO and boot it in Dolphin
|
|
||||||
run: ./scripts/test-gamecube-dolphin.sh
|
|
||||||
|
|
||||||
run-tests-wii-dolphin:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
continue-on-error: true
|
|
||||||
container:
|
|
||||||
image: ghcr.io/extremscorner/libogc2:latest
|
|
||||||
steps:
|
|
||||||
- name: Install Node.js
|
|
||||||
run: apt-get update && apt-get install -y nodejs
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Install additional dependencies
|
|
||||||
run: |
|
|
||||||
apt-get install -y \
|
|
||||||
python3-pip python3-polib python3-pil \
|
|
||||||
python3-dotenv python3-pyqt5 python3-opengl xorriso \
|
|
||||||
dolphin-emu xvfb
|
|
||||||
dkp-pacman -Syu --noconfirm
|
|
||||||
dkp-pacman -S --needed --noconfirm \
|
|
||||||
gamecube-sdl2 ppc-liblzma ppc-libzip \
|
|
||||||
gamecube-tools ppc-libmad ppc-zlib-ng ppc-bzip2 ppc-zstd
|
|
||||||
- name: Build Wii ISO and boot it in Dolphin
|
|
||||||
run: ./scripts/test-wii-dolphin.sh
|
|
||||||
|
|
||||||
run-tests-psp-ppsspp:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
continue-on-error: true
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Setup pspdev
|
|
||||||
uses: ./.github/actions/setup-pspdev
|
|
||||||
- name: Install PPSSPPHeadless build dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y git cmake ninja-build libsdl2-dev zlib1g-dev
|
|
||||||
- name: Build PPSSPPHeadless
|
|
||||||
run: |
|
|
||||||
git clone --recursive --depth 1 https://github.com/hrydgard/ppsspp.git /tmp/ppsspp
|
|
||||||
cmake -S /tmp/ppsspp -B /tmp/ppsspp/build -DCMAKE_BUILD_TYPE=Release
|
|
||||||
cmake --build /tmp/ppsspp/build --target PPSSPPHeadless -- -j$(nproc)
|
|
||||||
echo "PPSSPP_HEADLESS_BIN=/tmp/ppsspp/build/PPSSPPHeadless" >> "$GITHUB_ENV"
|
|
||||||
- name: Build PSP EBOOT and boot it in PPSSPPHeadless
|
|
||||||
run: ./scripts/test-psp-ppsspp.sh
|
|
||||||
|
|||||||
+1
-12
@@ -13,7 +13,6 @@ cmake_policy(SET CMP0079 NEW)
|
|||||||
# set(FETCHCONTENT_UPDATES_DISCONNECTED ON)
|
# set(FETCHCONTENT_UPDATES_DISCONNECTED ON)
|
||||||
|
|
||||||
option(DUSK_BUILD_TESTS "Enable tests" OFF)
|
option(DUSK_BUILD_TESTS "Enable tests" OFF)
|
||||||
option(DUSK_NETWORKING "Enable networking support" OFF)
|
|
||||||
|
|
||||||
set(DUSK_GAME_NAME "Dusk" CACHE STRING "Game display name")
|
set(DUSK_GAME_NAME "Dusk" CACHE STRING "Game display name")
|
||||||
set(DUSK_GAME_AUTHOR "YourWishes" CACHE STRING "Game author / coder")
|
set(DUSK_GAME_AUTHOR "YourWishes" CACHE STRING "Game author / coder")
|
||||||
@@ -91,12 +90,6 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME}
|
|||||||
DUSK_VERSION="${DUSK_VERSION}"
|
DUSK_VERSION="${DUSK_VERSION}"
|
||||||
)
|
)
|
||||||
|
|
||||||
if(DUSK_NETWORKING)
|
|
||||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
|
||||||
DUSK_NETWORKING
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Toolchains
|
# Toolchains
|
||||||
include(cmake/targets/${DUSK_TARGET_SYSTEM}.cmake)
|
include(cmake/targets/${DUSK_TARGET_SYSTEM}.cmake)
|
||||||
|
|
||||||
@@ -123,11 +116,7 @@ if(DUSK_BUILD_TESTS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Build assets
|
# Build assets
|
||||||
# Deliberately not CONFIGURE_DEPENDS: that reruns the full CMake configure
|
file(GLOB_RECURSE DUSK_ASSET_FILES CONFIGURE_DEPENDS "${DUSK_ASSETS_DIR}/*")
|
||||||
# step (which invalidates generated headers and forces a huge rebuild) on
|
|
||||||
# every single asset edit. Re-run cmake manually when assets are added or
|
|
||||||
# removed.
|
|
||||||
file(GLOB_RECURSE DUSK_ASSET_FILES "${DUSK_ASSETS_DIR}/*")
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT "${DUSK_ASSETS_ZIP}"
|
OUTPUT "${DUSK_ASSETS_ZIP}"
|
||||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${DUSK_ASSETS_DIR}"
|
COMMAND ${CMAKE_COMMAND} -E make_directory "${DUSK_ASSETS_DIR}"
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
# Copyright (c) 2026 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
add_subdirectory(debug)
|
|
||||||
add_subdirectory(frame)
|
|
||||||
add_subdirectory(focus)
|
|
||||||
add_subdirectory(overlay)
|
|
||||||
add_subdirectory(transition)
|
|
||||||
add_subdirectory(widget)
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|
||||||
PUBLIC
|
|
||||||
ui.c
|
|
||||||
uielement.c
|
|
||||||
)
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "uiconsole.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "console/console.h"
|
|
||||||
#include "display/screen/screen.h"
|
|
||||||
#include "display/text/text.h"
|
|
||||||
#include "display/spritebatch/spritebatch.h"
|
|
||||||
#include "display/shader/shaderunlit.h"
|
|
||||||
#include "display/mesh/mesh.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
mesh_t mesh;
|
|
||||||
bool_t built;
|
|
||||||
meshvertex_t vertices[UI_CONSOLE_CACHE_VERTEX_MAX];
|
|
||||||
int32_t vertexCount;
|
|
||||||
int32_t cachedScanX;
|
|
||||||
int32_t cachedScanY;
|
|
||||||
} uiconsolecache_t;
|
|
||||||
|
|
||||||
uiconsolecache_t UI_CONSOLE_CACHE;
|
|
||||||
|
|
||||||
errorret_t uiConsoleDraw(void) {
|
|
||||||
if(!CONSOLE.visible) errorOk();
|
|
||||||
|
|
||||||
if(
|
|
||||||
CONSOLE.dirty ||
|
|
||||||
UI_CONSOLE_CACHE.cachedScanX != SCREEN.scanX ||
|
|
||||||
UI_CONSOLE_CACHE.cachedScanY != SCREEN.scanY
|
|
||||||
) {
|
|
||||||
errorChain(uiConsoleRebuild());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(UI_CONSOLE_CACHE.vertexCount == 0) errorOk();
|
|
||||||
|
|
||||||
shadermaterial_t material = {
|
|
||||||
.unlit = {
|
|
||||||
.color = COLOR_RED,
|
|
||||||
.texture = FONT_DEFAULT.texture
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
errorChain(shaderBind(&SHADER_UNLIT));
|
|
||||||
errorChain(shaderSetMaterial(&SHADER_UNLIT, &material));
|
|
||||||
return meshDraw(&UI_CONSOLE_CACHE.mesh, 0, UI_CONSOLE_CACHE.vertexCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t uiConsoleRebuild(void) {
|
|
||||||
int32_t spriteCount = 0;
|
|
||||||
for(uint32_t i = 0; i < CONSOLE_HISTORY_MAX; i++) {
|
|
||||||
char_t c;
|
|
||||||
int32_t j = 0;
|
|
||||||
while((c = CONSOLE.line[i][j++]) != '\0') {
|
|
||||||
if(c != ' ' && c != '\n') spriteCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assertTrue(
|
|
||||||
spriteCount <= UI_CONSOLE_CACHE_GLYPH_MAX,
|
|
||||||
"Console history exceeds fixed cache capacity"
|
|
||||||
);
|
|
||||||
|
|
||||||
const int32_t vertexCount = spriteCount * QUAD_VERTEX_COUNT;
|
|
||||||
UI_CONSOLE_CACHE.vertexCount = vertexCount;
|
|
||||||
|
|
||||||
if(vertexCount > 0) {
|
|
||||||
const float_t lineH = (float_t)FONT_DEFAULT.tileset->tileHeight;
|
|
||||||
const float_t charW = (float_t)FONT_DEFAULT.tileset->tileWidth;
|
|
||||||
|
|
||||||
int32_t index = 0;
|
|
||||||
int32_t row = 0;
|
|
||||||
for(uint32_t i = 0; i < CONSOLE_HISTORY_MAX; i++) {
|
|
||||||
float_t posX = (float_t)SCREEN.scanX;
|
|
||||||
float_t posY = (float_t)SCREEN.scanY + lineH * (float_t)row;
|
|
||||||
|
|
||||||
char_t c;
|
|
||||||
int32_t j = 0;
|
|
||||||
while((c = CONSOLE.line[i][j++]) != '\0') {
|
|
||||||
if(c == '\n') {
|
|
||||||
posX = (float_t)SCREEN.scanX;
|
|
||||||
posY += lineH;
|
|
||||||
row++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(c == ' ') {
|
|
||||||
posX += charW;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const spritebatchsprite_t sprite = textGetSprite(
|
|
||||||
(vec2){ posX, posY }, c, &FONT_DEFAULT
|
|
||||||
);
|
|
||||||
spriteBatchBufferToMesh(
|
|
||||||
&sprite, 1,
|
|
||||||
&UI_CONSOLE_CACHE.vertices[index], QUAD_VERTEX_COUNT
|
|
||||||
);
|
|
||||||
|
|
||||||
index += QUAD_VERTEX_COUNT;
|
|
||||||
posX += charW;
|
|
||||||
}
|
|
||||||
|
|
||||||
row++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!UI_CONSOLE_CACHE.built) {
|
|
||||||
errorChain(meshInit(
|
|
||||||
&UI_CONSOLE_CACHE.mesh,
|
|
||||||
QUAD_PRIMITIVE_TYPE,
|
|
||||||
UI_CONSOLE_CACHE_VERTEX_MAX,
|
|
||||||
UI_CONSOLE_CACHE.vertices
|
|
||||||
));
|
|
||||||
UI_CONSOLE_CACHE.built = true;
|
|
||||||
} else {
|
|
||||||
errorChain(meshFlush(&UI_CONSOLE_CACHE.mesh, 0, vertexCount));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UI_CONSOLE_CACHE.cachedScanX = SCREEN.scanX;
|
|
||||||
UI_CONSOLE_CACHE.cachedScanY = SCREEN.scanY;
|
|
||||||
CONSOLE.dirty = false;
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t uiConsoleDispose(void) {
|
|
||||||
if(UI_CONSOLE_CACHE.built) {
|
|
||||||
errorChain(meshDispose(&UI_CONSOLE_CACHE.mesh));
|
|
||||||
}
|
|
||||||
|
|
||||||
memoryZero(&UI_CONSOLE_CACHE, sizeof(uiconsolecache_t));
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "error/error.h"
|
|
||||||
#include "display/mesh/quad.h"
|
|
||||||
|
|
||||||
// Fixed capacity for the console's cached glyph mesh, in glyphs -- the
|
|
||||||
// mesh/vertex buffer is sized to this once and never grown/shrunk, so
|
|
||||||
// history producing more non-space characters than this asserts (see
|
|
||||||
// uiConsoleRebuild) instead of reallocating.
|
|
||||||
#define UI_CONSOLE_CACHE_GLYPH_MAX 512
|
|
||||||
#define UI_CONSOLE_CACHE_VERTEX_MAX (\
|
|
||||||
UI_CONSOLE_CACHE_GLYPH_MAX * QUAD_VERTEX_COUNT \
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders the console history into the scan-safe area, drawing a mesh
|
|
||||||
* that is only rebuilt when the history changes or the scan-safe origin
|
|
||||||
* moves, rather than rebuffering vertices every frame. No-ops when the
|
|
||||||
* console is not visible.
|
|
||||||
*
|
|
||||||
* @return Any error that occurs.
|
|
||||||
*/
|
|
||||||
errorret_t uiConsoleDraw(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rebuilds the cached mesh for the console's current history and
|
|
||||||
* scan-safe origin. Called automatically by uiConsoleDraw() whenever
|
|
||||||
* needed; only needs calling directly to force an immediate rebuild.
|
|
||||||
* The underlying vertex buffer is a fixed-size array (see
|
|
||||||
* UI_CONSOLE_CACHE_VERTEX_MAX) -- asserts if the history produces more
|
|
||||||
* glyphs than that capacity, rather than growing it.
|
|
||||||
*
|
|
||||||
* @return Any error that occurs.
|
|
||||||
*/
|
|
||||||
errorret_t uiConsoleRebuild(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Frees the mesh and vertex buffer built up by uiConsoleDraw().
|
|
||||||
*
|
|
||||||
* @return Any error that occurs.
|
|
||||||
*/
|
|
||||||
errorret_t uiConsoleDispose(void);
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "error/error.h"
|
|
||||||
#include "display/texture/texture.h"
|
|
||||||
#include "display/texture/tileset.h"
|
|
||||||
#include "display/spritebatch/spritebatchsprite.h"
|
|
||||||
|
|
||||||
#define UI_FRAME_BORDER_WIDTH 6
|
|
||||||
#define UI_FRAME_BORDER_HEIGHT 6
|
|
||||||
#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
|
|
||||||
#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[UI_FRAME_BORDER_WIDTH * UI_FRAME_BORDER_HEIGHT];
|
|
||||||
} uiframe_t;
|
|
||||||
|
|
||||||
extern uiframe_t UI_FRAME;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* @param x Screen x position.
|
|
||||||
* @param y Screen y position.
|
|
||||||
* @param width Total width of the frame.
|
|
||||||
* @param height Total height of the frame.
|
|
||||||
* @return Any error that occurs.
|
|
||||||
*/
|
|
||||||
errorret_t uiFrameDraw(
|
|
||||||
const float_t x,
|
|
||||||
const float_t y,
|
|
||||||
const float_t width,
|
|
||||||
const float_t height
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds the 9 sprites for a 9-slice frame at the given rect. Used
|
|
||||||
* internally by uiFrameDraw and uiFrameDrawCached -- call directly only
|
|
||||||
* if you need the raw sprites instead of buffering them.
|
|
||||||
*
|
|
||||||
* @param sprites Destination array of exactly 9 sprites.
|
|
||||||
* @param x Screen x position.
|
|
||||||
* @param y Screen y position.
|
|
||||||
* @param width Total width of the frame.
|
|
||||||
* @param height Total height of the frame.
|
|
||||||
*/
|
|
||||||
void uiFrameBuildSprites(
|
|
||||||
spritebatchsprite_t sprites[9],
|
|
||||||
const float_t x,
|
|
||||||
const float_t y,
|
|
||||||
const float_t width,
|
|
||||||
const float_t height
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A frame's cached 9-slice sprites, plus the rect they were built for.
|
|
||||||
* Owned by whichever widget/screen draws a frame repeatedly (dialogs,
|
|
||||||
* textboxes, settings panels) so uiFrameDrawCached can skip rebuilding
|
|
||||||
* the sprites when the rect hasn't moved since the last draw.
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
spritebatchsprite_t sprites[9];
|
|
||||||
bool_t built;
|
|
||||||
float_t lastX;
|
|
||||||
float_t lastY;
|
|
||||||
float_t lastWidth;
|
|
||||||
float_t lastHeight;
|
|
||||||
} uiframecache_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws a 9-slice frame using a caller-owned cache, only rebuilding the
|
|
||||||
* sprites (via uiFrameBuildSprites) when x/y/width/height differ from
|
|
||||||
* the last call -- unlike uiFrameDraw, which rebuilds unconditionally
|
|
||||||
* every time. Prefer this for frames redrawn every frame at a fixed or
|
|
||||||
* rarely-changing rect (dialogs, panels); use plain uiFrameDraw for
|
|
||||||
* genuinely one-off or per-frame-varying rects.
|
|
||||||
*
|
|
||||||
* @param cache Caller-owned cache, zero-initialized before first use.
|
|
||||||
* @param x Screen x position.
|
|
||||||
* @param y Screen y position.
|
|
||||||
* @param width Total width of the frame.
|
|
||||||
* @param height Total height of the frame.
|
|
||||||
* @return Any error that occurs.
|
|
||||||
*/
|
|
||||||
errorret_t uiFrameDrawCached(
|
|
||||||
uiframecache_t *cache,
|
|
||||||
const float_t x,
|
|
||||||
const float_t y,
|
|
||||||
const float_t width,
|
|
||||||
const float_t height
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes of the global UI_FRAME, releasing its GPU texture.
|
|
||||||
*
|
|
||||||
* @return Any error that occurs.
|
|
||||||
*/
|
|
||||||
errorret_t uiFrameDispose(void);
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ui.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "display/spritebatch/spritebatch.h"
|
|
||||||
#include "display/screen/screen.h"
|
|
||||||
#include "ui/uielement.h"
|
|
||||||
#include "ui/focus/uifocus.h"
|
|
||||||
|
|
||||||
ui_t UI;
|
|
||||||
|
|
||||||
errorret_t uiInit(void) {
|
|
||||||
memoryZero(&UI, sizeof(ui_t));
|
|
||||||
uiFocusInit();
|
|
||||||
uiElementsSort();
|
|
||||||
|
|
||||||
uielement_t *element = &UI_ELEMENTS[0];
|
|
||||||
while(!uiElementIsNull(element)) {
|
|
||||||
errorChain(uiElementInit(element));
|
|
||||||
element++;
|
|
||||||
}
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t uiUpdate(void) {
|
|
||||||
uiFocusUpdate();
|
|
||||||
|
|
||||||
uielement_t *element = &UI_ELEMENTS[0];
|
|
||||||
while(!uiElementIsNull(element)) {
|
|
||||||
errorChain(uiElementUpdate(element));
|
|
||||||
element++;
|
|
||||||
}
|
|
||||||
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t uiRender(void) {
|
|
||||||
const uielement_t *element = &UI_ELEMENTS[0];
|
|
||||||
while(!uiElementIsNull(element)) {
|
|
||||||
errorChain(uiElementDraw(element));
|
|
||||||
element++;
|
|
||||||
}
|
|
||||||
|
|
||||||
errorChain(spriteBatchFlush());
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t uiDispose(void) {
|
|
||||||
uielement_t *element = &UI_ELEMENTS[0];
|
|
||||||
while(!uiElementIsNull(element)) {
|
|
||||||
errorChain(uiElementDispose(element));
|
|
||||||
element++;
|
|
||||||
}
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 {
|
|
||||||
void *nothing;
|
|
||||||
} ui_t;
|
|
||||||
|
|
||||||
extern ui_t UI;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the UI system.
|
|
||||||
*/
|
|
||||||
errorret_t uiInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the UI system.
|
|
||||||
*
|
|
||||||
* @return Any error that occurs.
|
|
||||||
*/
|
|
||||||
errorret_t uiUpdate(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders the UI system.
|
|
||||||
*
|
|
||||||
* @return Any error that occurs.
|
|
||||||
*/
|
|
||||||
errorret_t uiRender(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes of the UI system.
|
|
||||||
*
|
|
||||||
* @return Any error that occurs.
|
|
||||||
*/
|
|
||||||
errorret_t uiDispose(void);
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "uielement.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "util/sort.h"
|
|
||||||
#include "ui/frame/uiframe.h"
|
|
||||||
#include "ui/debug/uifps.h"
|
|
||||||
#include "engine/engine.h"
|
|
||||||
#include "ui/overlay/uifullbox.h"
|
|
||||||
#include "ui/overlay/uiloading.h"
|
|
||||||
#include "ui/overlay/uicrop.h"
|
|
||||||
#include "ui/transition/uitransition.h"
|
|
||||||
#include "ui/debug/uiconsole.h"
|
|
||||||
#include "ui/frame/uiconfirm.h"
|
|
||||||
|
|
||||||
// Priming pass: X does nothing here, so this only exists to process any
|
|
||||||
// #include directives nested in uielementlist.h/ui/uielementgame.h at
|
|
||||||
// file scope (each such header's own #pragma once makes it a no-op on
|
|
||||||
// the real pass below, which happens inside UI_ELEMENTS[]'s braces,
|
|
||||||
// where a raw #include of a declaration would be invalid).
|
|
||||||
#define X(initFn, updateFn, drawFn, disposeFn, order) // do nothing
|
|
||||||
#include "uielementlist.h"
|
|
||||||
#undef X
|
|
||||||
|
|
||||||
uielement_t UI_ELEMENTS[] = {
|
|
||||||
#define X(initFn, updateFn, drawFn, disposeFn, elementOrder) \
|
|
||||||
{ \
|
|
||||||
.init = initFn, .update = updateFn, .draw = drawFn, \
|
|
||||||
.dispose = disposeFn, .order = elementOrder \
|
|
||||||
},
|
|
||||||
#include "uielementlist.h"
|
|
||||||
#undef X
|
|
||||||
|
|
||||||
{ 0 } // Null terminator
|
|
||||||
};
|
|
||||||
|
|
||||||
bool_t uiElementIsNull(const uielement_t *element) {
|
|
||||||
return element->init == NULL &&
|
|
||||||
element->update == NULL &&
|
|
||||||
element->draw == NULL &&
|
|
||||||
element->dispose == NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int_t uiElementCompareOrder(const void *a, const void *b) {
|
|
||||||
const uielement_t *elementA = (const uielement_t *)a;
|
|
||||||
const uielement_t *elementB = (const uielement_t *)b;
|
|
||||||
if(elementA->order < elementB->order) return -1;
|
|
||||||
if(elementA->order > elementB->order) return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiElementsSort(void) {
|
|
||||||
// The trailing null terminator is always the last static entry -- sort
|
|
||||||
// everything before it, and leave it in place.
|
|
||||||
const size_t count = (sizeof(UI_ELEMENTS) / sizeof(UI_ELEMENTS[0])) - 1;
|
|
||||||
sortBubble(UI_ELEMENTS, count, sizeof(uielement_t), uiElementCompareOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t uiElementInit(uielement_t *element) {
|
|
||||||
assertNotNull(element, "element must not be NULL");
|
|
||||||
if(element->init != NULL) errorChain(element->init());
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t uiElementUpdate(uielement_t *element) {
|
|
||||||
assertNotNull(element, "element must not be NULL");
|
|
||||||
if(element->update != NULL) errorChain(element->update());
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t uiElementDraw(const uielement_t *element) {
|
|
||||||
assertNotNull(element, "element must not be NULL");
|
|
||||||
if(element->draw != NULL) errorChain(element->draw());
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t uiElementDispose(uielement_t *element) {
|
|
||||||
assertNotNull(element, "element must not be NULL");
|
|
||||||
if(element->dispose != NULL) errorChain(element->dispose());
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "error/error.h"
|
|
||||||
|
|
||||||
// Built-in order tiers. Lower values update/render first; ties preserve
|
|
||||||
// their relative UI_ELEMENTS declaration order (uiElementsSort uses a
|
|
||||||
// stable sort). Game-specific elements can use any int32_t value -- these
|
|
||||||
// are just the ones the engine itself relies on.
|
|
||||||
#define UI_ELEMENT_ORDER_DEFAULT 0
|
|
||||||
#define UI_ELEMENT_ORDER_DEBUG 1000
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
errorret_t (*init)();
|
|
||||||
errorret_t (*update)();
|
|
||||||
errorret_t (*draw)();
|
|
||||||
errorret_t (*dispose)();
|
|
||||||
int32_t order;
|
|
||||||
} uielement_t;
|
|
||||||
|
|
||||||
extern uielement_t UI_ELEMENTS[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares two elements by their .order field, ascending. Matches
|
|
||||||
* sortcompare_t, for use with the project's sort utilities.
|
|
||||||
*
|
|
||||||
* @param a First uielement_t to compare.
|
|
||||||
* @param b Second uielement_t to compare.
|
|
||||||
* @return Negative if a < b, zero if a == b, positive if a > b.
|
|
||||||
*/
|
|
||||||
int_t uiElementCompareOrder(const void *a, const void *b);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stably sorts UI_ELEMENTS in place by .order, ascending. The trailing
|
|
||||||
* null terminator is never moved. Called once by uiInit -- element order
|
|
||||||
* is static after that, so there's no need to re-sort every frame.
|
|
||||||
*/
|
|
||||||
void uiElementsSort(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes a UI element, invoking its init callback if set.
|
|
||||||
*
|
|
||||||
* @param element The element to initialize.
|
|
||||||
* @return Any error that occurs.
|
|
||||||
*/
|
|
||||||
errorret_t uiElementInit(uielement_t *element);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates a UI element, calling its update callback if set.
|
|
||||||
*
|
|
||||||
* @param element The element to update.
|
|
||||||
* @return Any error that occurs.
|
|
||||||
*/
|
|
||||||
errorret_t uiElementUpdate(uielement_t *element);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws a UI element, calling its draw callback if set.
|
|
||||||
*
|
|
||||||
* @param element The element to render.
|
|
||||||
* @return Any error that occurs.
|
|
||||||
*/
|
|
||||||
errorret_t uiElementDraw(const uielement_t *element);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes of a UI element, invoking its dispose callback if set.
|
|
||||||
*
|
|
||||||
* @param element The element to dispose.
|
|
||||||
* @return Any error that occurs.
|
|
||||||
*/
|
|
||||||
errorret_t uiElementDispose(uielement_t *element);
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
// X(init, update, draw, dispose, order) -- pass NULL for any callback the
|
|
||||||
// element doesn't need. order controls update/draw sequence (lower first,
|
|
||||||
// see UI_ELEMENT_ORDER_* in uielement.h); ties preserve declaration order.
|
|
||||||
// See uielement.c for how this expands.
|
|
||||||
|
|
||||||
X(uiFrameInit, NULL, NULL, uiFrameDispose, UI_ELEMENT_ORDER_DEFAULT)
|
|
||||||
|
|
||||||
// Fullbox under: above scene, below system UI.
|
|
||||||
X(
|
|
||||||
uiFullboxUnderInit, uiFullboxUnderUpdate, uiFullboxUnderDraw, NULL,
|
|
||||||
UI_ELEMENT_ORDER_DEFAULT
|
|
||||||
)
|
|
||||||
|
|
||||||
// Text stuffs
|
|
||||||
X(uiConfirmInit, NULL, uiConfirmDraw, uiConfirmDispose, UI_ELEMENT_ORDER_DEFAULT)
|
|
||||||
|
|
||||||
X(
|
|
||||||
uiTransitionInit, uiTransitionUpdate, uiTransitionDraw, NULL,
|
|
||||||
UI_ELEMENT_ORDER_DEFAULT
|
|
||||||
)
|
|
||||||
|
|
||||||
// Fullbox over: above absolutely everything (except debug).
|
|
||||||
X(
|
|
||||||
uiFullboxOverInit, uiFullboxOverUpdate, uiFullboxOverDraw, NULL,
|
|
||||||
UI_ELEMENT_ORDER_DEFAULT
|
|
||||||
)
|
|
||||||
|
|
||||||
X(
|
|
||||||
uiLoadingInit, uiLoadingUpdate, uiLoadingDraw, NULL,
|
|
||||||
UI_ELEMENT_ORDER_DEFAULT
|
|
||||||
)
|
|
||||||
|
|
||||||
X(uiCropInit, NULL, uiCropDraw, NULL, UI_ELEMENT_ORDER_DEFAULT)
|
|
||||||
|
|
||||||
// Debug items -- always last.
|
|
||||||
X(NULL, NULL, uiConsoleDraw, uiConsoleDispose, UI_ELEMENT_ORDER_DEBUG)
|
|
||||||
X(uiFPSInit, NULL, uiFPSDraw, NULL, UI_ELEMENT_ORDER_DEBUG)
|
|
||||||
|
|
||||||
// Game-specific UI elements (duskrpg)
|
|
||||||
#include "ui/uielementgame.h"
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# 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
|
|
||||||
uibutton.c
|
|
||||||
uicheckbox.c
|
|
||||||
uitab.c
|
|
||||||
uislider.c
|
|
||||||
uidropdown.c
|
|
||||||
uiscrolling.c
|
|
||||||
uimenu.c
|
|
||||||
uilabel.c
|
|
||||||
uiwidgetlabel.c
|
|
||||||
)
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "uilabel.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "util/string.h"
|
|
||||||
#include "display/text/text.h"
|
|
||||||
|
|
||||||
void uiLabelInit(uilabel_t *label, font_t *font) {
|
|
||||||
assertNotNull(label, "Label cannot be NULL");
|
|
||||||
assertNotNull(font, "Font cannot be NULL");
|
|
||||||
|
|
||||||
memoryZero(label, sizeof(uilabel_t));
|
|
||||||
label->font = font;
|
|
||||||
label->color = COLOR_WHITE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiLabelSetText(uilabel_t *label, const char_t *text) {
|
|
||||||
assertNotNull(label, "Label cannot be NULL");
|
|
||||||
assertNotNull(text, "Text cannot be NULL");
|
|
||||||
assertStrLenMax(text, UI_LABEL_TEXT_MAX, "Label text too long");
|
|
||||||
|
|
||||||
stringCopy(label->text, text, UI_LABEL_TEXT_MAX);
|
|
||||||
label->spriteCount = textBuildSpriteCache(
|
|
||||||
label->text, label->font, label->sprites, UI_LABEL_SPRITE_COUNT_MAX,
|
|
||||||
&label->width, &label->height
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiLabelSetColor(uilabel_t *label, const color_t color) {
|
|
||||||
assertNotNull(label, "Label cannot be NULL");
|
|
||||||
label->color = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiLabelGetSize(
|
|
||||||
const uilabel_t *label,
|
|
||||||
int32_t *outWidth,
|
|
||||||
int32_t *outHeight
|
|
||||||
) {
|
|
||||||
assertNotNull(label, "Label cannot be NULL");
|
|
||||||
assertNotNull(outWidth, "Output width cannot be NULL");
|
|
||||||
assertNotNull(outHeight, "Output height cannot be NULL");
|
|
||||||
*outWidth = label->width;
|
|
||||||
*outHeight = label->height;
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t uiLabelDraw(
|
|
||||||
const uilabel_t *label,
|
|
||||||
const float_t x,
|
|
||||||
const float_t y
|
|
||||||
) {
|
|
||||||
assertNotNull(label, "Label cannot be NULL");
|
|
||||||
if(label->spriteCount == 0) errorOk();
|
|
||||||
|
|
||||||
// Cached sprites are relative to (0,0); textDrawSpriteCache translates
|
|
||||||
// into the requested screen position here instead of in
|
|
||||||
// uiLabelSetText, so a label can be repositioned every frame without
|
|
||||||
// rebuilding the (much more expensive) glyph/UV cache.
|
|
||||||
spritebatchsprite_t scratch[UI_LABEL_SPRITE_COUNT_MAX];
|
|
||||||
errorChain(textDrawSpriteCache(
|
|
||||||
label->sprites, label->spriteCount, scratch, x, y, label->color,
|
|
||||||
label->font->texture
|
|
||||||
));
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "error/error.h"
|
|
||||||
#include "display/text/font.h"
|
|
||||||
#include "display/spritebatch/spritebatchsprite.h"
|
|
||||||
#include "display/color.h"
|
|
||||||
|
|
||||||
#define UI_LABEL_TEXT_MAX 256
|
|
||||||
#define UI_LABEL_SPRITE_COUNT_MAX UI_LABEL_TEXT_MAX
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char_t text[UI_LABEL_TEXT_MAX];
|
|
||||||
color_t color;
|
|
||||||
font_t *font;
|
|
||||||
|
|
||||||
spritebatchsprite_t sprites[UI_LABEL_SPRITE_COUNT_MAX];
|
|
||||||
uint32_t spriteCount;
|
|
||||||
int32_t width;
|
|
||||||
int32_t height;
|
|
||||||
} uilabel_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes a label, defaulting to white text and no text set.
|
|
||||||
*
|
|
||||||
* @param label The label to initialize.
|
|
||||||
* @param font Font to use for rendering. Must outlive the label.
|
|
||||||
*/
|
|
||||||
void uiLabelInit(uilabel_t *label, font_t *font);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the label's text, rebuilding its cached sprites (glyph lookup, UV,
|
|
||||||
* and layout) immediately. This is the only way the sprite cache gets
|
|
||||||
* rebuilt -- uiLabelDraw never recomputes it, so call this whenever the
|
|
||||||
* text changes rather than once up front and expecting it to stay in sync.
|
|
||||||
*
|
|
||||||
* @param label The label to update.
|
|
||||||
* @param text Null-terminated string to display. Must be shorter than
|
|
||||||
* UI_LABEL_TEXT_MAX.
|
|
||||||
*/
|
|
||||||
void uiLabelSetText(uilabel_t *label, const char_t *text);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the label's tint color. Cheap -- doesn't touch the sprite cache,
|
|
||||||
* since color is applied via the draw material, not baked into sprites.
|
|
||||||
*
|
|
||||||
* @param label The label to update.
|
|
||||||
* @param color The new tint color.
|
|
||||||
*/
|
|
||||||
void uiLabelSetColor(uilabel_t *label, const color_t color);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the measured size (in pixels) of the label's current text, cached
|
|
||||||
* from the last uiLabelSetText call.
|
|
||||||
*
|
|
||||||
* @param label The label to query.
|
|
||||||
* @param outWidth Pointer to store the width.
|
|
||||||
* @param outHeight Pointer to store the height.
|
|
||||||
*/
|
|
||||||
void uiLabelGetSize(
|
|
||||||
const uilabel_t *label,
|
|
||||||
int32_t *outWidth,
|
|
||||||
int32_t *outHeight
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws the label's cached sprites at the given screen position in a
|
|
||||||
* single batched buffer call. Does not recompute glyph layout -- call
|
|
||||||
* uiLabelSetText first whenever the text changes.
|
|
||||||
*
|
|
||||||
* @param label The label to draw.
|
|
||||||
* @param x Screen x position.
|
|
||||||
* @param y Screen y position.
|
|
||||||
* @return Any error that occurs.
|
|
||||||
*/
|
|
||||||
errorret_t uiLabelDraw(
|
|
||||||
const uilabel_t *label,
|
|
||||||
const float_t x,
|
|
||||||
const float_t y
|
|
||||||
);
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "uiwidgetlabel.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "util/string.h"
|
|
||||||
#include "display/text/text.h"
|
|
||||||
|
|
||||||
void uiWidgetLabelInit(uiwidgetlabel_t *label, font_t *font) {
|
|
||||||
assertNotNull(label, "Label cannot be NULL");
|
|
||||||
assertNotNull(font, "Font cannot be NULL");
|
|
||||||
|
|
||||||
memoryZero(label, sizeof(uiwidgetlabel_t));
|
|
||||||
label->font = font;
|
|
||||||
label->color = COLOR_WHITE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiWidgetLabelSetText(uiwidgetlabel_t *label, const char_t *text) {
|
|
||||||
assertNotNull(label, "Label cannot be NULL");
|
|
||||||
assertNotNull(text, "Text cannot be NULL");
|
|
||||||
assertStrLenMax(text, UI_WIDGET_LABEL_TEXT_MAX, "Label text too long");
|
|
||||||
|
|
||||||
stringCopy(label->text, text, UI_WIDGET_LABEL_TEXT_MAX);
|
|
||||||
label->spriteCount = textBuildSpriteCache(
|
|
||||||
label->text, label->font, label->sprites,
|
|
||||||
UI_WIDGET_LABEL_SPRITE_COUNT_MAX, &label->width, &label->height
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiWidgetLabelSetColor(uiwidgetlabel_t *label, const color_t color) {
|
|
||||||
assertNotNull(label, "Label cannot be NULL");
|
|
||||||
label->color = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiWidgetLabelGetSize(
|
|
||||||
const uiwidgetlabel_t *label,
|
|
||||||
int32_t *outWidth,
|
|
||||||
int32_t *outHeight
|
|
||||||
) {
|
|
||||||
assertNotNull(label, "Label cannot be NULL");
|
|
||||||
assertNotNull(outWidth, "Output width cannot be NULL");
|
|
||||||
assertNotNull(outHeight, "Output height cannot be NULL");
|
|
||||||
*outWidth = label->width;
|
|
||||||
*outHeight = label->height;
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t uiWidgetLabelDraw(
|
|
||||||
const uiwidgetlabel_t *label,
|
|
||||||
const float_t x,
|
|
||||||
const float_t y
|
|
||||||
) {
|
|
||||||
assertNotNull(label, "Label cannot be NULL");
|
|
||||||
if(label->spriteCount == 0) errorOk();
|
|
||||||
|
|
||||||
spritebatchsprite_t scratch[UI_WIDGET_LABEL_SPRITE_COUNT_MAX];
|
|
||||||
errorChain(textDrawSpriteCache(
|
|
||||||
label->sprites, label->spriteCount, scratch, x, y, label->color,
|
|
||||||
label->font->texture
|
|
||||||
));
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "error/error.h"
|
|
||||||
#include "display/text/font.h"
|
|
||||||
#include "display/spritebatch/spritebatchsprite.h"
|
|
||||||
#include "display/color.h"
|
|
||||||
|
|
||||||
// A cached, cheap-to-redraw label sized for embedding directly inside
|
|
||||||
// small widgets (buttons, checkboxes, sliders, ...) rather than
|
|
||||||
// referencing text by pointer and re-deriving glyph geometry every
|
|
||||||
// frame. Capacity is intentionally much smaller than uilabel_t's -- it
|
|
||||||
// gets embedded by value in every widget instance (and inside
|
|
||||||
// uimenuitem_t's union, which is often arrayed), so keeping it small
|
|
||||||
// matters on memory-constrained targets. See uilabel_t for the
|
|
||||||
// general-purpose, larger-capacity equivalent.
|
|
||||||
#define UI_WIDGET_LABEL_TEXT_MAX 64
|
|
||||||
#define UI_WIDGET_LABEL_SPRITE_COUNT_MAX UI_WIDGET_LABEL_TEXT_MAX
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char_t text[UI_WIDGET_LABEL_TEXT_MAX];
|
|
||||||
color_t color;
|
|
||||||
font_t *font;
|
|
||||||
|
|
||||||
spritebatchsprite_t sprites[UI_WIDGET_LABEL_SPRITE_COUNT_MAX];
|
|
||||||
uint32_t spriteCount;
|
|
||||||
int32_t width;
|
|
||||||
int32_t height;
|
|
||||||
} uiwidgetlabel_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes a widget label, defaulting to white text and no text set.
|
|
||||||
*
|
|
||||||
* @param label The label to initialize.
|
|
||||||
* @param font Font to use for rendering. Must outlive the label.
|
|
||||||
*/
|
|
||||||
void uiWidgetLabelInit(uiwidgetlabel_t *label, font_t *font);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the label's text, rebuilding its cached sprites immediately.
|
|
||||||
* Call this whenever the displayed text changes -- uiWidgetLabelDraw
|
|
||||||
* never recomputes it.
|
|
||||||
*
|
|
||||||
* @param label The label to update.
|
|
||||||
* @param text Null-terminated string to display. Must be shorter than
|
|
||||||
* UI_WIDGET_LABEL_TEXT_MAX.
|
|
||||||
*/
|
|
||||||
void uiWidgetLabelSetText(uiwidgetlabel_t *label, const char_t *text);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the label's tint color. Cheap -- doesn't touch the sprite cache.
|
|
||||||
*
|
|
||||||
* @param label The label to update.
|
|
||||||
* @param color The new tint color.
|
|
||||||
*/
|
|
||||||
void uiWidgetLabelSetColor(uiwidgetlabel_t *label, const color_t color);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the measured size (in pixels) of the label's current text, cached
|
|
||||||
* from the last uiWidgetLabelSetText call.
|
|
||||||
*
|
|
||||||
* @param label The label to query.
|
|
||||||
* @param outWidth Pointer to store the width.
|
|
||||||
* @param outHeight Pointer to store the height.
|
|
||||||
*/
|
|
||||||
void uiWidgetLabelGetSize(
|
|
||||||
const uiwidgetlabel_t *label,
|
|
||||||
int32_t *outWidth,
|
|
||||||
int32_t *outHeight
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws the label's cached sprites at the given screen position in a
|
|
||||||
* single batched buffer call. Does not recompute glyph layout.
|
|
||||||
*
|
|
||||||
* @param label The label to draw.
|
|
||||||
* @param x Screen x position.
|
|
||||||
* @param y Screen y position.
|
|
||||||
* @return Any error that occurs.
|
|
||||||
*/
|
|
||||||
errorret_t uiWidgetLabelDraw(
|
|
||||||
const uiwidgetlabel_t *label,
|
|
||||||
const float_t x,
|
|
||||||
const float_t y
|
|
||||||
);
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "uitestlabel.h"
|
|
||||||
#include "ui/widget/uilabel.h"
|
|
||||||
#include "display/text/text.h"
|
|
||||||
#include "display/screen/screen.h"
|
|
||||||
#include "engine/engine.h"
|
|
||||||
|
|
||||||
uilabel_t UI_TEST_LABEL;
|
|
||||||
|
|
||||||
errorret_t uiTestLabelInit() {
|
|
||||||
uiLabelInit(&UI_TEST_LABEL, &FONT_DEFAULT);
|
|
||||||
uiLabelSetText(&UI_TEST_LABEL, "duskrpg");
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t uiTestLabelDraw() {
|
|
||||||
int32_t versionWidth, versionHeight;
|
|
||||||
textMeasure(ENGINE.version, &FONT_DEFAULT, &versionWidth, &versionHeight);
|
|
||||||
|
|
||||||
errorChain(uiLabelDraw(
|
|
||||||
&UI_TEST_LABEL,
|
|
||||||
(float_t)(SCREEN.scanX + SCREEN.scanWidth - UI_TEST_LABEL.width),
|
|
||||||
(float_t)(
|
|
||||||
SCREEN.scanY + SCREEN.scanHeight - versionHeight -
|
|
||||||
UI_TEST_LABEL.height
|
|
||||||
)
|
|
||||||
));
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "error/error.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the test label, a smoke test for the duskrpg-side UI
|
|
||||||
* element extension point (uielementgame.h).
|
|
||||||
*
|
|
||||||
* @return Any error that occurs.
|
|
||||||
*/
|
|
||||||
errorret_t uiTestLabelInit();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws the test label above the engine's debug version number.
|
|
||||||
*
|
|
||||||
* @return Any error that occurs.
|
|
||||||
*/
|
|
||||||
errorret_t uiTestLabelDraw();
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
# Copyright (c) 2026 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
include(dusktest)
|
|
||||||
|
|
||||||
# Tests
|
|
||||||
dusktest(test_uiframe.c)
|
|
||||||
dusktest(test_uislider.c)
|
|
||||||
dusktest(test_uitab.c)
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dusktest.h"
|
|
||||||
#include "ui/frame/uiframe.h"
|
|
||||||
|
|
||||||
static void test_uiFrameBuildSprites_layout(void **state) {
|
|
||||||
UI_FRAME.tileset = (tileset_t){
|
|
||||||
.tileWidth = 1,
|
|
||||||
.tileHeight = 1,
|
|
||||||
.tileCount = 9,
|
|
||||||
.columns = 3,
|
|
||||||
.rows = 3,
|
|
||||||
.uv = { 0.25f, 0.25f }
|
|
||||||
};
|
|
||||||
|
|
||||||
const float_t x = 100.0f;
|
|
||||||
const float_t y = 50.0f;
|
|
||||||
const float_t width = 40.0f;
|
|
||||||
const float_t height = 30.0f;
|
|
||||||
const float_t tileW = (float_t)UI_FRAME_BORDER_WIDTH;
|
|
||||||
const float_t tileH = (float_t)UI_FRAME_BORDER_HEIGHT;
|
|
||||||
|
|
||||||
spritebatchsprite_t sprites[9];
|
|
||||||
uiFrameBuildSprites(sprites, x, y, width, height);
|
|
||||||
|
|
||||||
// Top-left corner sits exactly at the rect's origin.
|
|
||||||
assert_float_equal(sprites[0].min[0], x, 0.0001f);
|
|
||||||
assert_float_equal(sprites[0].min[1], y, 0.0001f);
|
|
||||||
assert_float_equal(sprites[0].max[0], x + tileW, 0.0001f);
|
|
||||||
assert_float_equal(sprites[0].max[1], y + tileH, 0.0001f);
|
|
||||||
assert_float_equal(sprites[0].uvMin[0], 0.0f, 0.0001f);
|
|
||||||
assert_float_equal(sprites[0].uvMin[1], 0.0f, 0.0001f);
|
|
||||||
assert_float_equal(sprites[0].uvMax[0], 0.25f, 0.0001f);
|
|
||||||
assert_float_equal(sprites[0].uvMax[1], 0.25f, 0.0001f);
|
|
||||||
|
|
||||||
// Top-middle edge stretches to fill the width minus both corners.
|
|
||||||
assert_float_equal(sprites[1].min[0], x + tileW, 0.0001f);
|
|
||||||
assert_float_equal(sprites[1].max[0], x + width - tileW, 0.0001f);
|
|
||||||
assert_float_equal(sprites[1].max[1], y + tileH, 0.0001f);
|
|
||||||
|
|
||||||
// Center tile fills the remaining interior on both axes.
|
|
||||||
assert_float_equal(sprites[4].min[0], x + tileW, 0.0001f);
|
|
||||||
assert_float_equal(sprites[4].min[1], y + tileH, 0.0001f);
|
|
||||||
assert_float_equal(sprites[4].max[0], x + width - tileW, 0.0001f);
|
|
||||||
assert_float_equal(sprites[4].max[1], y + height - tileH, 0.0001f);
|
|
||||||
|
|
||||||
// Bottom-right corner sits exactly at the rect's far corner, sampling
|
|
||||||
// tileset column 2, row 2.
|
|
||||||
assert_float_equal(sprites[8].min[0], x + width - tileW, 0.0001f);
|
|
||||||
assert_float_equal(sprites[8].min[1], y + height - tileH, 0.0001f);
|
|
||||||
assert_float_equal(sprites[8].max[0], x + width, 0.0001f);
|
|
||||||
assert_float_equal(sprites[8].max[1], y + height, 0.0001f);
|
|
||||||
assert_float_equal(sprites[8].uvMin[0], 0.5f, 0.0001f);
|
|
||||||
assert_float_equal(sprites[8].uvMin[1], 0.5f, 0.0001f);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
const struct CMUnitTest tests[] = {
|
|
||||||
cmocka_unit_test(test_uiFrameBuildSprites_layout),
|
|
||||||
};
|
|
||||||
|
|
||||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dusktest.h"
|
|
||||||
#include "ui/widget/uislider.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
|
|
||||||
// uiSliderInitFloat/InitInt require a real font (FONT_DEFAULT) to build
|
|
||||||
// the label's glyph cache, which needs a live GL context this test binary
|
|
||||||
// doesn't have -- see the label size caching contract for why this is
|
|
||||||
// safe to bypass here (uiSliderRebuildGeometry only reads slider->label's
|
|
||||||
// already-measured width/height, it never touches the font itself).
|
|
||||||
static void sliderSetupFloat(
|
|
||||||
uislider_t *slider,
|
|
||||||
const float_t value,
|
|
||||||
const float_t min,
|
|
||||||
const float_t max,
|
|
||||||
const float_t step
|
|
||||||
) {
|
|
||||||
memoryZero(slider, sizeof(uislider_t));
|
|
||||||
slider->label.width = 20;
|
|
||||||
slider->label.height = 10;
|
|
||||||
slider->type = UI_SLIDER_TYPE_FLOAT;
|
|
||||||
slider->min.f = min;
|
|
||||||
slider->max.f = max;
|
|
||||||
slider->step.f = step;
|
|
||||||
slider->value.f = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sliderSetupInt(
|
|
||||||
uislider_t *slider,
|
|
||||||
const int32_t value,
|
|
||||||
const int32_t min,
|
|
||||||
const int32_t max,
|
|
||||||
const int32_t step
|
|
||||||
) {
|
|
||||||
memoryZero(slider, sizeof(uislider_t));
|
|
||||||
slider->label.width = 20;
|
|
||||||
slider->label.height = 10;
|
|
||||||
slider->type = UI_SLIDER_TYPE_INT;
|
|
||||||
slider->min.i = min;
|
|
||||||
slider->max.i = max;
|
|
||||||
slider->step.i = step;
|
|
||||||
slider->value.i = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_uiSliderRebuildGeometry_trackPosition(void **state) {
|
|
||||||
uislider_t slider;
|
|
||||||
sliderSetupFloat(&slider, 0.0f, 0.0f, 1.0f, 0.1f);
|
|
||||||
uiSliderRebuildGeometry(&slider);
|
|
||||||
|
|
||||||
const float_t expectedX = (float_t)slider.label.width + UI_SLIDER_GAP;
|
|
||||||
const float_t expectedY =
|
|
||||||
((float_t)slider.label.height - UI_SLIDER_TRACK_HEIGHT) * 0.5f;
|
|
||||||
|
|
||||||
assert_float_equal(slider.cachedTrack.min[0], expectedX, 0.0001f);
|
|
||||||
assert_float_equal(slider.cachedTrack.min[1], expectedY, 0.0001f);
|
|
||||||
assert_float_equal(
|
|
||||||
slider.cachedTrack.max[0], expectedX + UI_SLIDER_TRACK_WIDTH, 0.0001f
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_uiSliderRebuildGeometry_fillVisibility(void **state) {
|
|
||||||
uislider_t slider;
|
|
||||||
|
|
||||||
// At the minimum value, the ratio is 0 -- no fill should be visible.
|
|
||||||
sliderSetupFloat(&slider, 0.0f, 0.0f, 10.0f, 1.0f);
|
|
||||||
uiSliderRebuildGeometry(&slider);
|
|
||||||
assert_false(slider.cachedFillVisible);
|
|
||||||
|
|
||||||
// Halfway to max -- fill should cover half the track.
|
|
||||||
sliderSetupFloat(&slider, 5.0f, 0.0f, 10.0f, 1.0f);
|
|
||||||
uiSliderRebuildGeometry(&slider);
|
|
||||||
assert_true(slider.cachedFillVisible);
|
|
||||||
assert_float_equal(
|
|
||||||
slider.cachedFill.max[0] - slider.cachedFill.min[0],
|
|
||||||
UI_SLIDER_TRACK_WIDTH * 0.5f,
|
|
||||||
0.0001f
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_uiSliderRebuildGeometry_stepMarkers(void **state) {
|
|
||||||
uislider_t slider;
|
|
||||||
|
|
||||||
// 5 steps between 0 and 10 -- under the marker cap, so markers render.
|
|
||||||
sliderSetupInt(&slider, 0, 0, 10, 2);
|
|
||||||
uiSliderRebuildGeometry(&slider);
|
|
||||||
assert_int_equal(slider.cachedMarkerCount, 6);
|
|
||||||
assert_float_equal(
|
|
||||||
slider.cachedMarkers[0].min[0], slider.cachedTrack.min[0] -
|
|
||||||
UI_SLIDER_STEP_MARKER_WIDTH * 0.5f,
|
|
||||||
0.0001f
|
|
||||||
);
|
|
||||||
|
|
||||||
// 100 steps -- over the marker cap, so it falls back to a smooth fill
|
|
||||||
// with no markers.
|
|
||||||
sliderSetupInt(&slider, 0, 0, 100, 1);
|
|
||||||
uiSliderRebuildGeometry(&slider);
|
|
||||||
assert_int_equal(slider.cachedMarkerCount, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
const struct CMUnitTest tests[] = {
|
|
||||||
cmocka_unit_test(test_uiSliderRebuildGeometry_trackPosition),
|
|
||||||
cmocka_unit_test(test_uiSliderRebuildGeometry_fillVisibility),
|
|
||||||
cmocka_unit_test(test_uiSliderRebuildGeometry_stepMarkers),
|
|
||||||
};
|
|
||||||
|
|
||||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dusktest.h"
|
|
||||||
#include "ui/widget/uitab.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
|
|
||||||
// uiTabInit requires a real font (FONT_DEFAULT) to build the label's
|
|
||||||
// glyph cache, which needs a live GL context this test binary doesn't
|
|
||||||
// have. uiTabRebuildBackground only reads the label's already-measured
|
|
||||||
// width/height, so it's safe to set those directly and exercise it here.
|
|
||||||
static void test_uiTabRebuildBackground_matchesLabelSize(void **state) {
|
|
||||||
uitab_t tab;
|
|
||||||
memoryZero(&tab, sizeof(uitab_t));
|
|
||||||
tab.label.width = 42;
|
|
||||||
tab.label.height = 12;
|
|
||||||
|
|
||||||
uiTabRebuildBackground(&tab);
|
|
||||||
|
|
||||||
assert_float_equal(tab.cachedBackground.min[0], 0.0f, 0.0001f);
|
|
||||||
assert_float_equal(tab.cachedBackground.min[1], 0.0f, 0.0001f);
|
|
||||||
assert_float_equal(tab.cachedBackground.max[0], 42.0f, 0.0001f);
|
|
||||||
assert_float_equal(tab.cachedBackground.max[1], 12.0f, 0.0001f);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
const struct CMUnitTest tests[] = {
|
|
||||||
cmocka_unit_test(test_uiTabRebuildBackground_matchesLabelSize),
|
|
||||||
};
|
|
||||||
|
|
||||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
|
||||||
}
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -56,6 +56,10 @@ msgstr "Items"
|
|||||||
msgid "ui.game_menu.settings"
|
msgid "ui.game_menu.settings"
|
||||||
msgstr "Settings"
|
msgstr "Settings"
|
||||||
|
|
||||||
|
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||||
|
msgid "ui.game_menu.save"
|
||||||
|
msgstr "Save"
|
||||||
|
|
||||||
msgid "item.potion.name"
|
msgid "item.potion.name"
|
||||||
msgstr "Potion"
|
msgstr "Potion"
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ msgstr "Objetos"
|
|||||||
msgid "ui.game_menu.settings"
|
msgid "ui.game_menu.settings"
|
||||||
msgstr "Configuración"
|
msgstr "Configuración"
|
||||||
|
|
||||||
|
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||||
|
msgid "ui.game_menu.save"
|
||||||
|
msgstr "Guardar"
|
||||||
|
|
||||||
#: src/dusk/rpg/item/item.json
|
#: src/dusk/rpg/item/item.json
|
||||||
msgid "item.potion.name"
|
msgid "item.potion.name"
|
||||||
msgstr "Poción"
|
msgstr "Poción"
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ msgstr "アイテム"
|
|||||||
msgid "ui.game_menu.settings"
|
msgid "ui.game_menu.settings"
|
||||||
msgstr "設定"
|
msgstr "設定"
|
||||||
|
|
||||||
|
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||||
|
msgid "ui.game_menu.save"
|
||||||
|
msgstr "セーブ"
|
||||||
|
|
||||||
#: src/dusk/rpg/item/item.json
|
#: src/dusk/rpg/item/item.json
|
||||||
msgid "item.potion.name"
|
msgid "item.potion.name"
|
||||||
msgstr "ポーション"
|
msgstr "ポーション"
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user