broken ui system
This commit is contained in:
581
src/uisystem.cpp
Normal file
581
src/uisystem.cpp
Normal file
@@ -0,0 +1,581 @@
|
||||
#include "uisystem.hh"
|
||||
|
||||
#include <psyqo/kernel.hh>
|
||||
#include <psyqo/primitives/common.hh>
|
||||
#include <psyqo/primitives/misc.hh>
|
||||
#include <psyqo/primitives/rectangles.hh>
|
||||
#include <psyqo/primitives/triangles.hh>
|
||||
|
||||
namespace psxsplash {
|
||||
|
||||
// Bare-metal string compare (no libc)
|
||||
static bool ui_streq(const char* a, const char* b) {
|
||||
while (*a && *b) {
|
||||
if (*a++ != *b++) return false;
|
||||
}
|
||||
return *a == *b;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Init
|
||||
// ============================================================================
|
||||
|
||||
void UISystem::init(psyqo::Font<>& systemFont) {
|
||||
m_systemFont = &systemFont;
|
||||
m_canvasCount = 0;
|
||||
m_elementCount = 0;
|
||||
m_pendingTextCount = 0;
|
||||
m_fontCount = 0;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Load from splashpack (zero-copy, pointer fixup)
|
||||
// ============================================================================
|
||||
|
||||
void UISystem::loadFromSplashpack(uint8_t* data, uint16_t canvasCount,
|
||||
uint8_t fontCount, uint32_t tableOffset) {
|
||||
if (tableOffset == 0) return;
|
||||
|
||||
uint8_t* ptr = data + tableOffset;
|
||||
|
||||
// ── Parse font descriptors (16 bytes each, before canvas data) ──
|
||||
if (fontCount > UI_MAX_FONTS - 1) fontCount = UI_MAX_FONTS - 1;
|
||||
m_fontCount = fontCount;
|
||||
for (int fi = 0; fi < fontCount; fi++) {
|
||||
UIFontDesc& fd = m_fontDescs[fi];
|
||||
fd.glyphW = ptr[0];
|
||||
fd.glyphH = ptr[1];
|
||||
fd.vramX = *reinterpret_cast<uint16_t*>(ptr + 2);
|
||||
fd.vramY = *reinterpret_cast<uint16_t*>(ptr + 4);
|
||||
fd.textureH = *reinterpret_cast<uint16_t*>(ptr + 6);
|
||||
uint32_t dataOff = *reinterpret_cast<uint32_t*>(ptr + 8);
|
||||
fd.pixelDataSize = *reinterpret_cast<uint32_t*>(ptr + 12);
|
||||
fd.pixelData = (dataOff != 0) ? (data + dataOff) : nullptr;
|
||||
ptr += 16;
|
||||
}
|
||||
|
||||
// ── Parse canvas descriptors ──
|
||||
if (canvasCount == 0) return;
|
||||
if (canvasCount > UI_MAX_CANVASES) canvasCount = UI_MAX_CANVASES;
|
||||
|
||||
// Canvas descriptor table: 12 bytes per entry
|
||||
// struct { uint32_t dataOffset; uint8_t nameLen; uint8_t sortOrder;
|
||||
// uint8_t elementCount; uint8_t flags; uint32_t nameOffset; }
|
||||
uint8_t* tablePtr = ptr; // starts right after font descriptors
|
||||
m_canvasCount = canvasCount;
|
||||
m_elementCount = 0;
|
||||
|
||||
for (int ci = 0; ci < canvasCount; ci++) {
|
||||
uint32_t dataOffset = *reinterpret_cast<uint32_t*>(tablePtr); tablePtr += 4;
|
||||
uint8_t nameLen = *tablePtr++;
|
||||
uint8_t sortOrder = *tablePtr++;
|
||||
uint8_t elementCount = *tablePtr++;
|
||||
uint8_t flags = *tablePtr++;
|
||||
uint32_t nameOffset = *reinterpret_cast<uint32_t*>(tablePtr); tablePtr += 4;
|
||||
|
||||
UICanvas& cv = m_canvases[ci];
|
||||
cv.name = (nameLen > 0 && nameOffset != 0)
|
||||
? reinterpret_cast<const char*>(data + nameOffset)
|
||||
: "";
|
||||
cv.visible = (flags & 0x01) != 0;
|
||||
cv.sortOrder = sortOrder;
|
||||
cv.elements = &m_elements[m_elementCount];
|
||||
|
||||
// Cap element count against pool
|
||||
if (m_elementCount + elementCount > UI_MAX_ELEMENTS)
|
||||
elementCount = (uint8_t)(UI_MAX_ELEMENTS - m_elementCount);
|
||||
cv.elementCount = elementCount;
|
||||
|
||||
// Parse element array (48 bytes per entry)
|
||||
uint8_t* elemPtr = data + dataOffset;
|
||||
for (int ei = 0; ei < elementCount; ei++) {
|
||||
UIElement& el = m_elements[m_elementCount++];
|
||||
|
||||
// Identity (8 bytes)
|
||||
el.type = static_cast<UIElementType>(*elemPtr++);
|
||||
uint8_t eFlags = *elemPtr++;
|
||||
el.visible = (eFlags & 0x01) != 0;
|
||||
uint8_t eNameLen = *elemPtr++;
|
||||
elemPtr++; // pad0
|
||||
uint32_t eNameOff = *reinterpret_cast<uint32_t*>(elemPtr); elemPtr += 4;
|
||||
el.name = (eNameLen > 0 && eNameOff != 0)
|
||||
? reinterpret_cast<const char*>(data + eNameOff)
|
||||
: "";
|
||||
|
||||
// Layout (8 bytes)
|
||||
el.x = *reinterpret_cast<int16_t*>(elemPtr); elemPtr += 2;
|
||||
el.y = *reinterpret_cast<int16_t*>(elemPtr); elemPtr += 2;
|
||||
el.w = *reinterpret_cast<int16_t*>(elemPtr); elemPtr += 2;
|
||||
el.h = *reinterpret_cast<int16_t*>(elemPtr); elemPtr += 2;
|
||||
|
||||
// Anchors (4 bytes)
|
||||
el.anchorMinX = *elemPtr++;
|
||||
el.anchorMinY = *elemPtr++;
|
||||
el.anchorMaxX = *elemPtr++;
|
||||
el.anchorMaxY = *elemPtr++;
|
||||
|
||||
// Primary color (4 bytes)
|
||||
el.colorR = *elemPtr++;
|
||||
el.colorG = *elemPtr++;
|
||||
el.colorB = *elemPtr++;
|
||||
elemPtr++; // pad1
|
||||
|
||||
// Type-specific data (16 bytes)
|
||||
uint8_t* typeData = elemPtr;
|
||||
elemPtr += 16;
|
||||
|
||||
// Initialize union to zero
|
||||
for (int i = 0; i < (int)sizeof(UIImageData); i++)
|
||||
reinterpret_cast<uint8_t*>(&el.image)[i] = 0;
|
||||
|
||||
switch (el.type) {
|
||||
case UIElementType::Image:
|
||||
el.image.texpageX = typeData[0];
|
||||
el.image.texpageY = typeData[1];
|
||||
el.image.clutX = *reinterpret_cast<uint16_t*>(&typeData[2]);
|
||||
el.image.clutY = *reinterpret_cast<uint16_t*>(&typeData[4]);
|
||||
el.image.u0 = typeData[6];
|
||||
el.image.v0 = typeData[7];
|
||||
el.image.u1 = typeData[8];
|
||||
el.image.v1 = typeData[9];
|
||||
el.image.bitDepth = typeData[10];
|
||||
break;
|
||||
case UIElementType::Progress:
|
||||
el.progress.bgR = typeData[0];
|
||||
el.progress.bgG = typeData[1];
|
||||
el.progress.bgB = typeData[2];
|
||||
el.progress.value = typeData[3];
|
||||
break;
|
||||
case UIElementType::Text:
|
||||
el.textData.fontIndex = typeData[0]; // 0=system, 1+=custom
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Text content offset (8 bytes)
|
||||
uint32_t textOff = *reinterpret_cast<uint32_t*>(elemPtr); elemPtr += 4;
|
||||
elemPtr += 4; // pad2
|
||||
|
||||
// Initialize text buffer
|
||||
el.textBuf[0] = '\0';
|
||||
if (el.type == UIElementType::Text && textOff != 0) {
|
||||
const char* src = reinterpret_cast<const char*>(data + textOff);
|
||||
int ti = 0;
|
||||
while (ti < UI_TEXT_BUF - 1 && src[ti] != '\0') {
|
||||
el.textBuf[ti] = src[ti];
|
||||
ti++;
|
||||
}
|
||||
el.textBuf[ti] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insertion sort canvases by sortOrder (ascending = back-to-front)
|
||||
for (int i = 1; i < m_canvasCount; i++) {
|
||||
UICanvas tmp = m_canvases[i];
|
||||
int j = i - 1;
|
||||
while (j >= 0 && m_canvases[j].sortOrder > tmp.sortOrder) {
|
||||
m_canvases[j + 1] = m_canvases[j];
|
||||
j--;
|
||||
}
|
||||
m_canvases[j + 1] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Layout resolution
|
||||
// ============================================================================
|
||||
|
||||
void UISystem::resolveLayout(const UIElement& el,
|
||||
int16_t& outX, int16_t& outY,
|
||||
int16_t& outW, int16_t& outH) const {
|
||||
// Anchor gives the origin point in screen space (8.8 fixed → pixel)
|
||||
int ax = ((int)el.anchorMinX * VRAM_RES_WIDTH) >> 8;
|
||||
int ay = ((int)el.anchorMinY * VRAM_RES_HEIGHT) >> 8;
|
||||
outX = (int16_t)(ax + el.x);
|
||||
outY = (int16_t)(ay + el.y);
|
||||
|
||||
// Stretch: anchorMax != anchorMin means width/height is determined by span + offset
|
||||
if (el.anchorMaxX != el.anchorMinX) {
|
||||
int bx = ((int)el.anchorMaxX * VRAM_RES_WIDTH) >> 8;
|
||||
outW = (int16_t)(bx - ax + el.w);
|
||||
} else {
|
||||
outW = el.w;
|
||||
}
|
||||
if (el.anchorMaxY != el.anchorMinY) {
|
||||
int by = ((int)el.anchorMaxY * VRAM_RES_HEIGHT) >> 8;
|
||||
outH = (int16_t)(by - ay + el.h);
|
||||
} else {
|
||||
outH = el.h;
|
||||
}
|
||||
|
||||
// Clamp to screen bounds (never draw outside the framebuffer)
|
||||
if (outX < 0) { outW += outX; outX = 0; }
|
||||
if (outY < 0) { outH += outY; outY = 0; }
|
||||
if (outW <= 0) outW = 1;
|
||||
if (outH <= 0) outH = 1;
|
||||
if (outX + outW > VRAM_RES_WIDTH) outW = (int16_t)(VRAM_RES_WIDTH - outX);
|
||||
if (outY + outH > VRAM_RES_HEIGHT) outH = (int16_t)(VRAM_RES_HEIGHT - outY);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TPage construction for UI images
|
||||
// ============================================================================
|
||||
|
||||
psyqo::PrimPieces::TPageAttr UISystem::makeTPage(const UIImageData& img) {
|
||||
psyqo::PrimPieces::TPageAttr tpage;
|
||||
tpage.setPageX(img.texpageX);
|
||||
tpage.setPageY(img.texpageY);
|
||||
// Color mode from bitDepth: 0→Tex4Bits, 1→Tex8Bits, 2→Tex16Bits
|
||||
switch (img.bitDepth) {
|
||||
case 0:
|
||||
tpage.set(psyqo::Prim::TPageAttr::Tex4Bits);
|
||||
break;
|
||||
case 1:
|
||||
tpage.set(psyqo::Prim::TPageAttr::Tex8Bits);
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
tpage.set(psyqo::Prim::TPageAttr::Tex16Bits);
|
||||
break;
|
||||
}
|
||||
tpage.setDithering(false); // UI doesn't need dithering
|
||||
return tpage;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Render a single element into the OT
|
||||
// ============================================================================
|
||||
|
||||
void UISystem::renderElement(UIElement& el,
|
||||
psyqo::OrderingTable<Renderer::ORDERING_TABLE_SIZE>& ot,
|
||||
psyqo::BumpAllocator<Renderer::BUMP_ALLOCATOR_SIZE>& balloc) {
|
||||
int16_t x, y, w, h;
|
||||
resolveLayout(el, x, y, w, h);
|
||||
|
||||
switch (el.type) {
|
||||
case UIElementType::Box: {
|
||||
auto& frag = balloc.allocateFragment<psyqo::Prim::Rectangle>();
|
||||
frag.primitive.setColor(psyqo::Color{.r = el.colorR, .g = el.colorG, .b = el.colorB});
|
||||
frag.primitive.position = {.x = x, .y = y};
|
||||
frag.primitive.size = {.x = w, .y = h};
|
||||
frag.primitive.setOpaque();
|
||||
ot.insert(frag, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case UIElementType::Progress: {
|
||||
// Background: full rect
|
||||
auto& bgFrag = balloc.allocateFragment<psyqo::Prim::Rectangle>();
|
||||
bgFrag.primitive.setColor(psyqo::Color{.r = el.progress.bgR, .g = el.progress.bgG, .b = el.progress.bgB});
|
||||
bgFrag.primitive.position = {.x = x, .y = y};
|
||||
bgFrag.primitive.size = {.x = w, .y = h};
|
||||
bgFrag.primitive.setOpaque();
|
||||
ot.insert(bgFrag, 1);
|
||||
|
||||
// Fill: partial width
|
||||
int fillW = (int)el.progress.value * w / 100;
|
||||
if (fillW < 0) fillW = 0;
|
||||
if (fillW > w) fillW = w;
|
||||
if (fillW > 0) {
|
||||
auto& fillFrag = balloc.allocateFragment<psyqo::Prim::Rectangle>();
|
||||
fillFrag.primitive.setColor(psyqo::Color{.r = el.colorR, .g = el.colorG, .b = el.colorB});
|
||||
fillFrag.primitive.position = {.x = x, .y = y};
|
||||
fillFrag.primitive.size = {.x = (int16_t)fillW, .y = h};
|
||||
fillFrag.primitive.setOpaque();
|
||||
ot.insert(fillFrag, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case UIElementType::Image: {
|
||||
psyqo::PrimPieces::TPageAttr tpage = makeTPage(el.image);
|
||||
psyqo::PrimPieces::ClutIndex clut(el.image.clutX, el.image.clutY);
|
||||
psyqo::Color tint = {.r = el.colorR, .g = el.colorG, .b = el.colorB};
|
||||
|
||||
// Triangle 0: top-left, top-right, bottom-left
|
||||
{
|
||||
auto& tri = balloc.allocateFragment<psyqo::Prim::GouraudTexturedTriangle>();
|
||||
tri.primitive.pointA.x = x; tri.primitive.pointA.y = y;
|
||||
tri.primitive.pointB.x = x + w; tri.primitive.pointB.y = y;
|
||||
tri.primitive.pointC.x = x; tri.primitive.pointC.y = y + h;
|
||||
tri.primitive.uvA.u = el.image.u0; tri.primitive.uvA.v = el.image.v0;
|
||||
tri.primitive.uvB.u = el.image.u1; tri.primitive.uvB.v = el.image.v0;
|
||||
tri.primitive.uvC.u = el.image.u0; tri.primitive.uvC.v = el.image.v1;
|
||||
tri.primitive.tpage = tpage;
|
||||
tri.primitive.clutIndex = clut;
|
||||
tri.primitive.setColorA(tint);
|
||||
tri.primitive.setColorB(tint);
|
||||
tri.primitive.setColorC(tint);
|
||||
tri.primitive.setOpaque();
|
||||
ot.insert(tri, 0);
|
||||
}
|
||||
// Triangle 1: top-right, bottom-right, bottom-left
|
||||
{
|
||||
auto& tri = balloc.allocateFragment<psyqo::Prim::GouraudTexturedTriangle>();
|
||||
tri.primitive.pointA.x = x + w; tri.primitive.pointA.y = y;
|
||||
tri.primitive.pointB.x = x + w; tri.primitive.pointB.y = y + h;
|
||||
tri.primitive.pointC.x = x; tri.primitive.pointC.y = y + h;
|
||||
tri.primitive.uvA.u = el.image.u1; tri.primitive.uvA.v = el.image.v0;
|
||||
tri.primitive.uvB.u = el.image.u1; tri.primitive.uvB.v = el.image.v1;
|
||||
tri.primitive.uvC.u = el.image.u0; tri.primitive.uvC.v = el.image.v1;
|
||||
tri.primitive.tpage = tpage;
|
||||
tri.primitive.clutIndex = clut;
|
||||
tri.primitive.setColorA(tint);
|
||||
tri.primitive.setColorB(tint);
|
||||
tri.primitive.setColorC(tint);
|
||||
tri.primitive.setOpaque();
|
||||
ot.insert(tri, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case UIElementType::Text: {
|
||||
// Queue text for phase 2 (after gpu.chain)
|
||||
if (m_pendingTextCount < UI_MAX_ELEMENTS) {
|
||||
uint8_t fi = (el.type == UIElementType::Text) ? el.textData.fontIndex : 0;
|
||||
m_pendingTexts[m_pendingTextCount++] = {
|
||||
x, y,
|
||||
el.colorR, el.colorG, el.colorB,
|
||||
fi,
|
||||
el.textBuf
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Render phases
|
||||
// ============================================================================
|
||||
|
||||
void UISystem::renderOT(psyqo::GPU& gpu,
|
||||
psyqo::OrderingTable<Renderer::ORDERING_TABLE_SIZE>& ot,
|
||||
psyqo::BumpAllocator<Renderer::BUMP_ALLOCATOR_SIZE>& balloc) {
|
||||
m_pendingTextCount = 0;
|
||||
|
||||
// Canvases are pre-sorted by sortOrder (ascending = back first).
|
||||
// Higher-sortOrder canvases insert at OT 0 later, appearing on top.
|
||||
for (int i = 0; i < m_canvasCount; i++) {
|
||||
UICanvas& cv = m_canvases[i];
|
||||
if (!cv.visible) continue;
|
||||
for (int j = 0; j < cv.elementCount; j++) {
|
||||
UIElement& el = cv.elements[j];
|
||||
if (!el.visible) continue;
|
||||
renderElement(el, ot, balloc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UISystem::renderText(psyqo::GPU& gpu) {
|
||||
for (int i = 0; i < m_pendingTextCount; i++) {
|
||||
auto& pt = m_pendingTexts[i];
|
||||
psyqo::FontBase* font = resolveFont(pt.fontIndex);
|
||||
if (!font) continue;
|
||||
font->chainprintf(gpu,
|
||||
{{.x = pt.x, .y = pt.y}},
|
||||
{{.r = pt.r, .g = pt.g, .b = pt.b}},
|
||||
"%s", pt.text);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Font support
|
||||
// ============================================================================
|
||||
|
||||
psyqo::FontBase* UISystem::resolveFont(uint8_t fontIndex) {
|
||||
if (fontIndex == 0 || fontIndex > m_fontCount) return m_systemFont;
|
||||
return &m_customFonts[fontIndex - 1];
|
||||
}
|
||||
|
||||
void UISystem::uploadFonts(psyqo::GPU& gpu) {
|
||||
for (int i = 0; i < m_fontCount; i++) {
|
||||
UIFontDesc& fd = m_fontDescs[i];
|
||||
if (!fd.pixelData || fd.pixelDataSize == 0) continue;
|
||||
|
||||
// Upload 4bpp texture to VRAM
|
||||
// 4bpp 256px wide = 64 VRAM hwords wide
|
||||
Renderer::GetInstance().VramUpload(
|
||||
reinterpret_cast<const uint16_t*>(fd.pixelData),
|
||||
(int16_t)fd.vramX, (int16_t)fd.vramY,
|
||||
64, (int16_t)fd.textureH);
|
||||
|
||||
// Initialize the Font<2> instance for this custom font
|
||||
m_customFonts[i].initialize(gpu,
|
||||
{{.x = (int16_t)fd.vramX, .y = (int16_t)fd.vramY}},
|
||||
{{.x = (int16_t)fd.glyphW, .y = (int16_t)fd.glyphH}});
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Canvas API
|
||||
// ============================================================================
|
||||
|
||||
int UISystem::findCanvas(const char* name) const {
|
||||
if (!name) return -1;
|
||||
for (int i = 0; i < m_canvasCount; i++) {
|
||||
if (m_canvases[i].name && ui_streq(m_canvases[i].name, name))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void UISystem::setCanvasVisible(int idx, bool v) {
|
||||
if (idx >= 0 && idx < m_canvasCount)
|
||||
m_canvases[idx].visible = v;
|
||||
}
|
||||
|
||||
bool UISystem::isCanvasVisible(int idx) const {
|
||||
if (idx >= 0 && idx < m_canvasCount)
|
||||
return m_canvases[idx].visible;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Element API
|
||||
// ============================================================================
|
||||
|
||||
int UISystem::findElement(int canvasIdx, const char* name) const {
|
||||
if (canvasIdx < 0 || canvasIdx >= m_canvasCount || !name) return -1;
|
||||
const UICanvas& cv = m_canvases[canvasIdx];
|
||||
for (int i = 0; i < cv.elementCount; i++) {
|
||||
if (cv.elements[i].name && ui_streq(cv.elements[i].name, name)) {
|
||||
// Return flat handle: index into m_elements
|
||||
int handle = (int)(cv.elements + i - m_elements);
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void UISystem::setElementVisible(int handle, bool v) {
|
||||
if (handle >= 0 && handle < m_elementCount)
|
||||
m_elements[handle].visible = v;
|
||||
}
|
||||
|
||||
bool UISystem::isElementVisible(int handle) const {
|
||||
if (handle >= 0 && handle < m_elementCount)
|
||||
return m_elements[handle].visible;
|
||||
return false;
|
||||
}
|
||||
|
||||
void UISystem::setText(int handle, const char* text) {
|
||||
if (handle < 0 || handle >= m_elementCount) return;
|
||||
UIElement& el = m_elements[handle];
|
||||
if (el.type != UIElementType::Text) return;
|
||||
if (!text) { el.textBuf[0] = '\0'; return; }
|
||||
int i = 0;
|
||||
while (i < UI_TEXT_BUF - 1 && text[i] != '\0') {
|
||||
el.textBuf[i] = text[i];
|
||||
i++;
|
||||
}
|
||||
el.textBuf[i] = '\0';
|
||||
}
|
||||
|
||||
const char* UISystem::getText(int handle) const {
|
||||
if (handle < 0 || handle >= m_elementCount) return "";
|
||||
const UIElement& el = m_elements[handle];
|
||||
if (el.type != UIElementType::Text) return "";
|
||||
return el.textBuf;
|
||||
}
|
||||
|
||||
void UISystem::setProgress(int handle, uint8_t value) {
|
||||
if (handle < 0 || handle >= m_elementCount) return;
|
||||
UIElement& el = m_elements[handle];
|
||||
if (el.type != UIElementType::Progress) return;
|
||||
if (value > 100) value = 100;
|
||||
el.progress.value = value;
|
||||
}
|
||||
|
||||
void UISystem::setColor(int handle, uint8_t r, uint8_t g, uint8_t b) {
|
||||
if (handle < 0 || handle >= m_elementCount) return;
|
||||
m_elements[handle].colorR = r;
|
||||
m_elements[handle].colorG = g;
|
||||
m_elements[handle].colorB = b;
|
||||
}
|
||||
|
||||
void UISystem::getColor(int handle, uint8_t& r, uint8_t& g, uint8_t& b) const {
|
||||
if (handle < 0 || handle >= m_elementCount) { r = g = b = 0; return; }
|
||||
r = m_elements[handle].colorR;
|
||||
g = m_elements[handle].colorG;
|
||||
b = m_elements[handle].colorB;
|
||||
}
|
||||
|
||||
void UISystem::setPosition(int handle, int16_t x, int16_t y) {
|
||||
if (handle < 0 || handle >= m_elementCount) return;
|
||||
UIElement& el = m_elements[handle];
|
||||
el.x = x;
|
||||
el.y = y;
|
||||
// Zero out anchors to make position absolute
|
||||
el.anchorMinX = 0;
|
||||
el.anchorMinY = 0;
|
||||
el.anchorMaxX = 0;
|
||||
el.anchorMaxY = 0;
|
||||
}
|
||||
|
||||
void UISystem::getPosition(int handle, int16_t& x, int16_t& y) const {
|
||||
if (handle < 0 || handle >= m_elementCount) { x = y = 0; return; }
|
||||
// Resolve full layout to return actual screen position
|
||||
int16_t rx, ry, rw, rh;
|
||||
resolveLayout(m_elements[handle], rx, ry, rw, rh);
|
||||
x = rx;
|
||||
y = ry;
|
||||
}
|
||||
|
||||
void UISystem::setSize(int handle, int16_t w, int16_t h) {
|
||||
if (handle < 0 || handle >= m_elementCount) return;
|
||||
m_elements[handle].w = w;
|
||||
m_elements[handle].h = h;
|
||||
// Clear stretch anchors so size is explicit
|
||||
m_elements[handle].anchorMaxX = m_elements[handle].anchorMinX;
|
||||
m_elements[handle].anchorMaxY = m_elements[handle].anchorMinY;
|
||||
}
|
||||
|
||||
void UISystem::getSize(int handle, int16_t& w, int16_t& h) const {
|
||||
if (handle < 0 || handle >= m_elementCount) { w = h = 0; return; }
|
||||
int16_t rx, ry, rw, rh;
|
||||
resolveLayout(m_elements[handle], rx, ry, rw, rh);
|
||||
w = rw;
|
||||
h = rh;
|
||||
}
|
||||
|
||||
void UISystem::setProgressColors(int handle, uint8_t bgR, uint8_t bgG, uint8_t bgB,
|
||||
uint8_t fillR, uint8_t fillG, uint8_t fillB) {
|
||||
if (handle < 0 || handle >= m_elementCount) return;
|
||||
UIElement& el = m_elements[handle];
|
||||
if (el.type != UIElementType::Progress) return;
|
||||
el.progress.bgR = bgR;
|
||||
el.progress.bgG = bgG;
|
||||
el.progress.bgB = bgB;
|
||||
el.colorR = fillR;
|
||||
el.colorG = fillG;
|
||||
el.colorB = fillB;
|
||||
}
|
||||
|
||||
uint8_t UISystem::getProgress(int handle) const {
|
||||
if (handle < 0 || handle >= m_elementCount) return 0;
|
||||
const UIElement& el = m_elements[handle];
|
||||
if (el.type != UIElementType::Progress) return 0;
|
||||
return el.progress.value;
|
||||
}
|
||||
|
||||
UIElementType UISystem::getElementType(int handle) const {
|
||||
if (handle < 0 || handle >= m_elementCount) return UIElementType::Box;
|
||||
return m_elements[handle].type;
|
||||
}
|
||||
|
||||
int UISystem::getCanvasElementCount(int canvasIdx) const {
|
||||
if (canvasIdx < 0 || canvasIdx >= m_canvasCount) return 0;
|
||||
return m_canvases[canvasIdx].elementCount;
|
||||
}
|
||||
|
||||
int UISystem::getCanvasElementHandle(int canvasIdx, int elementIndex) const {
|
||||
if (canvasIdx < 0 || canvasIdx >= m_canvasCount) return -1;
|
||||
const UICanvas& cv = m_canvases[canvasIdx];
|
||||
if (elementIndex < 0 || elementIndex >= cv.elementCount) return -1;
|
||||
return (int)(cv.elements + elementIndex - m_elements);
|
||||
}
|
||||
|
||||
} // namespace psxsplash
|
||||
Reference in New Issue
Block a user