#include "pch.h"
#include "table.h"
#include "container.h"
#include "util.h"
#include "icons.h"
#include "../common/context.h"
#include "../common/object.h"
#include "../common/types.h"
#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>
#include <winrt/Windows.ApplicationModel.h>
#include <winrt/Windows.ApplicationModel.DataTransfer.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;
static UINT ui_double_click_time = GetDoubleClickTime();
extern "C" void reg_table_destructor(UiContext * ctx, UiTable * table) {
}
static void textblock_set_str(TextBlock& t,
const char* str) {
if (str) {
wchar_t* wstr = str2wstr(str, nullptr);
t.Text(winrt::hstring(wstr));
free(wstr);
}
}
static void textblock_set_int(TextBlock& t,
int i) {
wchar_t buf[
16];
swprintf(buf,
16,
"%d"L, i);
t.Text(winrt::hstring(buf));
}
UIEXPORT UIWIDGET ui_table_create(UiObject* obj, UiListArgs args) {
if (!args.model) {
return nullptr;
}
UiObject* current = uic_current_obj(obj);
ScrollViewer scrollW = ScrollViewer();
Grid grid = Grid();
scrollW.Content(grid);
UiTable* uitable = new UiTable(obj, scrollW, grid);
reg_table_destructor(current->ctx, uitable);
uitable->getvalue = args.model->getvalue ? args.model->getvalue : args.getvalue;
uitable->onselection = args.onselection;
uitable->onselectiondata = args.onselectiondata;
uitable->onactivate = args.onactivate;
uitable->onactivatedata = args.onactivatedata;
uitable->ondragstart = args.ondragstart;
uitable->ondragstartdata = args.ondragstartdata;
uitable->ondragcomplete = args.ondragcomplete;
uitable->ondrop = args.ondrop;
uitable->ondropdata = args.ondropsdata;
winrt::Windows::
UI::Color bg = {
255,
255,
255,
255 };
SolidColorBrush brush = SolidColorBrush(bg);
grid.Background(brush);
uitable->add_header(args.model);
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->getselection = ui_table_selection;
list->obj = uitable;
uitable->update(list,
0);
}
UIElement elm = scrollW;
UiWidget* widget = new UiWidget(elm);
ui_context_add_widget_destructor(current->ctx, widget);
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);
}
extern "C" UiListSelection ui_table_selection(UiList * list) {
UiTable* table = (UiTable*)list->obj;
return table->uiselection();
}
UiTable::UiTable(UiObject *obj, winrt::Microsoft::
UI::Xaml::Controls::ScrollViewer scrollW, winrt::Microsoft::
UI::Xaml::Controls::Grid grid) {
this->obj = obj;
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 };
defaultBrush = SolidColorBrush(defaultBg);
winrt::Windows::
UI::Color selectedBg = {
255,
204,
232,
255 };
selectedBrush = SolidColorBrush(selectedBg);
winrt::Windows::
UI::Color selectedFg = {
255,
0,
90,
158 };
selectedBorderBrush = SolidColorBrush(selectedFg);
grid.KeyDown(
winrt::Microsoft::
UI::Xaml::Input::KeyEventHandler(
[=](IInspectable
const& sender, winrt::Microsoft::
UI::Xaml::Input::KeyRoutedEventArgs
const& args) {
})
);
}
UiTable::~UiTable() {
ui_model_free(
NULL, model);
}
void UiTable::add_header(UiModel* model) {
this->model = ui_model_copy(
NULL, model);
GridLength gl;
gl.Value =
0;
gl.GridUnitType = GridUnitType::Auto;
auto headerRowDef = RowDefinition();
headerRowDef.Height(gl);
grid.RowDefinitions().Append(headerRowDef);
winrt::Windows::
UI::Color borderColor = {
63,
0,
0,
0 };
SolidColorBrush borderBrush = SolidColorBrush(borderColor);
for (
int i =
0; i < model->columns;i++) {
char* title = model->titles[i];
UiModelType type = model->types[i];
auto colDef = ColumnDefinition();
colDef.Width(gl);
grid.ColumnDefinitions().Append(colDef);
Border headerBorder = Border();
Thickness border = {
0,
0,
1,
0 };
headerBorder.BorderThickness(border);
headerBorder.BorderBrush(borderBrush);
auto hLabel = TextBlock();
textblock_set_str(hLabel, title);
Thickness cellpadding = {
10,
4,
4,
4 };
hLabel.Padding(cellpadding);
hLabel.VerticalAlignment(VerticalAlignment::Stretch);
headerBorder.PointerPressed(
winrt::Microsoft::
UI::Xaml::Input::PointerEventHandler(
[=](IInspectable
const& sender, winrt::Microsoft::
UI::Xaml::Input::PointerRoutedEventArgs
const& args) {
if (i +
1 < model->columns) {
double width = headerBorder.ActualWidth();
auto point = args.GetCurrentPoint(headerBorder);
auto position = point.Position();
if (position.
X +
4 >= width) {
this->resize = true;
this->resizedCol = headerBorder;
}
}
})
);
headerBorder.PointerReleased(
winrt::Microsoft::
UI::Xaml::Input::PointerEventHandler(
[=](IInspectable
const& sender, winrt::Microsoft::
UI::Xaml::Input::PointerRoutedEventArgs
const& args) {
this->resize = false;
})
);
headerBorder.PointerMoved(
winrt::Microsoft::
UI::Xaml::Input::PointerEventHandler(
[=](IInspectable
const& sender, winrt::Microsoft::
UI::Xaml::Input::PointerRoutedEventArgs
const& args) {
if (this->resize) {
auto point = args.GetCurrentPoint(this->resizedCol);
auto position = point.Position();
if (position.
X >
1) {
this->resizedCol.Width(position.
X);
}
}
})
);
headerBorder.PointerEntered(
winrt::Microsoft::
UI::Xaml::Input::PointerEventHandler(
[=](IInspectable
const& sender, winrt::Microsoft::
UI::Xaml::Input::PointerRoutedEventArgs
const& args) {
})
);
headerBorder.PointerExited(
winrt::Microsoft::
UI::Xaml::Input::PointerEventHandler(
[=](IInspectable
const& sender, winrt::Microsoft::
UI::Xaml::Input::PointerRoutedEventArgs
const& args) {
})
);
headerBorder.Child(hLabel);
grid.SetColumn(headerBorder, i);
grid.SetRow(headerBorder,
0);
grid.Children().Append(headerBorder);
UiTableColumn h;
h.header = headerBorder;
header.push_back(h);
}
maxrows =
1;
}
static ULONG64 getsystime() {
SYSTEMTIME st;
GetSystemTime(&st);
return st.wYear *
10000000000000 +
st.wMonth *
100000000000 +
st.wDay *
1000000000 +
st.wHour *
10000000 +
st.wMinute *
100000 +
st.wSecond *
1000 +
st.wMilliseconds;
}
void UiTable::update(UiList* list,
int i) {
if (getvalue == nullptr) {
return;
}
Thickness b1 = {
1,
1,
0,
1 };
Thickness b2 = {
0,
1,
0,
1 };
Thickness b3 = {
0,
1,
1,
1 };
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;
}
Thickness cellpadding = {
10,
0,
4,
0 };
int model_col =
0;
for (
int col =
0; col < header.size(); col++, model_col++) {
Border cellBorder = Border();
cellBorder.Background(defaultBrush);
cellBorder.BorderBrush(defaultBrush);
if (col ==
0) {
cellBorder.BorderThickness(b1);
}
else if (col +
1 == header.size()) {
cellBorder.BorderThickness(b3);
}
else {
cellBorder.BorderThickness(b2);
}
if (ondragstart) {
cellBorder.CanDrag(true);
cellBorder.DragStarting([this](IInspectable
const& sender, DragStartingEventArgs args) {
UiDnD dnd;
dnd.evttype =
0;
dnd.dndstartargs = args;
dnd.dndcompletedargs = { nullptr };
dnd.drageventargs = { nullptr };
dnd.data = args.Data();
UiEvent evt;
evt.obj = this->obj;
evt.window = evt.obj->window;
evt.document = obj->ctx->document;
evt.eventdata = &dnd;
evt.intval =
0;
this->ondragstart(&evt, this->ondragstartdata);
});
cellBorder.DropCompleted([this](IInspectable
const& sender, DropCompletedEventArgs args) {
UiDnD dnd;
dnd.evttype =
1;
dnd.dndstartargs = { nullptr };
dnd.dndcompletedargs = args;
dnd.drageventargs = { nullptr };
dnd.data = { nullptr };
UiEvent evt;
evt.obj = this->obj;
evt.window = evt.obj->window;
evt.document = obj->ctx->document;
evt.eventdata = &dnd;
evt.intval =
0;
if (this->ondragcomplete) {
this->ondragcomplete(&evt, this->ondragcompletedata);
}
});
}
if (ondrop) {
cellBorder.AllowDrop(true);
cellBorder.Drop(DragEventHandler([this](winrt::Windows::Foundation::IInspectable
const& sender, DragEventArgs
const& args){
UiDnD dnd;
dnd.evttype =
2;
dnd.dndstartargs = { nullptr };
dnd.dndcompletedargs = { nullptr };
dnd.drageventargs = args;
dnd.dataview = args.DataView();
UiEvent evt;
evt.obj = this->obj;
evt.window = evt.obj->window;
evt.document = obj->ctx->document;
evt.eventdata = &dnd;
evt.intval =
0;
this->ondrop(&evt, this->ondropdata);
}));
cellBorder.DragOver(DragEventHandler([this](winrt::Windows::Foundation::IInspectable
const& sender, DragEventArgs
const& args){
args.AcceptedOperation(winrt::Windows::ApplicationModel::DataTransfer::DataPackageOperation::Copy);
}));
}
UiModelType type = model->types[col];
switch (type) {
case UI_STRING_FREE:
case UI_STRING: {
TextBlock cell = TextBlock();
cell.Padding(cellpadding);
cell.VerticalAlignment(VerticalAlignment::Stretch);
char *val = (
char*)getvalue(elm, model_col);
textblock_set_str(cell, val);
cellBorder.Child(cell);
if (type ==
UI_STRING_FREE && val) {
free(val);
}
break;
}
case UI_INTEGER: {
TextBlock cell = TextBlock();
cell.Padding(cellpadding);
cell.VerticalAlignment(VerticalAlignment::Stretch);
int *value = (
int*)getvalue(elm, model_col);
if (value) {
textblock_set_int(cell, *value);
}
cellBorder.Child(cell);
break;
}
case UI_ICON: {
UiIcon* iconConstr = (UiIcon*)getvalue(elm, model_col);
if (iconConstr) {
IconElement icon = iconConstr->getIcon();
cellBorder.Child(icon);
}
break;
}
case UI_ICON_TEXT_FREE:
case UI_ICON_TEXT: {
StackPanel cellPanel = StackPanel();
cellPanel.Spacing(
2);
cellPanel.Padding(cellpadding);
cellPanel.VerticalAlignment(VerticalAlignment::Stretch);
cellPanel.Orientation(Orientation::Horizontal);
UiIcon* iconConstr = (UiIcon*)getvalue(elm, model_col++);
char* str = (
char*)getvalue(elm, model_col);
if (iconConstr) {
IconElement icon = iconConstr->getIcon();
cellPanel.Children().Append(icon);
}
TextBlock cell = TextBlock();
textblock_set_str(cell, str);
cellPanel.Children().Append(cell);
cellBorder.Child(cellPanel);
if (type ==
UI_ICON_TEXT_FREE && str) {
free(str);
}
break;
}
}
cellBorder.PointerPressed(
winrt::Microsoft::
UI::Xaml::Input::PointerEventHandler(
[=](IInspectable
const& sender, winrt::Microsoft::
UI::Xaml::Input::PointerRoutedEventArgs
const& args) {
winrt::Windows::System::VirtualKeyModifiers modifiers = args.KeyModifiers();
bool update_selection = true;
if (modifiers == winrt::Windows::System::VirtualKeyModifiers::Control) {
if (!is_row_selected(row)) {
row_background(row, selectedBrush, selectedBorderBrush);
selection.push_back(row);
}
else {
row_background(row, highlightBrush, highlightBrush);
remove_from_selection(row);
}
}
else if (modifiers == winrt::Windows::System::VirtualKeyModifiers::None || selection.size() ==
0) {
if (selection.size() >
0) {
change_rows_bg(selection, defaultBrush, defaultBrush);
}
row_background(row, selectedBrush, selectedBorderBrush);
selection = { row };
if (modifiers == winrt::Windows::System::VirtualKeyModifiers::None) {
SYSTEMTIME st;
GetSystemTime(&st);
ULONG64 now = getsystime();
ULONG64 tdiff = now - lastPointerPress;
if (tdiff < ui_double_click_time && onactivate != nullptr) {
update_selection = false;
lastPointerPress =
0;
int selectedrow = row -
1;
UiListSelection selection;
selection.count =
1;
selection.rows = &selectedrow;
UiEvent evt;
evt.obj = obj;
evt.window = obj->window;
evt.document = obj->ctx->document;
evt.eventdata = &selection;
evt.intval = selectedrow;
onactivate(&evt, onactivatedata);
}
else {
lastPointerPress = now;
}
}
}
else if (modifiers == winrt::Windows::System::VirtualKeyModifiers::Shift) {
std::sort(selection.begin(), selection.end());
int first = selection.front();
int last = row;
if (first > row) {
last = first;
first = row;
}
change_rows_bg(selection, defaultBrush, defaultBrush);
std::vector<
int> newselection;
for (
int s = first; s <= last; s++) {
newselection.push_back(s);
}
selection = newselection;
change_rows_bg(selection, selectedBrush, selectedBorderBrush);
}
if (update_selection) {
call_handler(onselection, onselectiondata);
}
})
);
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 (!is_row_selected(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 (!is_row_selected(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 };
Thickness b2 = {
0,
1,
0,
1 };
Thickness b3 = {
0,
1,
1,
1 };
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);
}
}
}
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();
}
UiListSelection UiTable::uiselection() {
std::sort(selection.begin(), selection.end());
UiListSelection selobj;
selobj.count = selection.size();
selobj.rows = nullptr;
if (selobj.count >
0) {
selobj.rows = (
int*)calloc(selobj.count,
sizeof(
int));
memcpy(selobj.rows, selection.data(), selobj.count *
sizeof(
int));
for (
int i =
0; i < selobj.count; i++) {
selobj.rows[i]--;
}
}
return selobj;
}
void UiTable::call_handler(ui_callback cb,
void* cbdata) {
if (!cb) {
return;
}
UiListSelection selobj = uiselection();
UiEvent evt;
evt.obj = obj;
evt.window = obj->window;
evt.document = obj->ctx->document;
evt.eventdata = &selobj;
evt.intval =
0;
cb(&evt, cbdata);
if (selobj.rows) {
free(selobj.rows);
}
}