add button (Server)

Thu, 11 Dec 2025 19:52:44 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Thu, 11 Dec 2025 19:52:44 +0100
changeset 985
93f07ccfd997
parent 984
2cf5e6d55013
child 986
6f7600c2b9e1

add button (Server)

application/main.c file | annotate | diff | comparison | revisions
ui/common/utils.c file | annotate | diff | comparison | revisions
ui/common/utils.h file | annotate | diff | comparison | revisions
ui/server/args.c file | annotate | diff | comparison | revisions
ui/server/args.h file | annotate | diff | comparison | revisions
ui/server/button.c file | annotate | diff | comparison | revisions
ui/server/button.h file | annotate | diff | comparison | revisions
ui/server/container.c file | annotate | diff | comparison | revisions
ui/server/container.h file | annotate | diff | comparison | revisions
ui/server/objs.mk file | annotate | diff | comparison | revisions
ui/server/toolkit.c file | annotate | diff | comparison | revisions
ui/server/toolkit.h file | annotate | diff | comparison | revisions
ui/server/window.c file | annotate | diff | comparison | revisions
--- a/application/main.c	Wed Dec 10 22:22:55 2025 +0100
+++ b/application/main.c	Thu Dec 11 19:52:44 2025 +0100
@@ -1292,6 +1292,7 @@
 
 void application_startup(UiEvent *event, void *userdata) {
     UiObject *obj = ui_window("Test", NULL);
+    ui_button(obj, .label = "Button");
     ui_show(obj);
 }
 
--- a/ui/common/utils.c	Wed Dec 10 22:22:55 2025 +0100
+++ b/ui/common/utils.c	Thu Dec 11 19:52:44 2025 +0100
@@ -29,7 +29,11 @@
 #include "utils.h"
 #include "properties.h"
 
+#include <stdio.h>
+#include <string.h>
+
 #include <cx/string.h>
+#include <cx/buffer.h>
 
 UiPathElm* ui_default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) {
     cxstring *pathelms;
@@ -83,3 +87,65 @@
         }
     }
 }
+
+// from UCX json.c
+static cxmutstr escape_string(cxstring str, bool escape_slash) {
+    // note: this function produces the string without enclosing quotes
+    // the reason is that we don't want to allocate memory just for that
+    CxBuffer buf = {0};
+
+    bool all_printable = true;
+    for (size_t i = 0; i < str.length; i++) {
+        unsigned char c = str.ptr[i];
+        bool escape = c < 0x20 || c == '\\' || c == '"'
+            || (escape_slash && c == '/');
+
+        if (all_printable && escape) {
+            size_t capa = str.length + 32;
+            char *space = cxMallocDefault(capa);
+            if (space == NULL) return cx_mutstrn(NULL, 0);
+            cxBufferInit(&buf, space, capa, NULL, CX_BUFFER_AUTO_EXTEND);
+            cxBufferWrite(str.ptr, 1, i, &buf);
+            all_printable = false;
+        }
+        if (escape) {
+            cxBufferPut(&buf, '\\');
+            if (c == '\"') {
+                cxBufferPut(&buf, '\"');
+            } else if (c == '\n') {
+                cxBufferPut(&buf, 'n');
+            } else if (c == '\t') {
+                cxBufferPut(&buf, 't');
+            } else if (c == '\r') {
+                cxBufferPut(&buf, 'r');
+            } else if (c == '\\') {
+                cxBufferPut(&buf, '\\');
+            } else if (c == '/') {
+                cxBufferPut(&buf, '/');
+            } else if (c == '\f') {
+                cxBufferPut(&buf, 'f');
+            } else if (c == '\b') {
+                cxBufferPut(&buf, 'b');
+            } else {
+                char code[6];
+                snprintf(code, sizeof(code), "u%04x", (unsigned int) c);
+                cxBufferPutString(&buf, code);
+            }
+        } else if (!all_printable) {
+            cxBufferPut(&buf, c);
+        }
+    }
+    cxmutstr ret;
+    if (all_printable) {
+        // don't copy the string when we don't need to escape anything
+        ret = cx_mutstrn((char*)str.ptr, str.length);
+    } else {
+        ret = cx_mutstrn(buf.space, buf.size);
+    }
+    cxBufferDestroy(&buf);
+    return ret;
+}
+
+cxmutstr ui_escape_string(cxstring str) {
+    return escape_string(str, FALSE);
+}
--- a/ui/common/utils.h	Wed Dec 10 22:22:55 2025 +0100
+++ b/ui/common/utils.h	Thu Dec 11 19:52:44 2025 +0100
@@ -31,6 +31,8 @@
 #include "../ui/toolkit.h"
 #include "../ui/text.h"
 
