--- a/ui/server/container.c Thu Dec 11 21:05:40 2025 +0100 +++ b/ui/server/container.c Fri Dec 12 11:38:47 2025 +0100 @@ -26,10 +26,114 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include <cx/linked_list.h> +#include <cx/buffer.h> + #include "container.h" +#include "args.h" + +void ui_container_begin_close(UiObject *obj) { + UiContainerX *ct = obj->container_end; + ct->close = 1; +} + +int ui_container_finish(UiObject *obj) { + UiContainerX *ct = obj->container_end; + if(ct->close) { + ui_end_new(obj); + return 0; + } + return 1; +} UiContainerX* ui_widget_container(UiWidget *w) { UiContainerX *container = cxZalloc(w->obj->ctx->allocator, sizeof(UiContainerX)); container->container = w; return container; } + +cxmutstr ui_container_args_to_string(UiContext *ctx, UiContainerArgs *args) { + CxBuffer buf; + cxBufferInit(&buf, NULL, 256, ctx->allocator, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_FREE_CONTENTS); + + cxBufferPutString(&buf, "{"); + + UI_STANDARD_ARGS(&buf, args); + UI_SPACING_ARGS(&buf, args); + if(args->def_hfill) cxBufferPutString(&buf, "\"def_hfill\":true,"); + if(args->def_vfill) cxBufferPutString(&buf, "\"def_vfill\":true,"); + if(args->def_hexpand) cxBufferPutString(&buf, "\"def_hexpand\":true,"); + if(args->def_vexpand) cxBufferPutString(&buf, "\"def_vexpand\":true,"); + + if(buf.size > 0 && buf.space[buf.size-1] == ',') { + buf.space[buf.size-1] = ' '; + } + cxBufferPutString(&buf, "}"); + return cx_mutstrn(buf.space, buf.size); +} + +static UIWIDGET container_create(UiObject *obj, const char *type, UiContainerArgs *args) { + const CxAllocator *a = obj->ctx->allocator; + UiWidget *widget = cxZalloc(a, sizeof(UiWidget)); + widget->obj = obj->widget->obj; + widget->type = cx_str(type); + widget->args = ui_container_args_to_string(obj->ctx, args); + widget->children = cxLinkedListCreate(a, NULL, CX_STORE_POINTERS); + widget->serialize = ui_container_serialize; + uic_object_push_container(obj, ui_widget_container(widget)); + ui_reg_widget((UiWidget*)widget); + + UiWidget *parent = obj->container_end->container; + cxListAdd(parent->children, widget); + + return widget; +} + +void ui_serialize_children(UiWidget *w, CxBuffer *buf) { + size_t numchildren = cxListSize(w->children); + if(numchildren > 0) { + cxBufferPutString(buf, ",\"children\":["); + CxIterator i = cxListIterator(w->children); + cx_foreach(UiWidget *, child, i) { + cxmutstr child_str = child->serialize(child); + cxBufferWrite(child_str.ptr, 1, child_str.length, buf); + if(i.index+1 < numchildren) { + cxBufferPut(buf, ','); + } + } + cxBufferPutString(buf, "]"); + } +} + +cxmutstr ui_container_serialize(UiWidget *w) { + CxBuffer buf; + cxBufferInit(&buf, NULL, 1024, NULL, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_FREE_CONTENTS); + + cxBufferPutString(&buf, "{\"type\":\""); + cxBufferPutString(&buf, w->type.ptr); + cxBufferPutString(&buf, "\",\"args\":\""); + cxBufferPutString(&buf, w->args.ptr); + cxBufferPutString(&buf, "\",\"obj\":"); + cxBufferPutString(&buf, w->obj->id.ptr); + cxBufferPutString(&buf, "\",\"id\":"); + cxBufferPutString(&buf, w->id.ptr); + cxBufferPutString(&buf, "\""); + + ui_serialize_children(w, &buf); + + cxBufferPutString(&buf, "}\n"); + + return cx_mutstrn(buf.space, buf.size); +} + +UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs *args) { + return container_create(obj, "vbox", args); +} + +UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs *args) { + return container_create(obj, "hbox", args); +} + +UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) { + return container_create(obj, "grid", args); +}