# HG changeset patch # User Olaf Wintermann # Date 1696267866 -7200 # Node ID 320d85f3cd1488e04062ae51a4f13e63a6b15e1e # Parent 0f2e69873875d65540335839f1f772ffcf22361a add listview (WinUI3) diff -r 0f2e69873875 -r 320d85f3cd14 make/vs/testapp/main.c --- a/make/vs/testapp/main.c Mon Oct 02 10:10:09 2023 +0200 +++ b/make/vs/testapp/main.c Mon Oct 02 19:31:06 2023 +0200 @@ -40,6 +40,7 @@ UiInteger* radio; UiString* text; UiString* password; + UiList* list; } WindowData; void action1(UiEvent* event, void* data) { @@ -67,6 +68,24 @@ printf("onchange: %d\n", event->intval); } +void action_listselection_changed(UiEvent* event, void* data) { + printf("selection changed\n"); + UiListSelection* sel = event->eventdata; + for (int i = 0; i < sel->count; i++) { + int row = sel->rows[i]; + printf("row: %d\n", row); + } +} + +void action_onactivate(UiEvent* event, void* Data) { + printf("activate\n"); + UiListSelection* sel = event->eventdata; + for (int i = 0; i < sel->count; i++) { + int row = sel->rows[i]; + printf("row: %d\n", row); + } +} + void application_startup(UiEvent* event, void* data) { UiObject* obj = ui_window("Test", NULL); WindowData* wdata = ui_malloc(obj->ctx, sizeof(WindowData)); @@ -76,6 +95,14 @@ wdata->radio = ui_int_new(obj->ctx, "radio"); wdata->text = ui_string_new(obj->ctx, "text"); wdata->password = ui_string_new(obj->ctx, "password"); + wdata->list = ui_list_new(obj->ctx, "list"); + + ui_list_append(wdata->list, "Hello"); + ui_list_append(wdata->list, "World"); + ui_list_append(wdata->list, "Item3"); + ui_list_append(wdata->list, "Item4"); + ui_list_append(wdata->list, "Item5"); + ui_list_append(wdata->list, "Item6"); ui_scrolledwindow0(obj) { ui_grid(obj, .margin = 10, .columnspacing = 5, .rowspacing = 20) { @@ -114,11 +141,15 @@ } ui_newline(obj); - ui_expander(obj, .label = "Expand", .colspan = 3, .margin = 10, .spacing = 5, .isexpanded = true) { + /* + ui_expander(obj, .label = "Expand", .colspan = 3, .margin = 10, .spacing = 5, .isexpanded = false) { ui_button(obj, .label = "Button1", .onclick = action1, .onclickdata = "action1"); ui_button(obj, .label = "Button1", .onclick = action1, .onclickdata = "action1"); ui_button(obj, .label = "Button1", .onclick = action1, .onclickdata = "action1"); } + */ + + ui_listview(obj, .list = wdata->list, .hexpand = true, .vexpand = true, .multiselection = true, .onselection= action_listselection_changed, .onactivate= action_onactivate); } } diff -r 0f2e69873875 -r 320d85f3cd14 ui/ui/toolkit.h --- a/ui/ui/toolkit.h Mon Oct 02 10:10:09 2023 +0200 +++ b/ui/ui/toolkit.h Mon Oct 02 19:31:06 2023 +0200 @@ -87,11 +87,9 @@ class UiWidget { public: winrt::Microsoft::UI::Xaml::UIElement uielement; - - void* obj; - void(*event_func)(void*, void*) = nullptr; - void* event_data = nullptr; - + void* data1 = nullptr; + void* data2 = nullptr; + void* data3 = nullptr; UiWidget(winrt::Microsoft::UI::Xaml::UIElement& elm); }; diff -r 0f2e69873875 -r 320d85f3cd14 ui/ui/tree.h --- a/ui/ui/tree.h Mon Oct 02 10:10:09 2023 +0200 +++ b/ui/ui/tree.h Mon Oct 02 19:31:06 2023 +0200 @@ -39,6 +39,8 @@ typedef struct UiListCallbacks UiListCallbacks; typedef struct UiListSelection UiListSelection; +typedef struct UiListArgs UiListArgs; + typedef enum UiModelType { UI_STRING = 0, UI_INTEGER, @@ -108,24 +110,50 @@ int *rows; }; +struct UiListArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + int colspan; + int rowspan; + + UiList* list; + const char* varname; + UiModel* model; + ui_getvaluefunc getvalue; + ui_callback onactivate; + void* onactivatedata; + ui_callback onselection; + void* onselectiondata; + UiBool multiselection; +}; + UiModel* ui_model(UiContext *ctx, ...); void ui_model_free(UiContext *ctx, UiModel *mi); -UIWIDGET ui_listview(UiObject *obj, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata); -UIWIDGET ui_listview_str(UiObject *obj, UiList *list, ui_callback f, void *udata); -UIWIDGET ui_listview_nv(UiObject *obj, char *listname, ui_getvaluefunc getvalue, ui_callback f, void *udata); +#define ui_listview(obj, ...) ui_listview_create(obj, (UiListArgs) { __VA_ARGS__ } ) +#define ui_table(obj, ...) ui_table_create(obj, (UiListArgs) { __VA_ARGS__ } ) +#define ui_combobox(obj, ...) ui_combobox_create(obj, (UiListArgs) { __VA_ARGS__ } ) -UIWIDGET ui_table(UiObject *obj, UiList *data, UiModel *model, UiListCallbacks cb); -UIWIDGET ui_table_nv(UiObject *obj, char *varname, UiModel *model, UiListCallbacks cb); +UIWIDGET ui_listview_create(UiObject* obj, UiListArgs args); +UIWIDGET ui_table_create(UiObject* obj, UiListArgs args); +UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs); + +UIWIDGET ui_listview_deprecated(UiObject *obj, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata); +UIWIDGET ui_listview_str_deprecated(UiObject *obj, UiList *list, ui_callback f, void *udata); +UIWIDGET ui_listview_nv_deprecated(UiObject *obj, char *listname, ui_getvaluefunc getvalue, ui_callback f, void *udata); + +UIWIDGET ui_table_deprecated(UiObject *obj, UiList *data, UiModel *model, UiListCallbacks cb); +UIWIDGET ui_table_nv_deprecated(UiObject *obj, char *varname, UiModel *model, UiListCallbacks cb); void ui_table_dragsource(UIWIDGET tablewidget, int actions, char *target0, ...); void ui_table_dragsource_a(UIWIDGET tablewidget, int actions, char **targets, int nelm); void ui_table_dragdest(UIWIDGET tablewidget, int actions, char *target0, ...); void ui_table_dragdest_a(UIWIDGET tablewidget, int actions, char **targets, int nelm); -UIWIDGET ui_combobox(UiObject *obj, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata); -UIWIDGET ui_combobox_str(UiObject *obj, UiList *list, ui_callback f, void *udata); -UIWIDGET ui_combobox_nv(UiObject *obj, char *varname, ui_getvaluefunc getvalue, ui_callback f, void *udata); +UIWIDGET ui_combobox_deprecated(UiObject *obj, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata); +UIWIDGET ui_combobox_str_deprecated(UiObject *obj, UiList *list, ui_callback f, void *udata); +UIWIDGET ui_combobox_nv_deprecated(UiObject *obj, char *varname, ui_getvaluefunc getvalue, ui_callback f, void *udata); #ifdef __cplusplus } diff -r 0f2e69873875 -r 320d85f3cd14 ui/winui/button.cpp --- a/ui/winui/button.cpp Mon Oct 02 10:10:09 2023 +0200 +++ b/ui/winui/button.cpp Mon Oct 02 19:31:06 2023 +0200 @@ -68,19 +68,16 @@ // register callback if (args.onclick) { - widget->obj = obj; - widget->event_func = (ui_eventfunc)args.onclick; - widget->event_data = args.onclickdata; - button.Click([widget](IInspectable const& sender, RoutedEventArgs) { - ui_callback cb = (ui_callback)widget->event_func; - + ui_callback cbfunc = args.onclick; + void* cbdata = args.onclickdata; + button.Click([cbfunc, cbdata, obj](IInspectable const& sender, RoutedEventArgs) { UiEvent evt; - evt.obj = (UiObject*)widget->obj; - evt.window = evt.obj->window; - evt.document = evt.obj->ctx->document; + evt.obj = obj; + evt.window = obj->window; + evt.document = obj->ctx->document; evt.eventdata = nullptr; evt.intval = 0; - cb(&evt, widget->event_data); + cbfunc(&evt, cbdata); }); } diff -r 0f2e69873875 -r 320d85f3cd14 ui/winui/list.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/list.cpp Mon Oct 02 19:31:06 2023 +0200 @@ -0,0 +1,165 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 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" +#include "util.h" + +#include "../common/context.h" +#include "../common/object.h" + +#include + +using namespace winrt; +using namespace Microsoft::UI::Xaml; +using namespace Microsoft::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Interop; +using namespace winrt::Windows::Foundation; +using namespace winrt::Microsoft::UI::Xaml::Controls::Primitives; + +UIWIDGET ui_listview_create(UiObject* obj, UiListArgs args) { + UiObject* current = uic_current_obj(obj); + + // create listview and toolkit wrapper + ListView listview = ListView(); + if (args.multiselection) { + listview.SelectionMode(ListViewSelectionMode::Extended); + } + + bool clickEnabled = listview.IsItemClickEnabled(); + listview.IsItemClickEnabled(true); + + + UIElement elm = listview; + UiWidget* widget = new UiWidget(elm); + widget->data1 = args.model; + widget->data2 = args.getvalue; + ui_context_add_widget_destructor(current->ctx, widget); + + // bind var + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST); + if (var) { + UiList* list = (UiList*)var->value; + list->update = ui_simple_list_update; + list->obj = widget; + ui_simple_list_update(list, 0); + } + + if (args.onselection) { + ui_callback onselection = args.onselection; + void* cbdata = args.onselectiondata; + listview.SelectionChanged([onselection, cbdata, obj](IInspectable const& sender, RoutedEventArgs evtargs) { + std::vector selectedRows = ui_create_listview_selection(sender.as()); + + UiListSelection selection; + selection.rows = selectedRows.data(); + selection.count = selectedRows.size(); + + UiEvent evt; + evt.obj = obj; + evt.window = obj->window; + evt.document = obj->ctx->document; + evt.eventdata = &selection; + evt.intval = 0; + onselection(&evt, cbdata); + }); + } + if (args.onactivate) { + ui_callback cb = args.onactivate; + void* cbdata = args.onactivatedata; + listview.ItemClick([cb, cbdata, obj](IInspectable const& sender, RoutedEventArgs evtargs) { + std::vector selectedRows = ui_create_listview_selection(sender.as()); + UiListSelection selection; + selection.rows = selectedRows.data(); + selection.count = selectedRows.size(); + + UiEvent evt; + evt.obj = obj; + evt.window = obj->window; + evt.document = obj->ctx->document; + evt.eventdata = &selection; + evt.intval = 0; + cb(&evt, cbdata); + }); + } + + // add listview to current container + UI_APPLY_LAYOUT1(current, args); + + current->container->Add(listview, false); + + return widget; +} + +static void* getstrvalue(void* elm, int ignore) { + return elm; +} + +void ui_simple_list_update(UiList* list, int i) { + UiWidget* widget = (UiWidget*)list->obj; + UiModel* model = (UiModel*)widget->data1; + ui_getvaluefunc getvalue = (ui_getvaluefunc)widget->data2; + ListView listview = widget->uielement.as(); + auto items = listview.Items(); + + // priority: getvalue, model.getvalue, getstrvalue (fallback) + if (getvalue == nullptr) { + if (model && model->getvalue) { + getvalue = model->getvalue; + } else { + getvalue = getstrvalue; + } + } + + // add list elements to listview.Items + items.Clear(); + void* elm = list->first(list); + while (elm) { + char* value = (char*)getvalue(elm, 0); + wchar_t* wstr = str2wstr(value, nullptr); + items.Append(box_value(wstr)); + free(wstr); + + elm = list->next(list); + } +} + +std::vector ui_create_listview_selection(ListView listview) { + std::vector selection; + int p = 0; + auto ranges = listview.SelectedRanges(); + for (auto range : ranges) { + int begin = range.FirstIndex(); + int end = range.LastIndex(); + for (int i = begin; i <= end; i++) { + selection.push_back(i); + } + } + return selection; +} + diff -r 0f2e69873875 -r 320d85f3cd14 ui/winui/list.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/list.h Mon Oct 02 19:31:06 2023 +0200 @@ -0,0 +1,39 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 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. + */ + +#pragma once + +#include "../ui/tree.h" +#include "toolkit.h" + +#include "../ui/container.h" + +extern "C" void ui_simple_list_update(UiList * list, int i); + +std::vector ui_create_listview_selection(winrt::Microsoft::UI::Xaml::Controls::ListView listview); + diff -r 0f2e69873875 -r 320d85f3cd14 ui/winui/winui.vcxproj --- a/ui/winui/winui.vcxproj Mon Oct 02 10:10:09 2023 +0200 +++ b/ui/winui/winui.vcxproj Mon Oct 02 19:31:06 2023 +0200 @@ -153,6 +153,7 @@ + @@ -189,6 +190,7 @@ + diff -r 0f2e69873875 -r 320d85f3cd14 ui/winui/winui.vcxproj.filters --- a/ui/winui/winui.vcxproj.filters Mon Oct 02 10:10:09 2023 +0200 +++ b/ui/winui/winui.vcxproj.filters Mon Oct 02 19:31:06 2023 +0200 @@ -66,6 +66,9 @@ Quelldateien + + Quelldateien + @@ -164,5 +167,11 @@ Headerdateien + + Headerdateien + + + + \ No newline at end of file