+#include <cx/string.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -43,6 +45,8 @@
  */
 void ui_get_window_default_width(int *width, int *height);
 
+cxmutstr ui_escape_string(cxstring str);
+
 
 #ifdef __cplusplus
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/server/args.c	Thu Dec 11 19:52:44 2025 +0100
@@ -0,0 +1,57 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "args.h"
+
+#include <stdio.h>
+
+#include "toolkit.h"
+#include "../common/utils.h"
+
+void ui_argstr_add_int(CxBuffer *buf, const char *name, int i) {
+    char n[32];
+    snprintf(n, 32, "%i", i);
+    
+    cxBufferPutString(buf, "\"");
+    cxBufferPutString(buf, name);
+    cxBufferPutString(buf, "\":");
+    cxBufferPutString(buf, n);
+    cxBufferPutString(buf, ",");
+}
+
+void ui_argstr_add_str(CxBuffer *buf, const char *name, const char *value) {
+    cxmutstr value_escaped = ui_escape_string(cx_str(value));
+    cxBufferPutString(buf, "\"");
+    cxBufferPutString(buf, name);
+    cxBufferPutString(buf, "\":\"");
+    cxBufferPutString(buf, value_escaped.ptr);
+    cxBufferPutString(buf, "\",");
+    if(value != value_escaped.ptr) {
+        free(value_escaped.ptr);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/server/args.h	Thu Dec 11 19:52:44 2025 +0100
@@ -0,0 +1,70 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ARGS_H
+#define ARGS_H
+
+#include <cx/buffer.h>
+#include <cx/string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void ui_argstr_add_int(CxBuffer *buf, const char *name, int i);
+void ui_argstr_add_str(CxBuffer *buf, const char *name, const char *value);
+    
+#define UI_STANDARD_ARGS(buf, args) \
+    if(args->fill) cxBufferPutString(buf, "\"fill\":true,"); \
+    if(args->hexpand) cxBufferPutString(buf, "\"hexpand\":true,"); \
+    if(args->vexpand) cxBufferPutString(buf, "\"vexpand\":true,"); \
+    if(args->hfill) cxBufferPutString(buf, "\"hfill\":true,"); \
+    if(args->vfill) cxBufferPutString(buf, "\"vfill\":true,"); \
+    if(args->override_defaults) cxBufferPutString(buf, "\"override_defaults\":true,"); \
+    if(args->margin != 0) ui_argstr_add_int(buf, "margin", args->margin); \
+    if(args->margin_left != 0) ui_argstr_add_int(buf, "margin_left", args->margin_left); \
+    if(args->margin_right != 0) ui_argstr_add_int(buf, "margin_right", args->margin_right); \
+    if(args->margin_top != 0) ui_argstr_add_int(buf, "margin_top", args->margin_top); \
+    if(args->margin_bottom != 0) ui_argstr_add_int(buf, "margin_bottom", args->margin_bottom); \
+    if(args->colspan != 0) ui_argstr_add_int(buf, "colspan", args->colspan); \
+    if(args->rowspan != 0) ui_argstr_add_int(buf, "rowspan", args->rowspan); \
+    if(args->name) ui_argstr_add_str(buf, "name", args->name); \
+    if(args->style_class) ui_argstr_add_str(buf, "style_class", args->style_class)
+
+#define UI_LABEL_ARGS(buf, args) \
+    if(args->label) ui_argstr_add_str(buf, "label", args->label); \
+    if(args->icon) ui_argstr_add_str(buf, "icon", args->icon); \
+    if(args->tooltip) ui_argstr_add_str(buf, "tooltip", args->tooltip)
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ARGS_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/server/button.c	Thu Dec 11 19:52:44 2025 +0100
@@ -0,0 +1,106 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "button.h"
+#include <cx/buffer.h>
+#include <cx/printf.h>
+
+#include "args.h"
+#include "container.h"
+
+cxmutstr ui_button_args_to_string(UiContext *ctx, UiButtonArgs *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_LABEL_ARGS(&buf, args);
+    // TODO: labeltype, states
+    
+    if(buf.size > 0 && buf.space[buf.size-1] == ',') {
+        buf.space[buf.size-1] = ' ';
+    }
+    cxBufferPutString(&buf, "}");
+    return cx_mutstrn(buf.space, buf.size);
+}
+
+cxmutstr ui_toggle_args_to_string(UiContext *ctx, UiToggleArgs *args) {
+    
+}
+
+
+UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs *args) {
+    UiCallbackWidget *widget = cxZalloc(obj->ctx->allocator, sizeof(UiCallbackWidget));
+    widget->widget.obj = obj->widget->obj;
+    widget->widget.args = ui_button_args_to_string(obj->ctx, args);
+    widget->widget.serialize = (ui_serialize_func)ui_button_serialize;
+    widget->callback = args->onclick;
+    widget->userdata = args->onclickdata;
+    ui_reg_widget((UiWidget*)widget);
+    
+    UiWidget *parent = obj->container_end->container;
+    cxListAdd(parent->children, widget);
+    
+    return (UiWidget*)widget;
+}
+
+cxmutstr ui_button_serialize(UiCallbackWidget *w) {
+    CxBuffer buf;
+    cxBufferInit(&buf, NULL, 1024, NULL, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_FREE_CONTENTS);
+    
+    cxBufferPutString(&buf, "{\"type\":\"button\", \"obj\":\"");
+    cxBufferPutString(&buf, w->widget.obj->id.ptr);
+    cxBufferPutString(&buf, "\", \"id\":\"");
+    cxBufferPutString(&buf, w->widget.id.ptr);
+    cxBufferPutString(&buf, "\", \"args\":");
+    cxBufferPutString(&buf, w->widget.args.ptr);
+    cxBufferPutString(&buf, "}\n");
+    
+    return cx_mutstrn(buf.space, buf.size);
+}
+
+static UIWIDGET togglebutton_create(UiObject *obj, const char *type, UiToggleArgs *args) {
+    return NULL;
+}
+
+UIWIDGET ui_togglebutton_create(UiObject *obj, UiToggleArgs *args) {
+    return togglebutton_create(obj, "togglebutton", args);
+}
+
+UIWIDGET ui_checkbox_create(UiObject *obj, UiToggleArgs *args) {
+    return togglebutton_create(obj, "checkbox", args);
+}
+
+UIWIDGET ui_switch_create(UiObject *obj, UiToggleArgs *args) {
+    return togglebutton_create(obj, "switch", args);
+}
+
+UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs *args) {
+    return togglebutton_create(obj, "radiobutton", args);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/server/button.h	Thu Dec 11 19:52:44 2025 +0100
@@ -0,0 +1,50 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BUTTON_H
+#define BUTTON_H
+
+#include "toolkit.h"
+
+#include "../ui/button.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+cxmutstr ui_button_args_to_string(UiContext *ctx, UiButtonArgs *args);
+cxmutstr ui_toggle_args_to_string(UiContext *ctx, UiToggleArgs *args);
+
+cxmutstr ui_button_serialize(UiCallbackWidget *w);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BUTTON_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/server/container.c	Thu Dec 11 19:52:44 2025 +0100
@@ -0,0 +1,35 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "container.h"
+
+UiContainerX* ui_widget_container(UiWidget *w) {
+    UiContainerX *container = cxZalloc(w->obj->ctx->allocator, sizeof(UiContainerX));
+    container->container = w;
+    return container;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/server/container.h	Thu Dec 11 19:52:44 2025 +0100
@@ -0,0 +1,46 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CONTAINER_H
+#define CONTAINER_H
+
+#include "toolkit.h"
+#include "../common/container.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+UiContainerX* ui_widget_container(UiWidget *w);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONTAINER_H */
+
--- a/ui/server/objs.mk	Wed Dec 10 22:22:55 2025 +0100
+++ b/ui/server/objs.mk	Thu Dec 11 19:52:44 2025 +0100
@@ -31,7 +31,10 @@
 
 SERVEROBJ = toolkit.o
 SERVEROBJ += widget.o
+SERVEROBJ += args.o
 SERVEROBJ += window.o
+SERVEROBJ += container.o
+SERVEROBJ += button.o
 SERVEROBJ += image.o
 
 TOOLKITOBJS += $(SERVEROBJ:%=$(SERVER_OBJPRE)%)
--- a/ui/server/toolkit.c	Wed Dec 10 22:22:55 2025 +0100
+++ b/ui/server/toolkit.c	Thu Dec 11 19:52:44 2025 +0100
@@ -114,8 +114,8 @@
         uic_message_send(message_handler, msg);
         free(msg.ptr);
     }
-    cxmutstr msg = cx_asprintf("{\"type\":\"show\", \"obj\":\"%s\"}", obj->widget->obj->id.ptr);
-    obj->widget->visible = TRUE;
+    cxmutstr msg = cx_asprintf("{\"type\":\"show\", \"obj\":\"%s\"}\n", obj->widget->obj->id.ptr);
+    obj->widget->invisible = FALSE;
     uic_message_send(message_handler, msg);
     free(msg.ptr);
 }
