--- a/ui/win32/button.c Sun Dec 07 20:00:33 2025 +0100 +++ b/ui/win32/button.c Sat Dec 13 15:58:58 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 = { @@ -42,6 +44,22 @@ .destroy = w32_widget_default_destroy }; +static W32WidgetClass togglebutton_widget_class = { + .eventproc = ui_togglebutton_eventproc, + .enable = w32_widget_default_enable, + .show = w32_widget_default_show, + .get_preferred_size = ui_button_get_preferred_size, + .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); @@ -82,11 +100,12 @@ return size; } -void ui_button_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { +int ui_button_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { UiWidget *w = (UiWidget*)widget; - printf("button eventproc\n"); - fflush(stdout); + if (uMsg != WM_COMMAND) { + return 0; + } UiEvent e; e.obj = w->obj; @@ -100,4 +119,200 @@ if (w->callback) { w->callback(&e, w->callbackdata); } + + return 0; } + +static UIWIDGET create_togglebutton(UiObject *obj, UiToggleArgs *args, unsigned long type) { + 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 | type, + 0, 0, 100, 30, + parent, + (HMENU)1, + hInstance, + NULL); + ui_win32_set_ui_font(hwnd); + + W32Widget *widget = w32_widget_create(&togglebutton_widget_class, hwnd, sizeof(UiWidget)); + ui_container_add(container, widget, &layout); + + UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER); + if (var) { + UiInteger *i = var->value; + i->obj = widget; + i->get = ui_togglebutton_get; + i->set = ui_togglebutton_set; + if (i->value != 0) { + SendMessage(hwnd, BM_SETCHECK, BST_CHECKED, 0); + } + } + + UiWidget *btn = (UiWidget*)widget; + btn->obj = obj; + btn->var = var; + btn->callback = args->onchange; + btn->callbackdata = args->onchangedata; + + return widget; +} + +UIWIDGET ui_togglebutton_create(UiObject *obj, UiToggleArgs *args) { + return create_togglebutton(obj, args, BS_AUTOCHECKBOX | BS_PUSHLIKE); +} + +UIWIDGET ui_checkbox_create(UiObject *obj, UiToggleArgs *args) { + return create_togglebutton(obj, args, BS_AUTOCHECKBOX); +} + +UIWIDGET ui_switch_create(UiObject *obj, UiToggleArgs *args) { + return create_togglebutton(obj, args, BS_AUTOCHECKBOX); +} + +int64_t ui_togglebutton_get(UiInteger *i) { + UiWidget *btn = (UiWidget*)i->obj; + LRESULT state = SendMessage(btn->widget.hwnd, BM_GETCHECK, 0, 0); + i->value = state; + return state; +} + +void ui_togglebutton_set(UiInteger *i, int64_t v) { + UiWidget *btn = (UiWidget*)i->obj; + WPARAM state = v ? BST_CHECKED : BST_UNCHECKED; + SendMessage(btn->widget.hwnd, BM_SETCHECK, state, 0); + i->value = v; +} + +int ui_togglebutton_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if (uMsg != WM_COMMAND) { + return 0; + } + UiWidget *w = (UiWidget*)widget; + + 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 = SendMessage(w->widget.hwnd, BM_GETCHECK, 0, 0); + e.set = ui_get_setop(); + + if (w->callback) { + w->callback(&e, w->callbackdata); + } + + return 0; +} + + +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; +} + +int ui_radiobutton_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if (uMsg != WM_COMMAND) { + return 0; + } + UiWidget *w = (UiWidget*)widget; + + int checked = SendMessage(w->widget.hwnd, BM_GETCHECK, 0, 0); + if (!checked) { + return 0; // 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); + } + + return 0; +} + +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; +}