#include "pch.h"
#include "window.h"
#include "appmenu.h"
#include "commandbar.h"
#include "container.h"
#include "util.h"
#include "button.h"
#include "../common/context.h"
#include "../common/object.h"
#include <stdlib.h>
#include <cx/mempool.h>
#include "MainWindow.xaml.h"
#include <Windows.h>
#include <shobjidl.h>
#include <iostream>
using namespace winrt;
using namespace Microsoft::
UI::Xaml;
using namespace Microsoft::
UI::Xaml::Controls;
using namespace Microsoft::
UI::Xaml::Controls::Primitives;
using namespace Microsoft::
UI::Xaml::XamlTypeInfo;
using namespace Microsoft::
UI::Xaml::Markup;
using namespace Windows::
UI::Xaml::Interop;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Storage::Pickers;
UiWindow::UiWindow(winrt::Microsoft::
UI::Xaml::Window& win) : window(win) {}
UiObject* ui_window(
const char* title,
void* window_data) {
UiObject* obj = ui_simple_window(title, window_data);
if (uic_toolbar_isenabled()) {
Grid toolbar_grid = Grid();
GridLength gl;
gl.Value =
0;
gl.GridUnitType = GridUnitType::Auto;
ColumnDefinition coldef0 = ColumnDefinition();
coldef0.Width(gl);
toolbar_grid.ColumnDefinitions().Append(coldef0);
gl.Value =
1;
gl.GridUnitType = GridUnitType::Star;
ColumnDefinition coldef1 = ColumnDefinition();
coldef1.Width(gl);
toolbar_grid.ColumnDefinitions().Append(coldef1);
gl.Value =
0;
gl.GridUnitType = GridUnitType::Auto;
ColumnDefinition coldef2 = ColumnDefinition();
coldef2.Width(gl);
toolbar_grid.ColumnDefinitions().Append(coldef2);
gl.Value =
0;
gl.GridUnitType = GridUnitType::Auto;
RowDefinition rowdef = RowDefinition();
rowdef.Height(gl);
toolbar_grid.RowDefinitions().Append(rowdef);
CxList* def_l = uic_get_toolbar_defaults(
UI_TOOLBAR_LEFT);
CxList* def_c = uic_get_toolbar_defaults(
UI_TOOLBAR_CENTER);
CxList* def_r = uic_get_toolbar_defaults(
UI_TOOLBAR_RIGHT);
bool addappmenu = true;
if (cxListSize(def_r) >
0) {
CommandBar toolbar_r = ui_create_toolbar(obj, def_r, addappmenu);
toolbar_grid.SetColumn(toolbar_r,
2);
toolbar_grid.SetRow(toolbar_r,
0);
toolbar_grid.Children().Append(toolbar_r);
addappmenu = false;
}
if (cxListSize(def_c) >
0) {
CommandBar toolbar_c = ui_create_toolbar(obj, def_c, addappmenu);
toolbar_c.HorizontalAlignment(HorizontalAlignment::Center);
toolbar_grid.SetColumn(toolbar_c,
1);
toolbar_grid.SetRow(toolbar_c,
0);
toolbar_grid.Children().Append(toolbar_c);
addappmenu = false;
}
if (cxListSize(def_l) >
0) {
CommandBar toolbar_l = ui_create_toolbar(obj, def_l, addappmenu);
toolbar_grid.SetColumn(toolbar_l,
0);
toolbar_grid.SetRow(toolbar_l,
0);
toolbar_grid.Children().Append(toolbar_l);
}
toolbar_grid.VerticalAlignment(VerticalAlignment::Top);
obj->container->Add(toolbar_grid, false);
}
return obj;
}
UIEXPORT UiObject* ui_simple_window(
const char *title,
void *window_data) {
CxMempool* mp = cxBasicMempoolCreate(
256);
UiObject* obj = (UiObject*)cxCalloc(mp->allocator,
1,
sizeof(UiObject));
obj->ctx = uic_context(obj, mp);
obj->window = window_data;
Window window = Window();
winrt::Windows::Foundation::Uri resourceLocator{
"ms-appx:///MainWindow.xaml"L };
Application::LoadComponent(window, resourceLocator, ComponentResourceLocation::Nested);
window.ExtendsContentIntoTitleBar(true);
Grid grid = Grid();
window.Content(grid);
StackPanel titleBar = StackPanel();
Thickness titleBarPadding = {
10,
5,
5,
10 };
titleBar.Padding(titleBarPadding);
titleBar.Orientation(Orientation::Horizontal);
TextBlock titleLabel = TextBlock();
titleBar.Children().Append(titleLabel);
if (title) {
wchar_t* wtitle = str2wstr(title, nullptr);
window.Title(wtitle);
titleLabel.Text(hstring(wtitle));
free(wtitle);
}
window.SetTitleBar(titleBar);
obj->wobj = new UiWindow(window);
ui_context_add_window_destructor(obj->ctx, obj->wobj);
window.Closed([obj](IInspectable
const& sender, WindowEventArgs) {
if (obj->ctx->close_callback) {
UiEvent evt;
evt.obj = obj;
evt.document = obj->ctx->document;
evt.window = obj->window;
evt.eventdata =
NULL;
evt.intval =
0;
obj->ctx->close_callback(&evt, obj->ctx->close_data);
}
else {
ui_context_destroy(obj->ctx);
}
});
obj->container = new UiBoxContainer(grid,
UI_BOX_CONTAINER_VBOX,
0,
0);
titleBar.VerticalAlignment(VerticalAlignment::Top);
obj->container->Add(titleBar, false);
obj->window = window_data;
return obj;
}
static void dialog_button_add_callback(ContentDialog dialog, Button button,
int num, UiObject *obj, ui_callback onclick,
void *onclickdata) {
button.Click([dialog, num, obj, onclick, onclickdata](IInspectable
const& sender, RoutedEventArgs) {
if (onclick) {
UiEvent evt;
evt.obj = obj;
evt.window = obj->window;
evt.document = obj->ctx->document;
evt.eventdata = nullptr;
evt.intval = num;
onclick(&evt, onclickdata);
}
dialog.Hide();
});
}
UIEXPORT UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args) {
UiWindow *window = parent->wobj;
if (!window) {
return NULL;
}
CxMempool* mp = cxBasicMempoolCreate(
256);
UiObject* obj = (UiObject*)cxCalloc(mp->allocator,
1,
sizeof(UiObject));
obj->ctx = uic_context(obj, mp);
ContentDialog dialog = ContentDialog();
UIElement elm = dialog;
UiWidget* widget = new UiWidget(elm);
ui_context_add_widget_destructor(obj->ctx, widget);
obj->widget = widget;
if (args.title) {
wchar_t* wtitle = str2wstr(args.title, nullptr);
dialog.Title(box_value(wtitle));
free(wtitle);
}
Grid dialogContent = Grid();
GridLength gl;
gl.Value =
1;
gl.GridUnitType = GridUnitType::Star;
RowDefinition rowdef0 = RowDefinition();
rowdef0.Height(gl);
dialogContent.RowDefinitions().Append(rowdef0);
gl.Value =
0;
gl.GridUnitType = GridUnitType::Auto;
RowDefinition rowdef1 = RowDefinition();
rowdef1.Height(gl);
dialogContent.RowDefinitions().Append(rowdef1);
gl.Value =
1;
gl.GridUnitType = GridUnitType::Star;
ColumnDefinition coldef = ColumnDefinition();
coldef.Width(gl);
dialogContent.ColumnDefinitions().Append(coldef);
Grid grid = Grid();
grid.SetRow(grid,
0);
grid.SetColumn(grid,
0);
dialogContent.Children().Append(grid);
obj->container = new UiBoxContainer(grid,
UI_BOX_CONTAINER_VBOX,
0,
0);
Grid buttons = Grid();
Thickness btnsMargin = { (
double)
0, (
double)
10, (
double)
0, (
double)
0 };
buttons.Margin(btnsMargin);
RowDefinition btnrowdef = RowDefinition();
gl.Value =
0;
gl.GridUnitType = GridUnitType::Auto;
btnrowdef.Height(gl);
buttons.RowDefinitions().Append(btnrowdef);
gl.Value =
1;
gl.GridUnitType = GridUnitType::Auto;
int c =
0;
if (args.lbutton1) {
ColumnDefinition bcoldef = ColumnDefinition();
bcoldef.Width(gl);
buttons.ColumnDefinitions().Append(bcoldef);
Button btn = Button();
ui_set_button_label(btn, args.lbutton1,
NULL,
NULL,
UI_LABEL_TEXT);
Thickness margin = { (
double)
5, (
double)
5, (
double)
5, (
double)
5 };
btn.Margin(margin);
btn.HorizontalAlignment(HorizontalAlignment::Stretch);
dialog_button_add_callback(dialog, btn,
1, obj, args.onclick, args.onclickdata);
buttons.SetRow(btn,
0);
buttons.SetColumn(btn, c++);
buttons.Children().Append(btn);
}
if (args.lbutton2) {
ColumnDefinition bcoldef = ColumnDefinition();
bcoldef.Width(gl);
buttons.ColumnDefinitions().Append(bcoldef);
Button btn = Button();
ui_set_button_label(btn, args.lbutton2,
NULL,
NULL,
UI_LABEL_TEXT);
Thickness margin = { (
double)
5, (
double)
5, (
double)
5, (
double)
5 };
btn.Margin(margin);
btn.HorizontalAlignment(HorizontalAlignment::Stretch);
dialog_button_add_callback(dialog, btn,
2, obj, args.onclick, args.onclickdata);
buttons.SetRow(btn,
0);
buttons.SetColumn(btn, c++);
buttons.Children().Append(btn);
}
if (args.rbutton3) {
ColumnDefinition bcoldef = ColumnDefinition();
bcoldef.Width(gl);
buttons.ColumnDefinitions().Append(bcoldef);
Button btn = Button();
ui_set_button_label(btn, args.rbutton3,
NULL,
NULL,
UI_LABEL_TEXT);
Thickness margin = { (
double)
5, (
double)
5, (
double)
5, (
double)
5 };
btn.Margin(margin);
btn.HorizontalAlignment(HorizontalAlignment::Stretch);
dialog_button_add_callback(dialog, btn,
3, obj, args.onclick, args.onclickdata);
buttons.SetRow(btn,
0);
buttons.SetColumn(btn, c++);
buttons.Children().Append(btn);
}
if (args.rbutton4) {
ColumnDefinition bcoldef = ColumnDefinition();
bcoldef.Width(gl);
buttons.ColumnDefinitions().Append(bcoldef);
Button btn = Button();
ui_set_button_label(btn, args.rbutton4,
NULL,
NULL,
UI_LABEL_TEXT);
Thickness margin = { (
double)
5, (
double)
5, (
double)
5, (
double)
5 };
btn.Margin(margin);
btn.HorizontalAlignment(HorizontalAlignment::Stretch);
dialog_button_add_callback(dialog, btn,
4, obj, args.onclick, args.onclickdata);
buttons.SetRow(btn,
0);
buttons.SetColumn(btn, c++);
buttons.Children().Append(btn);
}
dialogContent.SetRow(buttons,
1);
dialogContent.SetColumn(buttons,
0);
dialogContent.Children().Append(buttons);
dialog.Content(dialogContent);
dialog.XamlRoot(window->window.Content().XamlRoot());
obj->widget->Show = [dialog]() {
dialog.ShowAsync();
};
return obj;
}
void ui_window_size(UiObject *obj,
int width,
int height) {
UIWINDOW win = obj->wobj;
if (win) {
winrt::Windows::Graphics::SizeInt32 wsize;
wsize.Width = width;
wsize.Height = height;
win->window.AppWindow().Resize(wsize);
}
}
static Windows::Foundation::IAsyncAction create_dialog_async(UiObject *obj, UiDialogArgs args) {
UiObject* current = uic_current_obj(obj);
Window parentWindow = current->wobj->window;
ContentDialog dialog = ContentDialog();
dialog.XamlRoot(parentWindow.Content().XamlRoot());
if (args.title) {
wchar_t *str = str2wstr(args.title, nullptr);
dialog.Title(winrt::box_value(str));
free(str);
}
TextBox textfield{ nullptr };
PasswordBox password{ nullptr };
if(args.input || args.password) {
StackPanel panel = StackPanel();
panel.Orientation(Orientation::Vertical);
if (args.content) {
wchar_t *str = str2wstr(args.content, nullptr);
TextBlock label = TextBlock();
label.Text(str);
panel.Children().Append(label);
free(str);
}
Thickness margin = {
0,
5,
0,
0 };
if (args.password) {
password = PasswordBox();
password.Margin(margin);
panel.Children().Append(password);
}
else {
textfield = TextBox();
textfield.Margin(margin);
panel.Children().Append(textfield);
}
panel.Margin(margin);
dialog.Content(panel);
}
else {
if (args.content) {
wchar_t *str = str2wstr(args.content, nullptr);
dialog.Content(winrt::box_value(str));
free(str);
}
}
if (args.button1_label) {
wchar_t *str = str2wstr(args.button1_label, nullptr);
dialog.PrimaryButtonText(winrt::hstring(str));
free(str);
dialog.DefaultButton(ContentDialogButton::Primary);
}
if (args.button2_label) {
wchar_t *str = str2wstr(args.button2_label, nullptr);
dialog.SecondaryButtonText(winrt::hstring(str));
free(str);
}
if (args.closebutton_label) {
wchar_t *str = str2wstr(args.closebutton_label, nullptr);
dialog.CloseButtonText(winrt::hstring(str));
free(str);
}
ContentDialogResult result = co_await dialog.ShowAsync();
if (args.result) {
UiEvent evt;
evt.obj = current;
evt.document = current->ctx->document;
evt.window = current->window;
evt.eventdata =
NULL;
evt.intval =
0;
if (result == ContentDialogResult::Primary) {
evt.intval =
1;
}
else if (result == ContentDialogResult::Secondary) {
evt.intval =
2;
}
if (args.password) {
std::wstring wstr(password.Password());
char *text = wchar2utf8(wstr.c_str(), wstr.length());
evt.eventdata = text;
}
else if (args.input) {
std::wstring wstr(textfield.Text());
char *text = wchar2utf8(wstr.c_str(), wstr.length());
evt.eventdata = text;
}
args.result(&evt, args.resultdata);
if (evt.eventdata) {
free(evt.eventdata);
}
}
}
UIEXPORT void ui_dialog_create(UiObject *obj, UiDialogArgs args) {
create_dialog_async(obj, args);
}
static void filedialog_callback(
UiObject *obj,
ui_callback file_selected_callback,
void *cbdata,
winrt::Windows::Foundation::Collections::IVectorView<winrt::Windows::Storage::StorageFile> result)
{
UiFileList flist;
flist.nfiles = result.Size();
flist.files = new
char*[flist.nfiles];
int i =
0;
for (
auto const& file : result) {
winrt::hstring path = file.Path();
flist.files[i++] = wchar2utf8(path.c_str(), path.size());
}
UiEvent evt;
evt.obj = obj;
evt.document = obj->ctx->document;
evt.window = obj->window;
evt.eventdata = &flist;
evt.intval =
0;
file_selected_callback(&evt, cbdata);
for (
int i =
0; i < flist.nfiles;i++) {
free(flist.files[i]);
}
delete[] flist.files;
}
static Windows::Foundation::IAsyncAction open_filedialog_async(UiObject *obj,
unsigned int mode, ui_callback file_selected_callback,
void *cbdata) {
FileOpenPicker openFileDialog = FileOpenPicker();
auto initializeWithWindow { openFileDialog.as<::IInitializeWithWindow>()
};
HWND hwnd{ nullptr };
winrt::check_hresult(obj->wobj->window.as<IWindowNative>()->get_WindowHandle(&hwnd));
initializeWithWindow->Initialize(hwnd);
openFileDialog.FileTypeFilter().Append(
"*"L);
if ((mode &
UI_FILEDIALOG_SELECT_MULTI) ==
UI_FILEDIALOG_SELECT_MULTI) {
auto files = co_await openFileDialog.PickMultipleFilesAsync();
filedialog_callback(obj, file_selected_callback, cbdata, files);
}
else {
auto file = co_await openFileDialog.PickSingleFileAsync();
auto files = single_threaded_vector<winrt::Windows::Storage::StorageFile>();
files.Append(file);
filedialog_callback(obj, file_selected_callback, cbdata, files.GetView());
}
}
static Windows::Foundation::IAsyncAction save_filedialog_async(UiObject *obj,
char *name, ui_callback file_selected_callback,
void *cbdata) {
IFileSaveDialog *saveFileDialog;
HRESULT hr = CoCreateInstance(CLSID_FileSaveDialog,
NULL,
CLSCTX_ALL, IID_IFileSaveDialog, reinterpret_cast<
void**>(&saveFileDialog));
if (
FAILED(hr))
{
co_return;
}
if (name) {
wchar_t *wname = str2wstr(name,
NULL);
saveFileDialog->SetFileName(wname);
free(wname);
free(name);
}
hr = saveFileDialog->Show(
NULL);
if (
SUCCEEDED(hr)) {
IShellItem *item;
hr = saveFileDialog->GetResult(&item);
if (
SUCCEEDED(hr)) {
PWSTR wpath;
hr = item->GetDisplayName(
SIGDN_FILESYSPATH, &wpath);
if (
SUCCEEDED(hr)) {
char *path = wchar2utf8(wpath, lstrlen(wpath));
CoTaskMemFree(wpath);
UiFileList flist;
flist.nfiles =
1;
flist.files = new
char*[
1];
flist.files[
0] = path;
UiEvent evt;
evt.obj = obj;
evt.document = obj->ctx->document;
evt.window = obj->window;
evt.eventdata = &flist;
evt.intval =
0;
file_selected_callback(&evt, cbdata);
free(path);
delete[] flist.files;
}
item->Release();
}
}
saveFileDialog->Release();
}
static Windows::Foundation::IAsyncAction folderdialog_async(UiObject *obj, ui_callback file_selected_callback,
void *cbdata) {
FolderPicker folderPicker = FolderPicker();
auto initializeWithWindow { folderPicker.as<::IInitializeWithWindow>()
};
HWND hwnd{ nullptr };
winrt::check_hresult(obj->wobj->window.as<IWindowNative>()->get_WindowHandle(&hwnd));
initializeWithWindow->Initialize(hwnd);
folderPicker.FileTypeFilter().Append(
"*"L);
auto folder = co_await folderPicker.PickSingleFolderAsync();
if (folder) {
winrt::hstring hpath = folder.Path();
char *cpath = wchar2utf8(hpath.c_str(), hpath.size());
UiFileList flist;
flist.nfiles =
1;
flist.files = &cpath;
UiEvent evt;
evt.obj = obj;
evt.document = obj->ctx->document;
evt.window = obj->window;
evt.eventdata = &flist;
evt.intval =
0;
file_selected_callback(&evt, cbdata);
}
}
UIEXPORT void ui_openfiledialog(UiObject *obj,
unsigned int mode, ui_callback file_selected_callback,
void *cbdata) {
if ((mode &
UI_FILEDIALOG_SELECT_FOLDER) ==
UI_FILEDIALOG_SELECT_FOLDER) {
folderdialog_async(obj, file_selected_callback, cbdata);
}
else {
open_filedialog_async(obj, mode, file_selected_callback, cbdata);
}
}
UIEXPORT void ui_savefiledialog(UiObject *obj,
const char *name, ui_callback file_selected_callback,
void *cbdata) {
char *n = name ? _strdup(name) :
NULL;
save_filedialog_async(obj, n, file_selected_callback, cbdata);
}