--- a/ui/server/toolkit.h	Wed Dec 10 22:22:55 2025 +0100
+++ b/ui/server/toolkit.h	Thu Dec 11 19:52:44 2025 +0100
@@ -77,12 +77,19 @@
     UiVarType var_type;
     CxList *children;
     cxmutstr args;
-    UiBool visible;
-    UiBool enabled;
+    UiBool invisible;
+    UiBool disabled;
     UiBool sent;
     ui_serialize_func serialize;
 };
 
+typedef struct UiCallbackWidget {
+    UiWidget widget;
+    cxstring type;
+    ui_callback callback;
+    void *userdata;
+} UiCallbackWidget; 
+
 void ui_server_message_received(cxstring msg);
 
 UiSrvObj* ui_create_server_object(UiContext *ctx);
--- a/ui/server/window.c	Wed Dec 10 22:22:55 2025 +0100
+++ b/ui/server/window.c	Thu Dec 11 19:52:44 2025 +0100
@@ -27,6 +27,7 @@
  */
 
 #include "window.h"
+#include "container.h"
 
 #include "../common/object.h"
 
@@ -40,9 +41,11 @@
     window->widget.obj = ui_create_server_object(obj->ctx);
     window->widget.children = cxLinkedListCreate(a, NULL, CX_STORE_POINTERS);
     window->widget.serialize = (ui_serialize_func)ui_window_serialize;
+    window->widget.invisible = TRUE;
     window->title = cx_strdup_a(a, title);
     
     obj->widget = (UiWidget*)window;
+    uic_object_push_container(obj, ui_widget_container((UiWidget*)window));
     ui_reg_widget(obj->widget);
     
     return obj;
@@ -94,6 +97,19 @@
         cxBufferPutString(&buf, "\"");
     }
     
+    size_t numchildren = cxListSize(w->widget.children);
+    if(numchildren > 0) {
+        cxBufferPutString(&buf, ",\"children\":[");
+        CxIterator i = cxListIterator(w->widget.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, "]");
+    }
     
     cxBufferPutString(&buf, "}\n");
     

mercurial