improve table row selection, add support for multiple selections (WinUI3) newapi

Fri, 13 Oct 2023 11:26:47 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Fri, 13 Oct 2023 11:26:47 +0200
branch
newapi
changeset 214
279c0c81d3b1
parent 213
7e39db525fd9
child 215
1bd5534c395d

improve table row selection, add support for multiple selections (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
--- a/make/vs/testapp/main.c	Thu Oct 12 16:03:35 2023 +0200
+++ b/make/vs/testapp/main.c	Fri Oct 13 11:26:47 2023 +0200
@@ -164,6 +164,9 @@
     TableData* td1 = malloc(sizeof(TableData));
     TableData* td2 = malloc(sizeof(TableData));
     TableData* td3 = malloc(sizeof(TableData));
+    TableData* td4 = malloc(sizeof(TableData));
+    TableData* td5 = malloc(sizeof(TableData));
+    TableData* td6 = malloc(sizeof(TableData));
     td1->col1 = "a1";
     td1->col2 = "b1";
     td1->col3 = "c1";
@@ -173,10 +176,22 @@
     td3->col1 = "a3";
     td3->col2 = "b3";
     td3->col3 = "c3";
+    td4->col1 = "a3";
+    td4->col2 = "b3";
+    td4->col3 = "c3";
+    td5->col1 = "a3";
+    td5->col2 = "b3";
+    td5->col3 = "c3";
+    td6->col1 = "a3";
+    td6->col2 = "b3";
+    td6->col3 = "c3";
 
     ui_list_append(wdata->list2, td1);
     ui_list_append(wdata->list2, td2);
     ui_list_append(wdata->list2, td3);
+    ui_list_append(wdata->list2, td4);
+    ui_list_append(wdata->list2, td5);
+    ui_list_append(wdata->list2, td6);
 
     ui_scrolledwindow0(obj) {
         ui_grid(obj, .margin = 10, .columnspacing = 5, .rowspacing = 20) {
@@ -263,7 +278,7 @@
 { 
     ui_init("app1", 0, NULL);
     ui_onstartup(application_startup, NULL);
-
+    
     ui_menu("File") {
         ui_menuitem(.label = "Item 1");
         ui_menuitem(.label = "Item 2");
--- a/ui/winui/table.cpp	Thu Oct 12 16:03:35 2023 +0200
+++ b/ui/winui/table.cpp	Fri Oct 13 11:26:47 2023 +0200
@@ -38,6 +38,7 @@
 #include <winrt/Microsoft.UI.Xaml.Data.h>
 #include <winrt/Microsoft.UI.Xaml.Media.h>
 #include <winrt/Microsoft.UI.Xaml.Input.h>
+#include <winrt/Windows.UI.Core.h>
 
 using namespace winrt;
 using namespace Microsoft::UI::Xaml;
@@ -120,6 +121,13 @@
 
 	winrt::Windows::UI::Color selectedFg = { 255, 0, 90, 158 }; // test color
 	selectedBorderBrush = SolidColorBrush(selectedFg);
+
+	grid.KeyDown(
+		winrt::Microsoft::UI::Xaml::Input::KeyEventHandler(
+			[=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const& args) {
+				// key event for hanling the table cursor or enter
+			})
+	);
 }
 
 void UiTable::add_header(UiModel* model) {
@@ -190,6 +198,8 @@
 		}
 
 		for (int col = 0; col < header.size(); col++) {
+			// create ui elements with the correct cell border
+			// dependeing on the column
 			Border cellBorder = Border();
 			cellBorder.Background(defaultBrush);
 			TextBlock cell = TextBlock();
@@ -204,26 +214,60 @@
 			else {
 				cellBorder.BorderThickness(b2);
 			}
+			Thickness padding = { 10,0,4,0 };
+			cell.Padding(padding);
+			cell.VerticalAlignment(VerticalAlignment::Stretch);
 
+			// set cell value
 			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);
+						winrt::Windows::System::VirtualKeyModifiers modifiers = args.KeyModifiers();
+
+						if (modifiers == winrt::Windows::System::VirtualKeyModifiers::Control) {
+							// add/remove current row
+							if (!is_row_selected(row)) {
+								row_background(row, selectedBrush, selectedBorderBrush);
+								selection.push_back(row);
+							}
+							else {
+								row_background(row, highlightBrush, highlightBrush);
+								remove_from_selection(row);
+							}
 						}
-						row_background(row, selectedBrush, selectedBorderBrush);
-						selection = row;
+						else if (modifiers == winrt::Windows::System::VirtualKeyModifiers::None || selection.size() == 0) {
+							// no modifier or shift is pressed but there is no selection
+							if (selection.size() > 0) {
+								change_rows_bg(selection, defaultBrush, defaultBrush);
+							}
+							
+							row_background(row, selectedBrush, selectedBorderBrush);
+							selection = { row };
+						}
+						else if (modifiers == winrt::Windows::System::VirtualKeyModifiers::Shift) {
+							// select everything between the first selection and the current row
+							std::sort(selection.begin(), selection.end());
+							int first = selection.front();
+
+							// clear previous selection
+							change_rows_bg(selection, defaultBrush, defaultBrush);
+
+							// create new selection
+							std::vector<int> newselection;
+							for (int s = first; s <= row; s++) {
+								newselection.push_back(s);
+							}
+							selection = newselection;
+							change_rows_bg(selection, selectedBrush, selectedBorderBrush);
+						}
 					})
 			);
 			cellBorder.PointerReleased(
@@ -235,7 +279,7 @@
 			cellBorder.PointerEntered(
 				winrt::Microsoft::UI::Xaml::Input::PointerEventHandler(
 					[=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) {
-						if (selection != row) {
+						if (!is_row_selected(row)) {
 							row_background(row, highlightBrush, highlightBrush);
 						}
 					})
@@ -243,7 +287,7 @@
 			cellBorder.PointerExited(
 				winrt::Microsoft::UI::Xaml::Input::PointerEventHandler(
 					[=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) {
-						if (selection != row) {
+						if (!is_row_selected(row)) {
 							row_background(row, defaultBrush, defaultBrush);
 						}
 					})
@@ -284,3 +328,16 @@
 		}
 	}
 }
+
+void UiTable::change_rows_bg(std::vector<int> rows, winrt::Microsoft::UI::Xaml::Media::Brush brush, winrt::Microsoft::UI::Xaml::Media::Brush borderBrush) {
+	std::for_each(rows.cbegin(), rows.cend(), [&](const int& row) {row_background(row, brush, borderBrush); });
+}
+
+bool UiTable::is_row_selected(int row) {
+	return std::find(selection.begin(), selection.end(), row) != selection.end() ? true : false;
+}
+
+void UiTable::remove_from_selection(int row) {
+	selection.erase(std::remove(selection.begin(), selection.end(), row), selection.end());
+	selection.shrink_to_fit();
+}
--- a/ui/winui/table.h	Thu Oct 12 16:03:35 2023 +0200
+++ b/ui/winui/table.h	Fri Oct 13 11:26:47 2023 +0200
@@ -48,7 +48,8 @@
 	std::vector<UiTableColumn> header;
 	ui_getvaluefunc getvalue;
 	int maxrows;
-	int selection = 0;
+	int lastSelection;
+	std::vector<int> selection;
 
 	UiTable(winrt::Microsoft::UI::Xaml::Controls::ScrollViewer scrollW, winrt::Microsoft::UI::Xaml::Controls::Grid grid);
 	
@@ -59,6 +60,12 @@
 	void clear();
 
 	void row_background(int row, winrt::Microsoft::UI::Xaml::Media::Brush brush, winrt::Microsoft::UI::Xaml::Media::Brush borderBrush);
+
+	void change_rows_bg(std::vector<int> rows, winrt::Microsoft::UI::Xaml::Media::Brush brush, winrt::Microsoft::UI::Xaml::Media::Brush borderBrush);
+
+	bool is_row_selected(int row);
+
+	void remove_from_selection(int row);
 } UiTable;
 
 extern "C" void ui_table_update(UiList * list, int i);

mercurial