Wed, 19 Nov 2025 13:02:55 +0100
implement listview selection events (Win32)
| application/main.c | file | annotate | diff | comparison | revisions | |
| ui/win32/list.c | file | annotate | diff | comparison | revisions | |
| ui/win32/list.h | file | annotate | diff | comparison | revisions | |
| ui/win32/toolkit.c | file | annotate | diff | comparison | revisions |
--- a/application/main.c Wed Nov 19 12:17:59 2025 +0100 +++ b/application/main.c Wed Nov 19 13:02:55 2025 +0100 @@ -1213,6 +1213,15 @@ } } +void list_onselection(UiEvent *event, void *userdata) { + UiListSelection *sel = event->eventdata; + for (int i=0;i<sel->count;i++) { + printf("selection: %d\n", sel->rows[i]); + } + printf("\n"); + fflush(stdout); +} + void application_startup(UiEvent *event, void *data) { UiObject *obj = ui_window("Test w32", NULL); @@ -1222,7 +1231,7 @@ ui_grid(obj, .margin = 10, .columnspacing = 10, .rowspacing = 10, .fill = TRUE) { UiModel *model = ui_model(obj->ctx, UI_STRING, "Name", UI_STRING, "Email", -1); - ui_table(obj, .fill = TRUE, .varname = "persons", .model = model, .getvalue = person_getvalue); + ui_table(obj, .fill = TRUE, .varname = "persons", .model = model, .getvalue = person_getvalue, .onselection = list_onselection); ui_model_free(obj->ctx, model); }
--- a/ui/win32/list.c Wed Nov 19 12:17:59 2025 +0100 +++ b/ui/win32/list.c Wed Nov 19 13:02:55 2025 +0100 @@ -26,6 +26,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include <stdio.h> +#include <stdlib.h> + +#include <cx/array_list.h> + #include "list.h" #include "container.h" @@ -52,11 +57,14 @@ return NULL; } - +/* + * Creates an UiListView widget object and initializes it from the UiListArgs + */ static UiListView* create_listview_widget(UiObject *obj, HWND hwnd, UiListArgs *args, UiBool table) { UiListView *listview = w32_widget_create(&listview_widget_class, hwnd, sizeof(UiListView)); listview->widget.hwnd = hwnd; - listview->preferred_width = args->width ? args->width : 300; + listview->obj = obj; + listview->preferred_width = args->width ? args->width : 300; // 300: default width/height listview->preferred_height = args->height ? args->height : 300; listview->onactivate = args->onactivate; listview->onactivatedata = args->onactivatedata; @@ -70,6 +78,7 @@ listview->ondropdata = args->ondropdata; listview->istable = table; + // convert ui_getvaluefunc into ui_getvaluefunc2 if necessary ui_getvaluefunc2 getvalue = args->getvalue2; void *getvaluedata = args->getvalue2data; if(!getvalue) { @@ -105,7 +114,6 @@ hInstance, NULL); ui_win32_set_ui_font(hwnd); - ListView_SetExtendedListViewStyle( hwnd, LVS_EX_FULLROWSELECT //| LVS_EX_GRIDLINES @@ -114,7 +122,8 @@ UiListView *listview = create_listview_widget(obj, hwnd, args, table); ui_container_add(container, (W32Widget*)listview, &layout); - // model + // init list model + // always initialize listview->model int numcolumns = 0; if (table) { if (args->model) { @@ -130,6 +139,7 @@ numcolumns = 1; } + // create columns UiModel *model = listview->model; for (int i=0;i<numcolumns;i++) { LVCOLUMN col; @@ -151,6 +161,7 @@ ListView_InsertColumn(hwnd, i, &col); } + // bind the listview to the provided UiList if (listview->var) { UiList *list = listview->var->value; list->obj = listview; @@ -164,8 +175,80 @@ return (W32Widget*)listview; } +static UiListSelection listview_get_selection(UiListView *listview) { + UiListSelection sel = { 0, NULL }; + HWND hwnd = listview->widget.hwnd; + + CX_ARRAY_DECLARE(int, indices); + cx_array_initialize(indices, 8); + + int index = -1; + while ((index = ListView_GetNextItem(hwnd, index, LVNI_SELECTED)) != -1) { + cx_array_simple_add(indices, index); + } + + if (indices_size > 0) { + sel.rows = indices; + sel.count = indices_size; + } + + return sel; +} + +// listview class event proc void ui_listview_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + UiListView *listview = (UiListView*)widget; + switch (uMsg) { + case WM_NOTIFY: { + LPNMHDR hdr = (LPNMHDR)lParam; + switch (hdr->code) { + case LVN_ITEMCHANGED: { + LPNMLISTVIEW lv = (LPNMLISTVIEW)lParam; + int row = lv->iItem; + if ((lv->uChanged & LVIF_STATE) && (lv->uNewState & LVIS_SELECTED) && listview->onselection) { + UiListSelection sel = listview_get_selection(listview); + UiEvent event; + event.obj = listview->obj; + event.window = listview->obj->window; + event.document = listview->obj->ctx->document; + event.eventdata = &sel; + event.eventdatatype = UI_EVENT_DATA_LIST_SELECTION; + event.intval = row; + event.set = ui_get_setop(); + listview->onselection(&event, listview->onselectiondata); + + ui_listselection_free(sel); + } + break; + } + case LVN_ITEMACTIVATE: { + LPNMLISTVIEW lv = (LPNMLISTVIEW)lParam; + int row = lv->iItem; + if (listview->onactivate) { + UiEvent event; + event.obj = listview->obj; + event.window = listview->obj->window; + event.document = listview->obj->ctx->document; + event.eventdata = NULL; + event.eventdatatype = UI_EVENT_DATA_LIST_ELM; + event.intval = row; + event.set = ui_get_setop(); + + if (listview->var) { + UiList *list = listview->var->value; + event.eventdata = list->get(list, row); + event.eventdatatype = UI_EVENT_DATA_LIST_ELM; + } + + listview->onactivate(&event, listview->onactivatedata); + } + break; + } + } + break; + } + } } W32Size ui_listview_get_preferred_size(W32Widget *widget) { @@ -176,6 +259,13 @@ return size; } +/* + * Creates and inserts an LVITEM + * + * list: An UiList bound to an UiListView + * row: row index + * elm: list element (same as list->get(list, row)) + */ static void insert_item(UiList *list, int row, void *elm) { UiListView *listview = (UiListView*)list->obj; HWND hwnd = listview->widget.hwnd; @@ -188,6 +278,8 @@ int idx = -1; for (int col=0;col<model->columns;col++) { UiBool freeResult = FALSE; + // convert the list element to a value, that can be displayed in the list view + // TODO: handle all model types char *str = listview->getvalue(list, elm, row, col, listview->getvaluedata, &freeResult); if (col == 0) { item.pszText = str; @@ -202,6 +294,12 @@ } } +/* + * UiList->update function + * + * Updates one or all rows + * row: list index or -1 for updating all rows + */ void ui_listview_update(UiList *list, int row) { UiListView *listview = (UiListView*)list->obj; HWND hwnd = listview->widget.hwnd; @@ -221,24 +319,27 @@ insert_item(list, row, elm); } + // re-adjust all columns for (int i=0;i<model->columns;i++) { ListView_SetColumnWidth(hwnd, i, LVSCW_AUTOSIZE); } } UiListSelection ui_listview_getselection(UiList *list) { - UiListSelection sel = { 0, NULL }; - return sel; + UiListView *listview = (UiListView*)list->obj; + return listview_get_selection(listview); } void ui_listview_setselection(UiList *list, UiListSelection selection) { } +// public API UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { return listview_create(obj, args, FALSE); } +// public API UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { return listview_create(obj, args, TRUE); }
--- a/ui/win32/list.h Wed Nov 19 12:17:59 2025 +0100 +++ b/ui/win32/list.h Wed Nov 19 13:02:55 2025 +0100 @@ -40,6 +40,7 @@ typedef struct UiListView { W32Widget widget; + UiObject *obj; UiVar *var; ui_getvaluefunc2 getvalue; void *getvaluedata;
--- a/ui/win32/toolkit.c Wed Nov 19 12:17:59 2025 +0100 +++ b/ui/win32/toolkit.c Wed Nov 19 13:02:55 2025 +0100 @@ -129,7 +129,6 @@ } LRESULT CALLBACK ui_default_eventproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - printf("default event proc: %d, %d\n", (int)hwnd, (int)uMsg); fflush(stdout); W32Widget *widget = (W32Widget*)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (widget && widget->wclass->eventproc) { @@ -148,6 +147,15 @@ } break; } + case WM_NOTIFY: { + LPNMHDR hdr = (LPNMHDR)lParam; + HWND hwndCtrl = hdr->hwndFrom; + W32Widget *cmdWidget = (W32Widget*)GetWindowLongPtr(hwndCtrl, GWLP_USERDATA); + if (cmdWidget && cmdWidget->wclass->eventproc) { + cmdWidget->wclass->eventproc(cmdWidget, hwnd, uMsg, wParam, lParam); + } + break; + } case WM_SIZE: { int width = LOWORD(lParam); int height = HIWORD(lParam);