add semi-functional table (WinUI3) newapi

Thu, 05 Oct 2023 18:50:42 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Thu, 05 Oct 2023 18:50:42 +0200
branch
newapi
changeset 203
0e94be3d9722
parent 202
9f309d1914a2
child 204
4a258d47f964

add semi-functional table (WinUI3)

make/vs/testapp/main.c file | annotate | diff | comparison | revisions
ui/winui/table.cpp file | annotate | diff | comparison | revisions
ui/winui/table.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	Tue Oct 03 21:10:15 2023 +0200
+++ b/make/vs/testapp/main.c	Thu Oct 05 18:50:42 2023 +0200
@@ -41,6 +41,10 @@
     UiString* text;
     UiString* password;
     UiList* list;
+    UiString* t1;
+    UiString* t2;
+    UiString* t3;
+    UiList* list2;
 } WindowData;
 
 void action1(UiEvent* event, void* data) {
@@ -68,6 +72,8 @@
     printf("onchange: %d\n", event->intval);
 }
 
+
+
 void action_listselection_changed(UiEvent* event, void* data) {
     printf("selection changed\n");
     UiListSelection* sel = event->eventdata;
@@ -86,6 +92,37 @@
     }
 }
 
+typedef struct TableData {
+    char* col1;
+    char* col2;
+    char* col3;
+} TableData;
+
+void* table_getvalue(void* data, int i) {
+    TableData* t = data;
+    switch (i) {
+    case 0: return t->col1;
+    case 1: return t->col2;
+    case 2: return t->col3;
+    }
+    return NULL;
+}
+
+void action_add(UiEvent* event, void* data) {
+    WindowData* wdata = event->window;
+    char* t1 = wdata->t1->get(wdata->t1);
+    char* t2 = wdata->t2->get(wdata->t2);
+    char* t3 = wdata->t3->get(wdata->t3);
+
+    TableData* tdat = malloc(sizeof(TableData));
+    tdat->col1 = _strdup(t1);
+    tdat->col2 = _strdup(t2);
+    tdat->col3 = _strdup(t3);
+    ui_list_append(wdata->list2, tdat);
+    wdata->list2->update(wdata->list2, 0);
+
+}
+
 void application_startup(UiEvent* event, void* data) {
     UiObject* obj = ui_window("Test", NULL);
     WindowData* wdata = ui_malloc(obj->ctx, sizeof(WindowData));
@@ -96,6 +133,10 @@
     wdata->text = ui_string_new(obj->ctx, "text");
     wdata->password = ui_string_new(obj->ctx, "password");
     wdata->list = ui_list_new(obj->ctx, "list");
+    wdata->list2 = ui_list_new(obj->ctx, "list2");
+    wdata->t1 = ui_string_new(obj->ctx, "t1");
+    wdata->t2 = ui_string_new(obj->ctx, "t2");
+    wdata->t3 = ui_string_new(obj->ctx, "t3");
 
     ui_list_append(wdata->list, "Hello");
     ui_list_append(wdata->list, "World");
@@ -104,6 +145,23 @@
     ui_list_append(wdata->list, "Item5");
     ui_list_append(wdata->list, "Item6");
 
+    TableData* td1 = malloc(sizeof(TableData));
+    TableData* td2 = malloc(sizeof(TableData));
+    TableData* td3 = malloc(sizeof(TableData));
+    td1->col1 = "a1";
+    td1->col2 = "b1";
+    td1->col3 = "c1";
+    td2->col1 = "a2";
+    td2->col2 = "b2";
+    td2->col3 = "b3";
+    td3->col1 = "a3";
+    td3->col2 = "b3";
+    td3->col3 = "c3";
+
+    ui_list_append(wdata->list2, td1);
+    ui_list_append(wdata->list2, td2);
+    ui_list_append(wdata->list2, td3);
+
     ui_scrolledwindow0(obj) {
         ui_grid(obj, .margin = 10, .columnspacing = 5, .rowspacing = 20) {
             ui_button(obj, .label = "Button1", .onclick = action1, .onclickdata = "action1");
@@ -159,6 +217,22 @@
                     ui_button(obj, .label = "Tab 2 Button");
                 }
             }
+
+            ui_newline(obj);
+            ui_textfield(obj, .value = wdata->t1);
+            ui_textfield(obj, .value = wdata->t2);
+            ui_textfield(obj, .value = wdata->t3);
+            ui_newline(obj);
+            ui_button(obj, .label = "Add", .onclick = action_add);
+            ui_newline(obj);
+
+
+            ui_newline(obj);
+
+            UiModel* model = ui_model(obj->ctx, UI_STRING, "Col 1", UI_STRING, "Col 2", UI_STRING, "Col 3", -1);
+            model->getvalue = table_getvalue;
+            ui_table(obj, .colspan = 3, .model = model, .list = wdata->list2);
+            ui_model_free(obj->ctx, model);
         }
     }   
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/winui/table.cpp	Thu Oct 05 18:50:42 2023 +0200
@@ -0,0 +1,284 @@
+/*
+ * 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 "table.h"
+#include "container.h"
+#include "util.h"
+
+#include "../common/context.h"
+#include "../common/object.h"
+
+#include <winrt/Microsoft.UI.Xaml.Data.h>
+#include <winrt/Microsoft.UI.Xaml.Media.h>
+#include <winrt/Microsoft.UI.Xaml.Input.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;
+using namespace winrt::Microsoft::UI::Xaml::Media;
+using namespace winrt::Windows::UI::Xaml::Input;
+
+extern "C" void reg_table_destructor(UiContext * ctx, UiTable * table) {
+	// TODO:
+}
+
+UIEXPORT UIWIDGET ui_table_create(UiObject* obj, UiListArgs args) {
+	if (!args.model) {
+		return nullptr;
+	}
+
+	UiObject* current = uic_current_obj(obj);
+
+	// create widgets and wrapper obj
+	ScrollViewer scrollW = ScrollViewer();
+	Grid grid = Grid();
+	scrollW.Content(grid);
+	UiTable* uitable = new UiTable(scrollW, grid);
+	reg_table_destructor(current->ctx, uitable);
+
+	
+	uitable->getvalue = args.model->getvalue ? args.model->getvalue : args.getvalue;
+
+	// grid styling
+	winrt::Windows::UI::Color bg = { 233, 233, 255, 255 }; // test color
+	SolidColorBrush brush = SolidColorBrush(bg);
+	grid.Background(brush);
+
+	// add columns from args.model
+	uitable->add_header(args.model);
+	
+	// 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_table_update;
+		list->obj = uitable;
+		uitable->update(list, 0);
+	}
+
+	// create toolkit wrapper object and register destructor
+	UIElement elm = scrollW;
+	UiWidget* widget = new UiWidget(elm);
+	ui_context_add_widget_destructor(current->ctx, widget);
+
+	// add scrollW to current container
+	UI_APPLY_LAYOUT1(current, args);
+
+	current->container->Add(scrollW, false);
+
+	return widget;
+}
+
+extern "C" void ui_table_update(UiList * list, int i) {
+	UiTable* table = (UiTable*)list->obj;
+	table->clear();
+	table->update(list, i);
+}
+
+UiTable::UiTable(winrt::Microsoft::UI::Xaml::Controls::ScrollViewer scrollW, winrt::Microsoft::UI::Xaml::Controls::Grid grid) {
+	this->scrollw = scrollw;
+	this->grid = grid;
+
+	winrt::Windows::UI::Color highlightBg = { 120, 120, 255, 255 }; // test color
+	highlightBrush = SolidColorBrush(highlightBg);
+
+	winrt::Windows::UI::Color defaultBg = { 0, 0, 0, 0 }; // default
+	defaultBrush = SolidColorBrush(defaultBg);
+
+	winrt::Windows::UI::Color selectedBg = { 255, 120, 120, 255 }; // test color
+	selectedBrush = SolidColorBrush(selectedBg);
+
+	winrt::Windows::UI::Color selectedFg = { 255, 20, 20, 255 }; // test color
+	selectedBorderBrush = SolidColorBrush(selectedFg);
+}
+
+void UiTable::add_header(UiModel* model) {
+	GridLength gl;
+	gl.Value = 0;
+	gl.GridUnitType = GridUnitType::Auto;
+
+	// add header row definition
+	auto headerRowDef = RowDefinition();
+	headerRowDef.Height(gl);
+	grid.RowDefinitions().Append(headerRowDef);
+
+
+	for (int i = 0; i < model->columns;i++) {
+		char* title = model->titles[i];
+		UiModelType type = model->types[i];
+
+		// add grid column definition
+		auto colDef = ColumnDefinition();
+		colDef.Width(gl);
+		grid.ColumnDefinitions().Append(colDef);
+
+		// add button
+		auto button = Button();
+		wchar_t* wlabel = str2wstr(title, nullptr);
+		button.Content(box_value(wlabel));
+		free(wlabel);
+
+		// some styling for the button
+		Thickness border = { 0,0,1,0 };
+		CornerRadius corner = { 0,0,0,0 };
+		button.BorderThickness(border);
+		button.CornerRadius(corner);
+
+		grid.SetColumn(button, i);
+		grid.SetRow(button, 0);
+		grid.Children().Append(button);
+
+		UiTableColumn h;
+		h.header = button;
+		header.push_back(h);
+	}
+
+	maxrows = 1;
+}
+
+void UiTable::update(UiList* list,  int i) {
+	if (getvalue == nullptr) {
+		return;
+	}
+
+	Thickness b1 = { 1, 1, 0, 1 }; // first col
+	Thickness b2 = { 0, 1, 0, 1 }; // middle
+	Thickness b3 = { 0, 1, 1, 1 }; // last col
+
+	GridLength gl;
+	gl.Value = 0;
+	gl.GridUnitType = GridUnitType::Auto;
+
+	int row = 1;
+	void* elm = list->first(list);
+	while (elm) {
+		if (row >= maxrows) {
+			auto rowdef = RowDefinition();
+			rowdef.Height(gl);
+			grid.RowDefinitions().Append(rowdef);
+			maxrows = row;
+		}
+
+		for (int col = 0; col < header.size(); col++) {
+			Border cellBorder = Border();
+			cellBorder.Background(defaultBrush);
+			TextBlock cell = TextBlock();
+			cellBorder.Child(cell);
+			cellBorder.BorderBrush(defaultBrush);
+			if (col == 0) {
+				cellBorder.BorderThickness(b1);
+			}
+			else if (col + 1 == header.size()) {
+				cellBorder.BorderThickness(b3);
+			}
+			else {
+				cellBorder.BorderThickness(b2);
+			}
+
+			char* value = (char*)getvalue(elm, col);
+			if (value) {
+				wchar_t* wstr = str2wstr(value, nullptr);
+				cell.Text(winrt::hstring(wstr));
+				free(wstr);
+			}
+			Thickness padding = { 10,0,4,0 };
+			cell.Padding(padding);
+			cell.VerticalAlignment(VerticalAlignment::Stretch);
+
+			// event handler
+			cellBorder.PointerPressed(
+				winrt::Microsoft::UI::Xaml::Input::PointerEventHandler(
+					[=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) {
+						if (selection > 0) {
+							row_background(selection, defaultBrush, defaultBrush);
+						}
+						row_background(row, selectedBrush, selectedBorderBrush);
+						selection = row;
+					})
+			);
+			cellBorder.PointerReleased(
+				winrt::Microsoft::UI::Xaml::Input::PointerEventHandler(
+					[=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) {
+
+					})
+			);
+			cellBorder.PointerEntered(
+				winrt::Microsoft::UI::Xaml::Input::PointerEventHandler(
+					[=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) {
+						if (selection != row) {
+							row_background(row, highlightBrush, highlightBrush);
+						}
+					})
+			);
+			cellBorder.PointerExited(
+				winrt::Microsoft::UI::Xaml::Input::PointerEventHandler(
+					[=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) {
+						if (selection != row) {
+							row_background(row, defaultBrush, defaultBrush);
+						}
+					})
+			);
+
+			grid.SetColumn(cellBorder, col);
+			grid.SetRow(cellBorder, row);
+			grid.Children().Append(cellBorder);
+		}
+
+		row++;
+		elm = list->next(list);
+	}
+}
+
+void UiTable::clear() {
+	for (int i = grid.Children().Size()-1; i >= 0; i--) {
+		FrameworkElement elm = grid.Children().GetAt(i).as<FrameworkElement>();
+		int child_row = grid.GetRow(elm);
+		if (child_row > 0) {
+			grid.Children().RemoveAt(i);
+		}
+	}
+}
+
+void UiTable::row_background(int row, winrt::Microsoft::UI::Xaml::Media::Brush brush, winrt::Microsoft::UI::Xaml::Media::Brush borderBrush) {
+	Thickness b1 = { 1, 1, 0, 1 }; // first col
+	Thickness b2 = { 0, 1, 0, 1 }; // middle
+	Thickness b3 = { 0, 1, 1, 1 }; // last col
+	
+	for (auto child : grid.Children()) {
+		FrameworkElement elm = child.as<FrameworkElement>();
+		int child_row = grid.GetRow(elm);
+		if (child_row == row) {
+			Border b = elm.as<Border>();
+			b.Background(brush);
+			b.BorderBrush(borderBrush);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/winui/table.h	Thu Oct 05 18:50:42 2023 +0200
@@ -0,0 +1,65 @@
+/*
+ * 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"
+
+typedef struct UiTableColumn {
+	winrt::Microsoft::UI::Xaml::Controls::Button header;
+
+} UiTableColumn;
+
+typedef struct UiTable {
+	winrt::Microsoft::UI::Xaml::Controls::ScrollViewer scrollw;
+	winrt::Microsoft::UI::Xaml::Controls::Grid grid;
+	winrt::Microsoft::UI::Xaml::Media::SolidColorBrush defaultBrush;
+	winrt::Microsoft::UI::Xaml::Media::SolidColorBrush highlightBrush;
+	winrt::Microsoft::UI::Xaml::Media::SolidColorBrush selectedBrush;
+	winrt::Microsoft::UI::Xaml::Media::SolidColorBrush selectedBorderBrush;
+	std::vector<UiTableColumn> header;
+	ui_getvaluefunc getvalue;
+	int maxrows;
+	int selection = 0;
+
+	UiTable(winrt::Microsoft::UI::Xaml::Controls::ScrollViewer scrollW, winrt::Microsoft::UI::Xaml::Controls::Grid grid);
+	
+	void add_header(UiModel* model);
+
+	void update(UiList* list, int i);
+
+	void clear();
+
+	void row_background(int row, winrt::Microsoft::UI::Xaml::Media::Brush brush, winrt::Microsoft::UI::Xaml::Media::Brush borderBrush);
+} UiTable;
+
+extern "C" void ui_table_update(UiList * list, int i);
+
--- a/ui/winui/winui.vcxproj	Tue Oct 03 21:10:15 2023 +0200
+++ b/ui/winui/winui.vcxproj	Thu Oct 05 18:50:42 2023 +0200
@@ -154,6 +154,7 @@
     <ClCompile Include="button.cpp" />
     <ClCompile Include="container.cpp" />
     <ClCompile Include="list.cpp" />
+    <ClCompile Include="table.cpp" />
     <ClCompile Include="text.cpp" />
     <ClCompile Include="toolkit.cpp" />
     <ClCompile Include="util.cpp" />
@@ -191,6 +192,7 @@
     <ClInclude Include="button.h" />
     <ClInclude Include="container.h" />
     <ClInclude Include="list.h" />
+    <ClInclude Include="table.h" />
     <ClInclude Include="text.h" />
     <ClInclude Include="toolkit.h" />
     <ClInclude Include="util.h" />
--- a/ui/winui/winui.vcxproj.filters	Tue Oct 03 21:10:15 2023 +0200
+++ b/ui/winui/winui.vcxproj.filters	Thu Oct 05 18:50:42 2023 +0200
@@ -69,6 +69,9 @@
     <ClCompile Include="list.cpp">
       <Filter>Quelldateien</Filter>
     </ClCompile>
+    <ClCompile Include="table.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <None Include="packages.config" />
@@ -170,6 +173,9 @@
     <ClInclude Include="list.h">
       <Filter>Headerdateien</Filter>
     </ClInclude>
+    <ClInclude Include="table.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />

mercurial