ui/win32/list.c

Wed, 19 Nov 2025 11:41:33 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Wed, 19 Nov 2025 11:41:33 +0100
changeset 917
ca3918f9c96b
child 918
7d3dd5aacfda
permissions
-rw-r--r--

add first win32 listview/table code

/*
* 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;i<numcolumns;i++) {
        LVCOLUMN col;
        UiModelType type = model->types[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;col<model->columns;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;i<model->columns;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);
}

mercurial