ui/winui/container.cpp

changeset 431
bb7da585debc
parent 378
d41b1ffc5f77
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/winui/container.cpp	Sat Jan 04 16:38:48 2025 +0100
@@ -0,0 +1,932 @@
+/*
+* 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 "container.h"
+
+#include "../common/context.h"
+#include "../common/object.h"
+
+#include "util.h"
+
+
+void ui_container_begin_close(UiObject* obj) {
+	UiContainer* ct = uic_get_current_container(obj);
+	ct->close = 1;
+}
+
+int ui_container_finish(UiObject* obj) {
+	UiContainer* ct = uic_get_current_container(obj);
+	if (ct->close) {
+		ui_end(obj);
+		return 0;
+	}
+	return 1;
+}
+
+
+// --------------------- UiBoxContainer ---------------------
+
+static UIWIDGET ui_box(UiObject* obj, UiContainerArgs args, UiBoxContainerType type) {
+	UiObject* current = uic_current_obj(obj);
+	UI_APPLY_LAYOUT1(current, args);
+
+	Grid grid = Grid();
+	current->container->Add(grid, true);
+
+	UIElement elm = grid;
+	UiWidget* widget = new UiWidget(elm);
+	ui_context_add_widget_destructor(current->ctx, widget);
+
+	UiObject* newobj = uic_object_new(obj, widget);
+	newobj->container = new UiBoxContainer(grid, type, args.margin, args.spacing);
+	ui_context_add_container_destructor(current->ctx, newobj->container);
+	uic_obj_add(obj, newobj);
+
+	return widget;
+}
+
+UIWIDGET ui_vbox_create(UiObject* obj, UiContainerArgs args) {
+	return ui_box(obj, args, UI_BOX_CONTAINER_VBOX);
+}
+
+UIWIDGET ui_hbox_create(UiObject* obj, UiContainerArgs args) {
+	return ui_box(obj, args, UI_BOX_CONTAINER_HBOX);
+}
+
+UiBoxContainer::UiBoxContainer(Grid grid, enum UiBoxContainerType type, int margin, int spacing) {
+	this->grid = grid;
+	this->type = type;
+
+	Thickness t = { (double)margin, (double)margin, (double)margin, (double)margin };
+	grid.Margin(t);
+	grid.ColumnSpacing((double)spacing);
+	grid.RowSpacing((double)spacing);
+
+	GridLength gl;
+	gl.Value = 1;
+	gl.GridUnitType = GridUnitType::Star;
+
+	// hbox needs one row def, vbox needs one col def
+	// all other col/row defs are created when elements are added
+	if (type == UI_BOX_CONTAINER_HBOX) {
+		boxRowDef = RowDefinition();
+		boxRowDef.Height(gl);
+		grid.RowDefinitions().Append(boxRowDef);
+	} else {
+		boxColDef = ColumnDefinition();
+		boxColDef.Width(gl);
+		grid.ColumnDefinitions().Append(boxColDef);
+	}
+
+	ui_reset_layout(layout);
+}
+
+void UiBoxContainer::Add(FrameworkElement control, UiBool fill) {
+	if (this->layout.fill != UI_LAYOUT_UNDEFINED) {
+		fill = ui_lb2bool(this->layout.fill);
+	}
+
+	GridLength gl;
+	if (fill) {
+		gl.Value = 1;
+		gl.GridUnitType = GridUnitType::Star;
+	}
+	else {
+		gl.Value = 0;
+		gl.GridUnitType = GridUnitType::Auto;
+	}
+
+	control.HorizontalAlignment(HorizontalAlignment::Stretch);
+	control.VerticalAlignment(VerticalAlignment::Stretch);
+
+	if (type == UI_CONTAINER_HBOX) {
+		ColumnDefinition coldef = ColumnDefinition();
+		coldef.Width(gl);
+		grid.ColumnDefinitions().Append(coldef);
+		grid.SetColumn(control, grid.Children().Size());
+		grid.SetRow(control, 0);
+	} else {
+		RowDefinition rowdef = RowDefinition();
+		rowdef.Height(gl);
+		grid.RowDefinitions().Append(rowdef);
+		grid.SetRow(control, grid.Children().Size());
+		grid.SetColumn(control, 0);
+	}
+
+	grid.Children().Append(control);
+
+	ui_reset_layout(layout);
+}
+
+
+// --------------------- UiGridContainer ---------------------
+
+UIWIDGET ui_grid_create(UiObject* obj, UiContainerArgs args) {
+	UiObject* current = uic_current_obj(obj);
+	UI_APPLY_LAYOUT1(current, args);
+
+	Grid grid = Grid();
+	current->container->Add(grid, true);
+
+	UIElement elm = grid;
+	UiWidget* widget = new UiWidget(elm);
+	ui_context_add_widget_destructor(current->ctx, widget);
+
+	UiObject* newobj = uic_object_new(obj, widget);
+	newobj->container = new UiGridContainer(grid, args.margin, args.columnspacing, args.rowspacing);
+	ui_context_add_container_destructor(current->ctx, newobj->container);
+	uic_obj_add(obj, newobj);
+
+	return widget;
+}
+
+UiGridContainer::UiGridContainer(Grid grid, int margin, int columnspacing, int rowspacing) {
+	this->grid = grid;
+	Thickness t = { (double)margin, (double)margin, (double)margin, (double)margin };
+	grid.Margin(t);
+	grid.ColumnSpacing((double)columnspacing);
+	grid.RowSpacing((double)rowspacing);
+	ui_reset_layout(layout);
+}
+
+void UiGridContainer::Add(FrameworkElement control, UiBool fill) {
+	GridLength gl;
+
+	bool hexpand = false;
+	bool vexpand = false;
+	bool hfill = false;
+	bool vfill = false;
+	if(layout.fill != UI_LAYOUT_UNDEFINED) {
+		fill = ui_lb2bool(layout.fill);
+	}
+	if (layout.hexpand != UI_LAYOUT_UNDEFINED) {
+		hexpand = layout.hexpand;
+		hfill = true;
+	}
+	if (layout.vexpand != UI_LAYOUT_UNDEFINED) {
+		vexpand = layout.vexpand;
+		vfill = true;
+	}
+	if (fill) {
+		hfill = true;
+		vfill = true;
+	}
+
+	// create new RowDefinition for the new line
+	if (layout.newline || y == -1) {
+		x = 0;
+		y++;
+		RowDefinition rowdef = RowDefinition();
+		if (vexpand) {
+			gl.GridUnitType = GridUnitType::Star;
+			gl.Value = 1;
+		}
+		else {
+			gl.GridUnitType = GridUnitType::Auto;
+			gl.Value = 0;
+		}
+		rowdef.Height(gl);
+		grid.RowDefinitions().Append(rowdef);
+	} else if (vexpand) {
+		// adjust row
+		gl.GridUnitType = GridUnitType::Star;
+		gl.Value = 1;
+		grid.RowDefinitions().GetAt(y).Height(gl);
+	}
+
+	// create new columndefinition, if a new column is added
+	if (x == cols) {
+		if (hexpand) {
+			gl.GridUnitType = GridUnitType::Star;
+			gl.Value = 1;
+		}
+		else {
+			gl.GridUnitType = GridUnitType::Auto;
+			gl.Value = 0;
+		}
+		ColumnDefinition coldef = ColumnDefinition();
+		coldef.Width(gl);
+		grid.ColumnDefinitions().Append(coldef);
+		cols++;
+	} else if(hexpand) {
+		// adjust column
+		if (layout.colspan == 0) {
+			gl.GridUnitType = GridUnitType::Star;
+			gl.Value = 1;
+			grid.ColumnDefinitions().GetAt(x).Width(gl);
+		} else {
+			int adjust_col = x;
+			bool adjust = true;
+			for (int i = 0; i < layout.colspan; i++) {
+				if (grid.ColumnDefinitions().Size() == x + i) {
+					break;
+				}
+				adjust_col = x + i;
+				GridLength w = grid.ColumnDefinitions().GetAt(adjust_col).Width();
+				if (w.GridUnitType == GridUnitType::Star) {
+					adjust = false;
+					break;
+				}
+			}
+
+			if (adjust) {
+				gl.GridUnitType = GridUnitType::Star;
+				gl.Value = 1;
+				grid.ColumnDefinitions().GetAt(adjust_col).Width(gl);
+			}
+		}
+	}
+
+	// add control
+	if (hfill) {
+		control.HorizontalAlignment(HorizontalAlignment::Stretch);
+	}
+	if (vfill) {
+		control.VerticalAlignment(VerticalAlignment::Stretch);
+	}
+
+	if (layout.colspan > 0) {
+		grid.SetColumnSpan(control, layout.colspan);
+	}
+	if (layout.rowspan > 0) {
+		grid.SetRowSpan(control, layout.rowspan);
+	}
+
+	grid.SetRow(control, y);
+	grid.SetColumn(control, x);
+	grid.Children().Append(control);
+
+	x++;
+
+	ui_reset_layout(layout);
+}
+
+// --------------------- UI Frame ---------------------
+
+UIWIDGET ui_frame_create(UiObject* obj, UiFrameArgs args) {
+	// create a grid for the frame, that contains the label and a sub-frame
+	Grid frame = Grid();
+
+	GridLength gl;
+	gl.GridUnitType = GridUnitType::Star;
+	gl.Value = 1;
+
+	ColumnDefinition coldef = ColumnDefinition();
+	coldef.Width(gl);
+	frame.ColumnDefinitions().Append(coldef);
+
+	RowDefinition rowdefFrame = RowDefinition();
+	rowdefFrame.Height(gl);
+
+	// label
+	int row = 0;
+	if (args.label) {
+		RowDefinition rowdefLabel = RowDefinition();
+		gl.GridUnitType = GridUnitType::Auto;
+		gl.Value = 0;
+		rowdefLabel.Height(gl);
+		frame.RowDefinitions().Append(rowdefLabel);
+
+		TextBlock label = TextBlock();
+		wchar_t* wlabel = str2wstr(args.label, nullptr);
+		winrt::hstring hstr(wlabel);
+		label.Text(hstr);
+		free(wlabel);
+
+		frame.SetRow(label, row++);
+		frame.SetColumn(label, 0);
+		frame.Children().Append(label);
+	}
+
+	// workarea frame
+	frame.RowDefinitions().Append(rowdefFrame);
+
+	Grid workarea = Grid();
+	frame.SetRow(workarea, row);
+	frame.SetColumn(workarea, 0);
+	frame.Children().Append(workarea);
+
+	// some styling for the workarea
+	winrt::Microsoft::UI::Xaml::Media::SolidColorBrush brush{ winrt::Microsoft::UI::ColorHelper::FromArgb(150, 150, 150, 150) };
+	workarea.BorderBrush(brush);
+	CornerRadius radius{ 8, 8, 8, 8 };
+	Thickness t = { 1, 1, 1, 1 };
+	workarea.CornerRadius(radius);
+	workarea.BorderThickness(t);
+
+	Thickness padding = { 10, 10, 10, 10 };
+	workarea.Padding(padding);
+
+	// add frame to the parent container
+	UiObject* current = uic_current_obj(obj);
+	UI_APPLY_LAYOUT1(current, args);
+	current->container->Add(frame, true);
+
+	UIElement elm = frame;
+	UiWidget* widget = new UiWidget(elm);
+	ui_context_add_widget_destructor(current->ctx, widget);
+
+	// sub container
+	UiContainer* ctn = nullptr;
+	switch (args.subcontainer) {
+		default:
+		case UI_CONTAINER_VBOX: {
+			ctn = new UiBoxContainer(workarea, UI_BOX_CONTAINER_VBOX, args.margin, args.spacing);
+			break;
+		}
+		case UI_CONTAINER_HBOX: {
+			ctn = new UiBoxContainer(workarea, UI_BOX_CONTAINER_HBOX, args.margin, args.spacing);
+			break;
+		}
+		case UI_CONTAINER_GRID: {
+			ctn = new UiGridContainer(workarea, args.margin, args.columnspacing, args.rowspacing);
+			break;
+		}
+	}
+	ui_context_add_container_destructor(current->ctx, ctn);
+
+	UiObject* newobj = uic_object_new(obj, widget);
+	newobj->container = ctn;
+	uic_obj_add(obj, newobj);
+
+	return widget;
+}
+
+// --------------------- UI Expander ---------------------
+
+UIWIDGET ui_expander_create(UiObject* obj, UiFrameArgs args) {
+	Expander expander = Expander();
+	if (args.label) {
+		wchar_t* wlabel = str2wstr(args.label, nullptr);
+		expander.Header(box_value(wlabel));
+		free(wlabel);
+	}
+	expander.IsExpanded(args.isexpanded);
+
+	// add frame to the parent container
+	UiObject* current = uic_current_obj(obj);
+	UI_APPLY_LAYOUT1(current, args);
+	current->container->Add(expander, true);
+
+	UIElement elm = expander;
+	UiWidget* widget = new UiWidget(elm);
+	ui_context_add_widget_destructor(current->ctx, widget);
+
+	Grid content = Grid();
+	expander.Content(content);
+
+	UiContainer* ctn = nullptr;
+	switch (args.subcontainer) {
+		default: 
+		case UI_CONTAINER_VBOX: {
+			ctn = new UiBoxContainer(content, UI_BOX_CONTAINER_VBOX, args.margin, args.spacing);
+			break;
+		}
+		case UI_CONTAINER_HBOX: {
+			ctn = new UiBoxContainer(content, UI_BOX_CONTAINER_HBOX, args.margin, args.spacing);
+			break;
+		}
+		case UI_CONTAINER_GRID: {
+			ctn = new UiGridContainer(content, args.margin, args.columnspacing, args.rowspacing);
+			break;
+		}
+	}
+	ui_context_add_container_destructor(current->ctx, ctn);
+
+	UiObject* newobj = uic_object_new(obj, widget);
+	newobj->container = ctn;
+	uic_obj_add(obj, newobj);
+
+	return widget;
+}
+
+// --------------------- UI ScrolledWindow ---------------------
+
+UIWIDGET ui_scrolledwindow_create(UiObject* obj, UiFrameArgs args) {
+	ScrollViewer scrollW = ScrollViewer();
+
+	// add frame to the parent container
+	UiObject* current = uic_current_obj(obj);
+	UI_APPLY_LAYOUT1(current, args);
+	current->container->Add(scrollW, true);
+
+	UIElement elm = scrollW;
+	UiWidget* widget = new UiWidget(elm);
+	ui_context_add_widget_destructor(current->ctx, widget);
+
+	// create child container
+	Grid content = Grid();
+	scrollW.Content(content);
+
+	UiContainer* ctn = nullptr;
+	switch (args.subcontainer) {
+		default:
+		case UI_CONTAINER_VBOX: {
+			ctn = new UiBoxContainer(content, UI_BOX_CONTAINER_VBOX, args.margin, args.spacing);
+			break;
+		}
+		case UI_CONTAINER_HBOX: {
+			ctn = new UiBoxContainer(content, UI_BOX_CONTAINER_HBOX, args.margin, args.spacing);
+			break;
+		}
+		case UI_CONTAINER_GRID: {
+			ctn = new UiGridContainer(content, args.margin, args.columnspacing, args.rowspacing);
+			break;
+		}
+	}
+	ui_context_add_container_destructor(current->ctx, ctn);
+
+	UiObject* newobj = uic_object_new(obj, widget);
+	newobj->container = ctn;
+	uic_obj_add(obj, newobj);
+
+	return widget;
+}
+
+// --------------------- UI TabView ---------------------
+
+UiTabViewContainer::UiTabViewContainer(UiTabView* tabview) {
+	this->tabview = tabview;
+}
+
+void UiTabViewContainer::Add(FrameworkElement control, UiBool fill) {
+	// noop
+}
+
+static UiObject* create_subcontainer_obj(UiObject* current, Grid subcontainer, UiSubContainerType type, int margin, int spacing, int columnspacing, int rowspacing) {
+	UiContainer* ctn = nullptr;
+	switch (type) {
+		default:
+		case UI_CONTAINER_VBOX: {
+			ctn = new UiBoxContainer(subcontainer, UI_BOX_CONTAINER_VBOX, margin, spacing);
+			break;
+		}
+		case UI_CONTAINER_HBOX: {
+			ctn = new UiBoxContainer(subcontainer, UI_BOX_CONTAINER_HBOX, margin, spacing);
+			break;
+		}
+		case UI_CONTAINER_GRID: {
+			ctn = new UiGridContainer(subcontainer, margin, columnspacing, rowspacing);
+			break;
+		}
+	}
+	ui_context_add_container_destructor(current->ctx, ctn);
+
+	UIElement elm = subcontainer;
+	UiWidget* widget = new UiWidget(elm);
+	ui_context_add_widget_destructor(current->ctx, widget);
+	UiObject* newobj = uic_object_new(current, widget);
+	newobj->container = ctn;
+	return newobj;
+}
+
+static UiTabView* tabview_pivot_create(UiObject* obj, UiTabViewArgs args) {
+	Pivot pivot = Pivot();
+	UiPivotTabView* tabview = new UiPivotTabView(obj, pivot, args);
+
+	return tabview;
+}
+
+UiPivotTabView::UiPivotTabView(UiObject* obj, Pivot pivot, UiTabViewArgs args) {
+	this->current = obj;
+	this->pivot = pivot;
+	this->subcontainer = args.subcontainer;
+	this->margin = args.margin;
+	this->spacing = args.spacing;
+	this->columnspacing = args.columnspacing;
+	this->rowspacing = args.rowspacing;
+}
+
+UiObject* UiPivotTabView::AddTab(const char* label, int index) {
+	TextBlock text = TextBlock();
+	wchar_t* wlabel = str2wstr(label, nullptr);
+	winrt::hstring hstr(wlabel);
+	text.Text(hstr);
+	free(wlabel);
+
+	PivotItem item = PivotItem();
+	item.Header(text);
+
+	// sub container
+	Grid subcontainer = Grid();
+	item.Content(subcontainer);
+	pivot.Items().Append(item);
+
+	return create_subcontainer_obj(current, subcontainer, this->subcontainer, margin, spacing, columnspacing, rowspacing);
+}
+
+void UiPivotTabView::Remove(int index) {
+	pivot.Items().RemoveAt(index);
+}
+
+void UiPivotTabView::Select(int index) {
+	
+}
+
+FrameworkElement UiPivotTabView::GetFrameworkElement() {
+	return pivot;
+}
+
+
+static UiTabView* tabview_invisible_create(UiObject *obj, UiTabViewArgs args) {
+	Grid container = Grid();
+	container.HorizontalAlignment(HorizontalAlignment::Stretch);
+	container.VerticalAlignment(VerticalAlignment::Stretch);
+	UiInvisibleTabView *tabview = new UiInvisibleTabView(obj, container, args);
+	return tabview;
+}
+
+UiInvisibleTabView::UiInvisibleTabView(UiObject* obj, Grid container, UiTabViewArgs args) {
+	this->current = obj;
+	this->container = container;
+	this->subcontainer = args.subcontainer;
+	this->margin = args.margin;
+	this->spacing = args.spacing;
+	this->columnspacing = args.columnspacing;
+	this->rowspacing = args.rowspacing;
+	this->currentIndex = -1;
+
+	GridLength gl;
+	gl.GridUnitType = GridUnitType::Star;
+	gl.Value = 1;
+
+	ColumnDefinition coldef = ColumnDefinition();
+	coldef.Width(gl);
+	container.ColumnDefinitions().Append(coldef);
+
+	RowDefinition rowdef = RowDefinition();
+	rowdef.Height(gl);
+	container.RowDefinitions().Append(rowdef);
+}
+
+UiObject* UiInvisibleTabView::AddTab(const char* label, int index) {
+	Grid subcontainer = Grid();
+	subcontainer.HorizontalAlignment(HorizontalAlignment::Stretch);
+	subcontainer.VerticalAlignment(VerticalAlignment::Stretch);
+	
+	if (pages.size() == 0) {
+		container.Children().Append(subcontainer);
+		currentIndex = 0;
+	}
+
+	if (index < 0) {
+		pages.push_back(subcontainer);
+	} else {
+		pages.insert(pages.begin() + index, subcontainer);
+	}
+
+	// sub container
+	return create_subcontainer_obj(current, subcontainer, this->subcontainer, margin, spacing, columnspacing, rowspacing);
+}
+
+void UiInvisibleTabView::Remove(int index) {
+	
+}
+
+void UiInvisibleTabView::Select(int index) {
+	if (index >= 0 && index < pages.size()) {
+		if (currentIndex != -1) {
+			container.Children().RemoveAt(0);
+		}
+		
+		container.Children().Append(pages.at(index));
+	}
+}
+
+FrameworkElement UiInvisibleTabView::GetFrameworkElement() {
+	return container;
+}
+
+
+static UiTabView* tabview_main_create(UiObject* obj, UiTabViewArgs args) {
+	TabView tabview = TabView();
+	tabview.IsAddTabButtonVisible(false);
+	//tabview.CanDragTabs(false);
+	//tabview.CanReorderTabs(false);
+	UiMainTabView* uitabview = new UiMainTabView(obj, tabview, args);
+
+	return uitabview;
+}
+
+UiMainTabView::UiMainTabView(UiObject* obj, TabView tabview, UiTabViewArgs args) {
+	this->current = obj;
+	this->tabview = tabview;
+	this->subcontainer = args.subcontainer;
+	this->margin = args.margin;
+	this->spacing = args.spacing;
+	this->columnspacing = args.columnspacing;
+	this->rowspacing = args.rowspacing;
+}
+
+UiObject* UiMainTabView::AddTab(const char* label, int index) {
+	TextBlock text = TextBlock();
+	wchar_t* wlabel = str2wstr(label, nullptr);
+	winrt::hstring hstr(wlabel);
+	text.Text(hstr);
+	free(wlabel);
+
+	TabViewItem item = TabViewItem();
+	item.Header(text);
+	item.CanDrag(false);
+	item.IsClosable(false);
+
+	// sub container
+	Grid subcontainer = Grid();
+	item.Content(subcontainer);
+	tabview.TabItems().Append(item);
+
+	return create_subcontainer_obj(current, subcontainer, this->subcontainer, margin, spacing, columnspacing, rowspacing);
+}
+
+void UiMainTabView::Remove(int index) {
+	this->tabview.TabItems().RemoveAt(index);
+}
+
+void UiMainTabView::Select(int index) {
+
+}
+
+FrameworkElement UiMainTabView::GetFrameworkElement() {
+	return tabview;
+}
+
+
+static UiTabView* tabview_navigationview_create(UiObject* obj, UiTabViewArgs args, UiTabViewType type) {
+	NavigationView navigationview = NavigationView();
+	UiNavigationTabView* tabview = new UiNavigationTabView(obj, navigationview, args, type);
+	navigationview.IsBackButtonVisible(NavigationViewBackButtonVisible::Collapsed);
+	navigationview.IsSettingsVisible(false);
+
+	return tabview;
+}
+
+UiNavigationTabView::UiNavigationTabView(UiObject* obj, NavigationView navigationview, UiTabViewArgs args, UiTabViewType type) {
+	this->current = obj;
+	this->navigationview = navigationview;
+	this->type = type;
+	this->margin = args.margin;
+	this->spacing = args.spacing;
+	this->columnspacing = args.columnspacing;
+	this->rowspacing = args.rowspacing;
+
+	if (type == UI_TABVIEW_NAVIGATION_TOP) {
+		navigationview.PaneDisplayMode(NavigationViewPaneDisplayMode::Top);
+	}
+
+	navigationview.SelectionChanged({ this, &UiNavigationTabView::SelectionChanged });
+}
+
+UiObject* UiNavigationTabView::AddTab(const char* label, int index1) {
+	TextBlock text = TextBlock();
+	wchar_t* wlabel = str2wstr(label, nullptr);
+	winrt::hstring hstr(wlabel);
+	text.Text(hstr);
+	free(wlabel);
+
+	NavigationViewItem item = NavigationViewItem();
+	item.Content(text);
+
+	// sub container
+	Grid subcontainer = Grid();
+	if (pages.size() == 0) {
+		navigationview.Content(subcontainer);
+		navigationview.SelectedItem(item);
+	}
+
+	navigationview.MenuItems().Append(item);
+	auto page = std::tuple<NavigationViewItem, FrameworkElement>{ item, subcontainer };
+	pages.push_back(page);
+
+	return create_subcontainer_obj(current, subcontainer, this->subcontainer, margin, spacing, columnspacing, rowspacing);
+}
+
+void UiNavigationTabView::Remove(int index) {
+	navigationview.MenuItems().RemoveAt(index);
+	pages.erase(pages.begin() + index);
+}
+
+void UiNavigationTabView::Select(int index) {
+
+}
+
+FrameworkElement UiNavigationTabView::GetFrameworkElement() {
+	return navigationview;
+}
+
+void UiNavigationTabView::SelectionChanged(NavigationView const& sender, NavigationViewSelectionChangedEventArgs const& args) {
+	for (auto page : pages) {
+		NavigationViewItem item = std::get<0>(page);
+		FrameworkElement elm = std::get<1>(page);
+		if (item == navigationview.SelectedItem()) {
+			navigationview.Content(elm);
+			break;
+		}
+	}
+}
+
+static int64_t ui_tabview_get(UiInteger *i) {
+	return 0;
+}
+
+static void ui_tabview_set(UiInteger *i, int64_t value) {
+	UiTabView *tabview = (UiTabView*)i->obj;
+	tabview->Select(value);
+}
+
+UIWIDGET ui_tabview_create(UiObject* obj, UiTabViewArgs args) {
+	UiTabViewType type = args.tabview == UI_TABVIEW_DEFAULT ? UI_TABVIEW_NAVIGATION_TOP2 : args.tabview;
+	UiTabView* tabview = nullptr;
+	switch (type) {
+		default: {
+			tabview = tabview_pivot_create(obj, args);
+			break;
+		}
+		case UI_TABVIEW_DOC: { 
+			tabview = tabview_main_create(obj, args);
+			break;
+		}
+		case UI_TABVIEW_NAVIGATION_SIDE: { 
+			tabview = tabview_navigationview_create(obj, args, type);
+			break;
+		}
+		case UI_TABVIEW_NAVIGATION_TOP: { 
+			tabview = tabview_navigationview_create(obj, args, type);
+			break;
+		}
+		case UI_TABVIEW_NAVIGATION_TOP2: { 
+			tabview = tabview_pivot_create(obj, args);
+			break;
+		}
+		case UI_TABVIEW_INVISIBLE: {
+			tabview = tabview_invisible_create(obj, args);
+			break;
+		}
+	}
+	UiTabViewContainer* ctn = new UiTabViewContainer(tabview);
+
+	// add frame to the parent container
+	UiObject* current = uic_current_obj(obj);
+	UI_APPLY_LAYOUT1(current, args);
+	current->container->Add(tabview->GetFrameworkElement(), true);
+
+	UIElement elm = tabview->GetFrameworkElement();
+	UiWidget* widget = new UiWidget(elm);
+	ui_context_add_widget_destructor(current->ctx, widget);
+	widget->data1 = tabview;
+
+	// TODO: add tabview destructor
+
+	// bind variable
+	UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_INTEGER);
+	if (var) {
+		UiInteger *i = (UiInteger*)var->value;
+		i->obj = tabview;
+		i->get = ui_tabview_get;
+		i->set = ui_tabview_set;
+	}
+
+	UiObject* newobj = uic_object_new(obj, widget);
+	newobj->container = ctn;
+	uic_obj_add(obj, newobj);
+
+	return widget;
+}
+
+void ui_tab_create(UiObject* obj, const char* title) {
+	UiObject* current = uic_current_obj(obj);
+	UiTabView* tabview = (UiTabView*)current->widget->data1;
+	UiObject* newobj = tabview->AddTab(title);
+	uic_obj_add(current, newobj);
+}
+
+UIEXPORT void ui_tabview_select(UIWIDGET tabview, int tab) {
+	UiTabView* t = (UiTabView*)tabview->data1;
+	t->Select(tab);
+}
+
+UIEXPORT void ui_tabview_remove(UIWIDGET tabview, int tab) {
+	UiTabView* t = (UiTabView*)tabview->data1;
+	t->Remove(tab);
+}
+
+UIEXPORT UiObject* ui_tabview_add(UIWIDGET tabview, const char *name, int tab_index) {
+	UiTabView* t = (UiTabView*)tabview->data1;
+	UiObject* newobj = t->AddTab(name, tab_index);
+	return newobj;
+}
+
+
+
+// --------------------- UI Headerbar ---------------------
+
+// TODO: replace placeholder implementation
+
+UIEXPORT UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs args) {
+	UiContainerArgs boxargs = { };
+	boxargs.fill = UI_OFF;
+	return ui_hbox_create(obj, boxargs);
+}
+
+UIEXPORT void ui_headerbar_start_create(UiObject *obj) {
+	UiContainerArgs boxargs = { };
+	boxargs.fill = UI_OFF;
+	ui_hbox_create(obj, boxargs);
+}
+
+UIEXPORT void ui_headerbar_center_create(UiObject *obj) {
+	UiContainerArgs boxargs = { };
+	boxargs.fill = UI_OFF;
+	ui_hbox_create(obj, boxargs);
+}
+
+UIEXPORT void ui_headerbar_end_create(UiObject *obj) {
+	UiContainerArgs boxargs = { };
+	boxargs.fill = UI_OFF;
+	ui_hbox_create(obj, boxargs);
+}
+
+
+/*
+* -------------------- Layout Functions --------------------
+*
+* functions for setting layout attributes for the current container
+*
+*/
+
+void ui_layout_fill(UiObject* obj, UiBool fill) {
+	UiContainer* ct = uic_get_current_container(obj);
+	ct->layout.fill = ui_bool2lb(fill);
+}
+
+void ui_layout_hexpand(UiObject* obj, UiBool expand) {
+	UiContainer* ct = uic_get_current_container(obj);
+	ct->layout.hexpand = expand;
+}
+
+void ui_layout_vexpand(UiObject* obj, UiBool expand) {
+	UiContainer* ct = uic_get_current_container(obj);
+	ct->layout.vexpand = expand;
+}
+
+void ui_layout_hfill(UiObject* obj, UiBool fill) {
+	UiContainer* ct = uic_get_current_container(obj);
+	ct->layout.hfill = fill;
+}
+
+void ui_layout_vfill(UiObject* obj, UiBool fill) {
+	UiContainer* ct = uic_get_current_container(obj);
+	ct->layout.vfill = fill;
+}
+
+void ui_layout_width(UiObject* obj, int width) {
+	UiContainer* ct = uic_get_current_container(obj);
+	ct->layout.width = width;
+}
+
+void ui_layout_height(UiObject* obj, int height) {
+	UiContainer* ct = uic_get_current_container(obj);
+	ct->layout.height = height;
+}
+
+void ui_layout_colspan(UiObject* obj, int cols) {
+	UiContainer* ct = uic_get_current_container(obj);
+	ct->layout.colspan = cols;
+}
+
+void ui_layout_rowspan(UiObject* obj, int rows) {
+	UiContainer* ct = uic_get_current_container(obj);
+	ct->layout.rowspan = rows;
+}
+
+void ui_newline(UiObject* obj) {
+	UiContainer* ct = uic_get_current_container(obj);
+	ct->layout.newline = TRUE;
+}
+

mercurial