# HG changeset patch # User Olaf Wintermann # Date 1763548893 -3600 # Node ID ca3918f9c96bd3c3b545e97abad1e8e1e48ab17f # Parent 6fdcf1cbbec92f9775106e94768620229c8ea1f1 add first win32 listview/table code diff -r 6fdcf1cbbec9 -r ca3918f9c96b application/main.c --- a/application/main.c Wed Nov 19 09:20:18 2025 +0100 +++ b/application/main.c Wed Nov 19 11:41:33 2025 +0100 @@ -1196,38 +1196,34 @@ printf("button clicked\n"); } +typedef struct Person { + char *name; + char *email; +} Person; + +Person person1 = { "Alice", "alice@localhost" }; +Person person2 = { "Bob", "bob@localhost" }; + +void* person_getvalue(void *elm, int col) { + Person *p = (Person *) elm; + if (col == 0) { + return p->name; + } else { + return p->email; + } +} + void application_startup(UiEvent *event, void *data) { UiObject *obj = ui_window("Test w32", NULL); - ui_grid(obj, .margin = 10, .columnspacing = 10, .rowspacing = 10, .fill = TRUE) { - ui_button(obj, .label = "Test", .hfill = TRUE, .hexpand = TRUE, .colspan = 3, .margin = 10); - ui_newline(obj); - ui_textfield(obj, .varname = "text1", .hfill = TRUE, .hexpand = TRUE, .colspan = 2); - ui_newline(obj); - - ui_button(obj, .label = "Test 2-1"); - ui_button(obj, .label = "Test 2-2"); - ui_button(obj, .label = "Test 2-3"); - ui_newline(obj); - - ui_button(obj, .label = "Test 3XX", .colspan = 3, .fill = TRUE, .onclick = action_button); - ui_newline(obj); + UiList *list = ui_list_new(obj->ctx, "persons"); + ui_list_append(list, &person1); + ui_list_append(list, &person2); - ui_button(obj, .label = "End"); - ui_newline(obj); - - ui_togglebutton(obj, .label = "Test"); - ui_newline(obj); - - ui_checkbox(obj, .label = "Checkbox"); - ui_newline(obj); - - ui_hbox(obj, .spacing = 10, .colspan = 3, .hexpand = TRUE, .hfill = TRUE) { - ui_radiobutton(obj, .label = "Option 1", .varname = "radio1"); - ui_radiobutton(obj, .label = "Option 2", .varname = "radio1"); - ui_radiobutton(obj, .label = "Option 3", .varname = "radio1"); - } - + 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_model_free(obj->ctx, model); } diff -r 6fdcf1cbbec9 -r ca3918f9c96b ui/ui/tree.h --- a/ui/ui/tree.h Wed Nov 19 09:20:18 2025 +0100 +++ b/ui/ui/tree.h Wed Nov 19 11:41:33 2025 +0100 @@ -132,9 +132,9 @@ const char *name; const char *style_class; - UiList* list; + UiList *list; const char* varname; - UiModel* model; + UiModel *model; char **static_elements; size_t static_nelm; ui_getvaluefunc getvalue; @@ -143,15 +143,15 @@ ui_getstylefunc getstyle; void *getstyledata; ui_callback onactivate; - void* onactivatedata; + void *onactivatedata; ui_callback onselection; - void* onselectiondata; + void *onselectiondata; ui_callback ondragstart; - void* ondragstartdata; + void *ondragstartdata; ui_callback ondragcomplete; - void* ondragcompletedata; + void *ondragcompletedata; ui_callback ondrop; - void* ondropdata; + void *ondropdata; UiBool multiselection; UiMenuBuilder *contextmenu; ui_list_savefunc onsave; @@ -300,10 +300,10 @@ #define ui_breadcrumbbar(obj, ...) ui_breadcrumbbar_create(obj, &(UiListArgs) { __VA_ARGS__ } ) #define ui_sourcelist(obj, ...) ui_sourcelist_create(obj, &(UiSourceListArgs) { __VA_ARGS__ } ) -UIEXPORT UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args); -UIEXPORT UIWIDGET ui_table_create(UiObject* obj, UiListArgs *args); -UIEXPORT UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args); -UIEXPORT UIWIDGET ui_breadcrumbbar_create(UiObject* obj, UiListArgs *args); +UIEXPORT UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args); +UIEXPORT UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args); +UIEXPORT UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args); +UIEXPORT UIWIDGET ui_breadcrumbbar_create(UiObject *obj, UiListArgs *args); UIEXPORT void ui_listview_select(UIWIDGET listview, int index); UIEXPORT void ui_combobox_select(UIWIDGET dropdown, int index); diff -r 6fdcf1cbbec9 -r ca3918f9c96b ui/win32/list.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/win32/list.c Wed Nov 19 11:41:33 2025 +0100 @@ -0,0 +1,237 @@ +/* +* 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 "list.h" +#include "container.h" + + + +static W32WidgetClass listview_widget_class = { + .eventproc = ui_listview_eventproc, + .enable = w32_widget_default_enable, + .show = w32_widget_default_show, + .get_preferred_size = ui_listview_get_preferred_size, + .destroy = w32_widget_default_destroy +}; + +static void* strmodel_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) { + return col == 0 ? elm : NULL; +} + +static void* getvalue_wrapper(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) { + ui_getvaluefunc getvalue = (ui_getvaluefunc)userdata; + return getvalue(elm, col); +} + +static void* null_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) { + return NULL; +} + + +static UiListView* create_listview_widget(UiObject *obj, HWND hwnd, UiListArgs *args, UiBool table) { + UiListView *listview = w32_widget_create(&listview_widget_class, hwnd, sizeof(UiWidget)); + listview->widget.hwnd = hwnd; + listview->preferred_width = args->width ? args->width : 300; + listview->preferred_height = args->height ? args->height : 300; + listview->onactivate = args->onactivate; + listview->onactivatedata = args->onactivatedata; + listview->onselection = args->onselection; + listview->onselectiondata = args->onselectiondata; + listview->ondragstart = args->ondragstart; + listview->ondragstartdata = args->ondragstartdata; + listview->ondragcomplete = args->ondragcomplete; + listview->ondragcompletedata = args->ondragcompletedata; + listview->ondrop = args->ondrop; + listview->ondropdata = args->ondropdata; + listview->istable = table; + + ui_getvaluefunc2 getvalue = args->getvalue2; + void *getvaluedata = args->getvalue2data; + if(!getvalue) { + if(args->getvalue) { + getvalue = getvalue_wrapper; + getvaluedata = (void*)args->getvalue; + } else { + getvalue = table ? null_getvalue : strmodel_getvalue; + } + } + listview->getvalue = getvalue; + listview->getvaluedata = getvaluedata; + + listview->var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST); + + return listview; +} + +static UIWIDGET listview_create(UiObject *obj, UiListArgs *args, UiBool table) { + HINSTANCE hInstance = GetModuleHandle(NULL); + UiContainerPrivate *container = ui_obj_container(obj); + HWND parent = ui_container_get_parent(container); + UiLayout layout = UI_ARGS2LAYOUT(args); + + int type = table ? LVS_REPORT : LVS_LIST; + HWND hwnd = CreateWindowEx( + WS_EX_CLIENTEDGE, + WC_LISTVIEW, + "", + WS_CHILD | WS_VISIBLE | LVS_REPORT, + 0, 0, 100, 100, + parent, + (HMENU)1, + hInstance, + NULL); + ui_win32_set_ui_font(hwnd); + + ListView_SetExtendedListViewStyle( + hwnd, + LVS_EX_FULLROWSELECT //| LVS_EX_GRIDLINES + ); + + UiListView *listview = create_listview_widget(obj, hwnd, args, table); + ui_container_add(container, (W32Widget*)listview, &layout); + + // model + int numcolumns = 0; + if (table) { + if (args->model) { + listview->model = ui_model_copy(obj->ctx, args->model); + numcolumns = listview->model->columns; + } else { + listview->model = ui_model_new(obj->ctx); + } + } else { + UiModel *model = ui_model_new(obj->ctx); + ui_model_add_column(obj->ctx, model, UI_STRING, "Test", -1); + listview->model = model; + numcolumns = 1; + } + + ///* + UiModel *model = listview->model; + for (int i=0;itypes[i]; + char *title = model->titles[i]; + size_t titlelen = title ? strlen(title) : 0; + int size = model->columnsize[i]; + switch (type) { + default: { + col.mask = LVCF_TEXT | LVCF_WIDTH; + col.pszText = title; + col.cx = size > 0 ? size : titlelen*10+5; + break; + } + case UI_ICON: { + break; // TODO + } + } + ListView_InsertColumn(hwnd, i, &col); + } + + if (listview->var) { + UiList *list = listview->var->value; + list->obj = listview; + list->update = ui_listview_update; + list->getselection = ui_listview_getselection; + list->setselection = ui_listview_setselection; + + ui_listview_update(list, -1); + } + + return (W32Widget*)listview; +} + +void ui_listview_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + +} + +W32Size ui_listview_get_preferred_size(W32Widget *widget) { + UiListView *listview = (UiListView*)widget; + W32Size size; + size.width = listview->preferred_width; + size.height = listview->preferred_height; + return size; +} + +void ui_listview_update(UiList *list, int row) { + UiListView *listview = (UiListView*)list->obj; + HWND hwnd = listview->widget.hwnd; + UiModel *model = listview->model; + if (row < 0) { + ListView_DeleteAllItems(hwnd); + void *elm = list->first(list); + int row = 0; + while (elm) { + LVITEM item; + item.mask = LVIF_TEXT; + item.iItem = row; + item.iSubItem = 0; + int idx = -1; + for (int col=0;colcolumns;col++) { + UiBool freeResult = FALSE; + char *str = listview->getvalue(list, elm, row, col, listview->getvaluedata, &freeResult); + if (col == 0) { + item.pszText = str; + idx = ListView_InsertItem(hwnd, &item); + } else { + ListView_SetItemText(hwnd, idx, col, str); + } + + if (freeResult) { + free(str); + } + } + + elm = list->next(list); + row++; + } + + for (int i=0;icolumns;i++) { + ListView_SetColumnWidth(hwnd, i, LVSCW_AUTOSIZE); + } + } else { + // TODO + } +} + +UiListSelection ui_listview_getselection(UiList *list) { + UiListSelection sel = { 0, NULL }; + return sel; +} + +void ui_listview_setselection(UiList *list, UiListSelection selection) { + +} + +UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { + return listview_create(obj, args, FALSE); +} + +UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { + return listview_create(obj, args, TRUE); +} diff -r 6fdcf1cbbec9 -r ca3918f9c96b ui/win32/list.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/win32/list.h Wed Nov 19 11:41:33 2025 +0100 @@ -0,0 +1,75 @@ +/* +* 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 LIST_H +#define LIST_H + +#include "toolkit.h" +#include "../ui/tree.h" +#include "win32.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct UiListView { + W32Widget widget; + UiVar *var; + ui_getvaluefunc2 getvalue; + void *getvaluedata; + ui_getstylefunc getstyle; + void *getstyledata; + UiModel *model; + UiBool istable; + int preferred_width; + int preferred_height; + ui_callback onactivate; + void* onactivatedata; + ui_callback onselection; + void* onselectiondata; + ui_callback ondragstart; + void* ondragstartdata; + ui_callback ondragcomplete; + void* ondragcompletedata; + ui_callback ondrop; + void* ondropdata; +} UiListView; + +void ui_listview_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +W32Size ui_listview_get_preferred_size(W32Widget *widget); + +void ui_listview_update(UiList *list, int row); +UiListSelection ui_listview_getselection(UiList *list); +void ui_listview_setselection(UiList *list, UiListSelection selection); + +#ifdef __cplusplus +} +#endif + +#endif //_LIST_H \ No newline at end of file diff -r 6fdcf1cbbec9 -r ca3918f9c96b ui/win32/objs.mk --- a/ui/win32/objs.mk Wed Nov 19 09:20:18 2025 +0100 +++ b/ui/win32/objs.mk Wed Nov 19 11:41:33 2025 +0100 @@ -38,6 +38,7 @@ WIN32OBJ += button.obj WIN32OBJ += grid.obj WIN32OBJ += text.obj +WIN32OBJ += list.obj TOOLKITOBJS += $(WIN32OBJ:%=$(WIN32_OBJPRE)%) TOOLKITSOURCE += $(WIN32OBJ:%.obj=win32/%.c) diff -r 6fdcf1cbbec9 -r ca3918f9c96b ui/win32/toolkit.c --- a/ui/win32/toolkit.c Wed Nov 19 09:20:18 2025 +0100 +++ b/ui/win32/toolkit.c Wed Nov 19 11:41:33 2025 +0100 @@ -64,7 +64,10 @@ ui_window_init(); - INITCOMMONCONTROLSEX icex = { sizeof(icex), ICC_WIN95_CLASSES }; + INITCOMMONCONTROLSEX icex = { + sizeof(icex), + ICC_WIN95_CLASSES | ICC_LISTVIEW_CLASSES | ICC_TREEVIEW_CLASSES | ICC_BAR_CLASSES | ICC_TAB_CLASSES + }; InitCommonControlsEx(&icex); SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);