add listview (WinUI3) newapi

Mon, 02 Oct 2023 19:31:06 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Mon, 02 Oct 2023 19:31:06 +0200
branch
newapi
changeset 196
320d85f3cd14
parent 195
0f2e69873875
child 197
8a82ebe23822

add listview (WinUI3)

make/vs/testapp/main.c file | annotate | diff | comparison | revisions
ui/ui/toolkit.h file | annotate | diff | comparison | revisions
ui/ui/tree.h file | annotate | diff | comparison | revisions
ui/winui/button.cpp file | annotate | diff | comparison | revisions
ui/winui/list.cpp file | annotate | diff | comparison | revisions
ui/winui/list.h file | annotate | diff | comparison | revisions
ui/winui/winui.vcxproj file | annotate | diff | comparison | revisions
ui/winui/winui.vcxproj.filters file | annotate | diff | comparison | revisions
--- 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);
         }
     }   
 
--- 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);
 
 };
--- 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
 }
--- 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);
 			});
 	}
 
--- /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 <winrt/Microsoft.UI.Xaml.Data.h>
+
+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<int> selectedRows = ui_create_listview_selection(sender.as<ListView>());
+
+            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<int> selectedRows = ui_create_listview_selection(sender.as<ListView>());
+            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<ListView>();
+    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<int> ui_create_listview_selection(ListView listview) {
+    std::vector<int> 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;
+}
+
--- /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<int> ui_create_listview_selection(winrt::Microsoft::UI::Xaml::Controls::ListView listview);
+
--- 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 @@
     <ClCompile Include="appmenu.cpp" />
     <ClCompile Include="button.cpp" />
     <ClCompile Include="container.cpp" />
+    <ClCompile Include="list.cpp" />
     <ClCompile Include="text.cpp" />
     <ClCompile Include="toolkit.cpp" />
     <ClCompile Include="util.cpp" />
@@ -189,6 +190,7 @@
     <ClInclude Include="appmenu.h" />
     <ClInclude Include="button.h" />
     <ClInclude Include="container.h" />
+    <ClInclude Include="list.h" />
     <ClInclude Include="text.h" />
     <ClInclude Include="toolkit.h" />
     <ClInclude Include="util.h" />
--- 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 @@
     <ClCompile Include="text.cpp">
       <Filter>Quelldateien</Filter>
     </ClCompile>
+    <ClCompile Include="list.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <None Include="packages.config" />
@@ -164,5 +167,11 @@
     <ClInclude Include="text.h">
       <Filter>Headerdateien</Filter>
     </ClInclude>
+    <ClInclude Include="list.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
   </ItemGroup>
 </Project>
\ No newline at end of file

mercurial