Files
dusk/test/script/test_moduleui.c
T
YourWishes 7ee04c78cd Rebuild UI as a scriptable element tree, archive the old system
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>
2026-08-13 11:13:23 -05:00

86 lines
2.2 KiB
C

/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "dusktest.h"
#include "console/console.h"
#include "script/scriptmanager.h"
#include "ui/ui.h"
#include "util/memory.h"
static int ui_setup(void **state) {
consoleInit();
memoryZero(UI_ELEMENTS, sizeof(UI_ELEMENTS));
memoryZero(&UI, sizeof(UI));
for(uint8_t i = 0; i < UI_ROOT_MAX; i++) UI.root[i] = UI_ELEMENT_ID_INVALID;
errorret_t ret = scriptManagerInit();
if(errorIsNotOk(ret)) { errorCatch(ret); return -1; }
return 0;
}
static int ui_teardown(void **state) {
errorret_t ret = scriptManagerDispose();
if(errorIsNotOk(ret)) errorCatch(ret);
consoleDispose();
return 0;
}
static void test_ui_add_requires_an_argument(void **state) {
errorret_t ret = scriptManagerExec("UI.add();", NULL);
assert_true(errorIsNotOk(ret));
errorCatch(ret);
assert_int_equal(memoryGetAllocatedCount(), 0);
}
static void test_ui_add_requires_an_object(void **state) {
errorret_t ret = scriptManagerExec("UI.add('button');", NULL);
assert_true(errorIsNotOk(ret));
errorCatch(ret);
assert_int_equal(memoryGetAllocatedCount(), 0);
}
static void test_ui_add_requires_a_uielement(void **state) {
errorret_t ret = scriptManagerExec("UI.add({});", NULL);
assert_true(errorIsNotOk(ret));
errorCatch(ret);
assert_int_equal(memoryGetAllocatedCount(), 0);
}
static void test_ui_remove_of_untracked_element_is_a_noop(void **state) {
jerry_value_t result;
errorret_t ret = scriptManagerExec(
"UI.remove(new UIElement());", &result
);
assert_true(errorIsOk(ret));
jerry_value_free(result);
}
int main(void) {
assertInit();
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(
test_ui_add_requires_an_argument, ui_setup, ui_teardown
),
cmocka_unit_test_setup_teardown(
test_ui_add_requires_an_object, ui_setup, ui_teardown
),
cmocka_unit_test_setup_teardown(
test_ui_add_requires_a_uielement, ui_setup, ui_teardown
),
cmocka_unit_test_setup_teardown(
test_ui_remove_of_untracked_element_is_a_noop, ui_setup, ui_teardown
),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}