ui/winui/table.cpp

Thu, 12 Oct 2023 13:52:18 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Thu, 12 Oct 2023 13:52:18 +0200
branch
newapi
changeset 211
5d71a36b833b
parent 210
83556205edad
child 214
279c0c81d3b1
permissions
-rw-r--r--

add custom titlebar (WinUI3)

/*
 * 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 "pch.h"

#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 = { 255, 255, 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 = { 255, 234, 234, 234 };
	highlightBrush = SolidColorBrush(highlightBg);

	winrt::Windows::UI::Color defaultBg = { 0, 0, 0, 0 }; // default
	defaultBrush = SolidColorBrush(defaultBg);

	winrt::Windows::UI::Color selectedBg = { 255, 204, 232, 255 }; // test color
	selectedBrush = SolidColorBrush(selectedBg);

	winrt::Windows::UI::Color selectedFg = { 255, 0, 90, 158 }; // 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);
		}
	}
}

mercurial