Sun, 16 Jun 2024 11:36:27 +0200
update toolkit
/* * 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; int hexpand = FALSE; int vexpand = FALSE; if (layout.hexpand != UI_LAYOUT_UNDEFINED) { hexpand = layout.hexpand; } if (layout.vexpand != UI_LAYOUT_UNDEFINED) { vexpand = layout.vexpand; } // 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); } // 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++; } // add control control.HorizontalAlignment(HorizontalAlignment::Stretch); 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; } UiPivotTabView::UiPivotTabView(UiObject* obj, Pivot pivot, UiTabViewArgs args) { this->current = obj; this->pivot = pivot; this->margin = args.margin; this->spacing = args.spacing; this->columnspacing = args.columnspacing; this->rowspacing = args.rowspacing; } UiObject* UiPivotTabView::AddTab(const char* label) { 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); } FrameworkElement UiPivotTabView::GetFrameworkElement() { return pivot; } static UiTabView* tabview_pivot_create(UiObject* obj, UiTabViewArgs args) { Pivot pivot = Pivot(); UiPivotTabView* tabview = new UiPivotTabView(obj, pivot, args); return tabview; } UiMainTabView::UiMainTabView(UiObject* obj, TabView tabview, UiTabViewArgs args) { this->current = obj; this->tabview = tabview; this->margin = args.margin; this->spacing = args.spacing; this->columnspacing = args.columnspacing; this->rowspacing = args.rowspacing; } UiObject* UiMainTabView::AddTab(const char* label) { 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); } FrameworkElement UiMainTabView::GetFrameworkElement() { return tabview; } 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; } 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) { 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); } 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 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; } 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; } } 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; 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); } /* * -------------------- 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_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; }