7ee04c78cd
The old UI system (X-macro static element list, hand-authored C screens/widgets/focus stack) is archived under archive/ rather than deleted, since it's a useful reference during the rewrite. New native UI element pool (src/dusk/ui): a flat UI_ELEMENTS[128] pool of tagged-union elements (Label, Rectangle, Scripted), a real persistent parent/child tree (children[8] per element, cycle- and capacity-checked uiElementSetParent), always-fresh worldX/worldY (cheap enough to recompute on every read, no dirty-flag cache needed), and cascading dispose. Rendering stays manual/immediate: a scripted element with no render() override auto-renders its children by default, but overriding render() takes full control (an override must call renderChildren() itself to opt back in) -- this is deliberately preserved end to end via a render()-shadow trampoline so overriding render() always keeps working the same way regardless of how a node is reached. New scripting layer (src/dusk/script/module/ui): UIElement/Label/ Rectangle JS classes (Label/Rectangle share UIElement's prototype via manual chaining, not JS `extends`), exposing x/y/worldX/worldY/parent/ add()/remove()/render()/renderChildren()/dispose(). UI.add()/ UI.remove() manage top-level render roots, mutually exclusive with being someone's child. Also: Scene gains a lateUpdate() hook (called once per frame after every other update, for things like camera-follow that need to react to where everything else ended up); several duskrpg call sites (cutscene items, entityinteractable, entityplayer) that depended on the now-archived RPG textbox are stubbed to console output instead of a dialogue box, pending the new UI reaching that far. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
122 lines
3.1 KiB
C
122 lines
3.1 KiB
C
/**
|
|
* Copyright (c) 2026 Dominic Masters
|
|
*
|
|
* This software is released under the MIT License.
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#include "uitextboxmain.h"
|
|
#include "ui/focus/uifocus.h"
|
|
#include "display/screen/screen.h"
|
|
#include "display/text/text.h"
|
|
#include "display/color.h"
|
|
#include "display/spritebatch/spritebatch.h"
|
|
#include "display/shader/shaderunlit.h"
|
|
#include "ui/frame/uiframe.h"
|
|
|
|
uitextboxmain_t UI_TEXTBOX_MAIN;
|
|
static uifocusitem_t *focusItem = NULL;
|
|
|
|
errorret_t uiTextboxMainInit(void) {
|
|
uiTextboxInit(
|
|
&UI_TEXTBOX_MAIN.box,
|
|
UI_TEXTBOX_MAIN.text, UI_TEXTBOX_MAIN_TEXT_MAX,
|
|
UI_TEXTBOX_MAIN.lines, UI_TEXTBOX_MAIN_LINES_MAX
|
|
);
|
|
errorOk();
|
|
}
|
|
|
|
void uiTextboxMainSetText(const char_t *text) {
|
|
uiTextboxSetText(&UI_TEXTBOX_MAIN.box, text);
|
|
if(focusItem != NULL) return;
|
|
focusItem = uiFocusPush(
|
|
1, 1,
|
|
uiTextboxMainFocusSelected,
|
|
NULL,
|
|
uiTextboxMainFocusClosed,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
errorret_t uiTextboxMainUpdate(void) {
|
|
if(focusItem == NULL) errorOk();
|
|
return uiTextboxUpdate(&UI_TEXTBOX_MAIN.box);
|
|
}
|
|
|
|
errorret_t uiTextboxMainDraw(void) {
|
|
if(focusItem == NULL) errorOk();
|
|
float_t fontH = (float_t)FONT_DEFAULT.tileset->tileHeight;
|
|
float_t h = (float_t)UI_TEXTBOX_MAIN_LINES * fontH +
|
|
(float_t)(UI_TEXTBOX_MAIN_LINES - 1) * UI_TEXTBOX_LINE_SPACING +
|
|
2.0f * (float_t)UI_FRAME_START_Y;
|
|
float_t w = (float_t)SCREEN.scanWidth;
|
|
float_t x = (float_t)SCREEN.scanX;
|
|
float_t y = (float_t)(SCREEN.scanY + SCREEN.scanHeight) - h;
|
|
errorChain(uiTextboxDraw(&UI_TEXTBOX_MAIN.box, x, y, w, h));
|
|
|
|
if(!uiTextboxPageIsComplete(&UI_TEXTBOX_MAIN.box)) errorOk();
|
|
|
|
float_t fontW = (float_t)FONT_DEFAULT.tileset->tileWidth;
|
|
float_t contentX = x + (float_t)UI_FRAME_START_X;
|
|
float_t contentY = y + (float_t)UI_FRAME_START_Y;
|
|
float_t contentW = w - 2.0f * (float_t)UI_FRAME_START_X;
|
|
float_t contentH = h - 2.0f * (float_t)UI_FRAME_START_Y;
|
|
|
|
shadermaterial_t material = {
|
|
.unlit = {
|
|
.color = COLOR_WHITE,
|
|
.texture = FONT_DEFAULT.texture
|
|
}
|
|
};
|
|
|
|
spritebatchsprite_t caret = textGetSprite(
|
|
(vec2){
|
|
contentX + contentW - fontW,
|
|
contentY + contentH - fontH
|
|
},
|
|
'v',
|
|
&FONT_DEFAULT
|
|
);
|
|
errorChain(spriteBatchBuffer(&caret, 1, &SHADER_UNLIT, material));
|
|
|
|
errorOk();
|
|
}
|
|
|
|
bool_t uiTextboxMainPageIsComplete(void) {
|
|
return uiTextboxPageIsComplete(&UI_TEXTBOX_MAIN.box);
|
|
}
|
|
|
|
bool_t uiTextboxMainHasNextPage(void) {
|
|
return uiTextboxHasNextPage(&UI_TEXTBOX_MAIN.box);
|
|
}
|
|
|
|
void uiTextboxMainNextPage(void) {
|
|
uiTextboxNextPage(&UI_TEXTBOX_MAIN.box);
|
|
}
|
|
|
|
bool_t uiTextboxMainIsActive(void) {
|
|
return focusItem != NULL;
|
|
}
|
|
|
|
bool_t uiTextboxMainFocusSelected(const uifocusitem_t *item) {
|
|
if(!uiTextboxPageIsComplete(&UI_TEXTBOX_MAIN.box)) {
|
|
UI_TEXTBOX_MAIN.box.scroll =
|
|
uiTextboxGetPageCharCount(&UI_TEXTBOX_MAIN.box);
|
|
return true;
|
|
}
|
|
|
|
if(uiTextboxHasNextPage(&UI_TEXTBOX_MAIN.box)) {
|
|
uiTextboxNextPage(&UI_TEXTBOX_MAIN.box);
|
|
return true;
|
|
}
|
|
|
|
uiFocusPopItem(focusItem);
|
|
return true;
|
|
}
|
|
|
|
bool_t uiTextboxMainFocusClosed(const uifocusitem_t *item) {
|
|
focusItem = NULL;
|
|
return true;
|
|
}
|