--- a/ui/win32/button.c Wed Nov 12 18:04:21 2025 +0100 +++ b/ui/win32/button.c Thu Nov 13 21:29:00 2025 +0100 @@ -32,6 +32,8 @@ #include <stdio.h> #include <stdlib.h> +#include <cx/array_list.h> + #include <commctrl.h> static W32WidgetClass button_widget_class = { @@ -50,6 +52,14 @@ .destroy = w32_widget_default_destroy }; +static W32WidgetClass radiobutton_widget_class = { + .eventproc = ui_radiobutton_eventproc, + .enable = w32_widget_default_enable, + .show = w32_widget_default_show, + .get_preferred_size = ui_button_get_preferred_size, + .destroy = w32_widget_default_destroy +}; + UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs *args) { HINSTANCE hInstance = GetModuleHandle(NULL); UiContainerPrivate *container = ui_obj_container(obj); @@ -196,3 +206,107 @@ w->callback(&e, w->callbackdata); } } + + +UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs *args) { + HINSTANCE hInstance = GetModuleHandle(NULL); + UiContainerPrivate *container = ui_obj_container(obj); + HWND parent = ui_container_get_parent(container); + UiLayout layout = UI_ARGS2LAYOUT(args); + + HWND hwnd = CreateWindow( + "BUTTON", + args->label, + WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON | WS_GROUP , + 0, 0, 100, 30, + parent, + (HMENU)1, + hInstance, + NULL); + ui_win32_set_ui_font(hwnd); + + W32Widget *widget = w32_widget_create(&radiobutton_widget_class, hwnd, sizeof(UiWidget)); + ui_container_add(container, widget, &layout); + UiWidget *btn = (UiWidget*)widget; + + UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER); + if (var) { + UiInteger *i = var->value; + // Use a CxList as binding object (i->obj) and add all radiobuttons to it + // The first radiobutton, which binds to this var, creates the CxList + CxList *group = NULL; + if (i->obj) { + group = i->obj; + } else { + group = cxArrayListCreate(obj->ctx->allocator, NULL, CX_STORE_POINTERS, 8); + i->obj = group; + } + + cxListAdd(group, btn); + if (i->value == cxListSize(group)) { + // TODO: select + } + } + + btn->obj = obj; + btn->var = var; + btn->callback = args->onchange; + btn->callbackdata = args->onchangedata; + + return widget; +} + +void ui_radiobutton_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if (uMsg != WM_COMMAND) { + return; + } + UiWidget *w = (UiWidget*)widget; + + int checked = SendMessage(w->widget.hwnd, BM_GETCHECK, 0, 0); + if (!checked) { + return; // ignore uncheck events + } + + int b = 0; + if (w->var) { + UiInteger *i = w->var->value; + CxList *group = i->obj; + // Find selected index and uncheck all radiobuttons in this group + // that are not this event widget + CxIterator iter = cxListIterator(group); + cx_foreach(UiWidget *, radiobutton, iter) { + if (radiobutton == w) { + i->value = iter.index+1; + b = i->value; + } else { + SendMessage(radiobutton->widget.hwnd, BM_SETCHECK, BST_UNCHECKED, 0); + } + } + } + + UiEvent e; + e.obj = w->obj; + e.document = e.obj->ctx->document; + e.window = e.obj->window; + e.eventdata = NULL; + e.eventdatatype = 0; + e.intval = b; + e.set = ui_get_setop(); + + if (w->callback) { + w->callback(&e, w->callbackdata); + } +} + +int64_t ui_radiobutton_get(UiInteger *i) { + return i->value; +} + +void ui_radiobutton_set(UiInteger *i, int64_t v) { + CxList *group = i->obj; + CxIterator iter = cxListIterator(group); + cx_foreach(UiWidget *, radiobutton, iter) { + SendMessage(radiobutton->widget.hwnd, BM_SETCHECK, iter.index+1 == v ? BST_CHECKED : BST_UNCHECKED, 0); + } + i->value = v; +}