1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include "pch.h"
30
31 #include "table.h"
32 #include "container.h"
33 #include "util.h"
34 #include "icons.h"
35
36 #include "../common/context.h"
37 #include "../common/object.h"
38 #include "../common/types.h"
39
40 #include <winrt/Microsoft.UI.Xaml.Data.h>
41 #include <winrt/Microsoft.UI.Xaml.Media.h>
42 #include <winrt/Microsoft.UI.Xaml.Input.h>
43 #include <winrt/Windows.UI.Core.h>
44 #include <winrt/Windows.ApplicationModel.h>
45 #include <winrt/Windows.ApplicationModel.DataTransfer.h>
46
47 using namespace winrt;
48 using namespace Microsoft::
UI::Xaml;
49 using namespace Microsoft::
UI::Xaml::Controls;
50 using namespace Windows::
UI::Xaml::Interop;
51 using namespace winrt::Windows::Foundation;
52 using namespace winrt::Microsoft::
UI::Xaml::Controls::Primitives;
53 using namespace winrt::Microsoft::
UI::Xaml::Media;
54 using namespace winrt::Windows::
UI::Xaml::Input;
55
56 static UINT ui_double_click_time = GetDoubleClickTime();
57
58 extern "C" void reg_table_destructor(UiContext * ctx, UiTable * table) {
59
60 }
61
62 static void textblock_set_str(TextBlock& t,
const char* str) {
63 if (str) {
64 wchar_t* wstr = str2wstr(str, nullptr);
65 t.Text(winrt::hstring(wstr));
66 free(wstr);
67 }
68 }
69
70 static void textblock_set_int(TextBlock& t,
int i) {
71 wchar_t buf[
16];
72 swprintf(buf,
16,
"%d"L, i);
73 t.Text(winrt::hstring(buf));
74 }
75
76 UIEXPORT UIWIDGET ui_table_create(UiObject* obj, UiListArgs args) {
77 if (!args.model) {
78 return nullptr;
79 }
80
81 UiObject* current = uic_current_obj(obj);
82
83
84 ScrollViewer scrollW = ScrollViewer();
85 Grid grid = Grid();
86 scrollW.Content(grid);
87 UiTable* uitable = new UiTable(obj, scrollW, grid);
88 reg_table_destructor(current->ctx, uitable);
89
90 uitable->getvalue = args.model->getvalue ? args.model->getvalue : args.getvalue;
91 uitable->onselection = args.onselection;
92 uitable->onselectiondata = args.onselectiondata;
93 uitable->onactivate = args.onactivate;
94 uitable->onactivatedata = args.onactivatedata;
95 uitable->ondragstart = args.ondragstart;
96 uitable->ondragstartdata = args.ondragstartdata;
97 uitable->ondragcomplete = args.ondragcomplete;
98 uitable->ondrop = args.ondrop;
99 uitable->ondropdata = args.ondropsdata;
100
101
102 winrt::Windows::
UI::Color bg = {
255,
255,
255,
255 };
103 SolidColorBrush brush = SolidColorBrush(bg);
104 grid.Background(brush);
105
106
107 uitable->add_header(args.model);
108
109
110 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname,
UI_VAR_LIST);
111 if (var) {
112 UiList* list = (UiList*)var->value;
113 list->update = ui_table_update;
114 list->getselection = ui_table_selection;
115 list->obj = uitable;
116 uitable->update(list,
0);
117 }
118
119
120 UIElement elm = scrollW;
121 UiWidget* widget = new UiWidget(elm);
122 ui_context_add_widget_destructor(current->ctx, widget);
123
124
125 UI_APPLY_LAYOUT1(current, args);
126
127 current->container->Add(scrollW, false);
128
129 return widget;
130 }
131
132 extern "C" void ui_table_update(UiList * list,
int i) {
133 UiTable* table = (UiTable*)list->obj;
134 table->clear();
135 table->update(list, i);
136 }
137
138 extern "C" UiListSelection ui_table_selection(UiList * list) {
139 UiTable* table = (UiTable*)list->obj;
140 return table->uiselection();
141 }
142
143 UiTable::UiTable(UiObject *obj, winrt::Microsoft::
UI::Xaml::Controls::ScrollViewer scrollW, winrt::Microsoft::
UI::Xaml::Controls::Grid grid) {
144 this->obj = obj;
145
146 this->scrollw = scrollw;
147 this->grid = grid;
148
149 winrt::Windows::
UI::Color highlightBg = {
255,
234,
234,
234 };
150 highlightBrush = SolidColorBrush(highlightBg);
151
152 winrt::Windows::
UI::Color defaultBg = {
0,
0,
0,
0 };
153 defaultBrush = SolidColorBrush(defaultBg);
154
155 winrt::Windows::
UI::Color selectedBg = {
255,
204,
232,
255 };
156 selectedBrush = SolidColorBrush(selectedBg);
157
158 winrt::Windows::
UI::Color selectedFg = {
255,
0,
90,
158 };
159 selectedBorderBrush = SolidColorBrush(selectedFg);
160
161 grid.KeyDown(
162 winrt::Microsoft::
UI::Xaml::Input::KeyEventHandler(
163 [=](IInspectable
const& sender, winrt::Microsoft::
UI::Xaml::Input::KeyRoutedEventArgs
const& args) {
164
165 })
166 );
167 }
168
169 UiTable::~UiTable() {
170 ui_model_free(
NULL, model);
171 }
172
173 void UiTable::add_header(UiModel* model) {
174 this->model = ui_model_copy(
NULL, model);
175
176 GridLength gl;
177 gl.Value =
0;
178 gl.GridUnitType = GridUnitType::Auto;
179
180
181 auto headerRowDef = RowDefinition();
182 headerRowDef.Height(gl);
183 grid.RowDefinitions().Append(headerRowDef);
184
185 winrt::Windows::
UI::Color borderColor = {
63,
0,
0,
0 };
186 SolidColorBrush borderBrush = SolidColorBrush(borderColor);
187
188
189 for (
int i =
0; i < model->columns;i++) {
190 char* title = model->titles[i];
191 UiModelType type = model->types[i];
192
193
194 auto colDef = ColumnDefinition();
195 colDef.Width(gl);
196 grid.ColumnDefinitions().Append(colDef);
197
198
199 Border headerBorder = Border();
200 Thickness border = {
0,
0,
1,
0 };
201 headerBorder.BorderThickness(border);
202 headerBorder.BorderBrush(borderBrush);
203
204
205 auto hLabel = TextBlock();
206 textblock_set_str(hLabel, title);
207 Thickness cellpadding = {
10,
4,
4,
4 };
208 hLabel.Padding(cellpadding);
209 hLabel.VerticalAlignment(VerticalAlignment::Stretch);
210
211
212 headerBorder.PointerPressed(
213 winrt::Microsoft::
UI::Xaml::Input::PointerEventHandler(
214 [=](IInspectable
const& sender, winrt::Microsoft::
UI::Xaml::Input::PointerRoutedEventArgs
const& args) {
215
216 if (i +
1 < model->columns) {
217 double width = headerBorder.ActualWidth();
218 auto point = args.GetCurrentPoint(headerBorder);
219 auto position = point.Position();
220 if (position.
X +
4 >= width) {
221 this->resize = true;
222 this->resizedCol = headerBorder;
223 }
224 }
225 })
226 );
227 headerBorder.PointerReleased(
228 winrt::Microsoft::
UI::Xaml::Input::PointerEventHandler(
229 [=](IInspectable
const& sender, winrt::Microsoft::
UI::Xaml::Input::PointerRoutedEventArgs
const& args) {
230 this->resize = false;
231 })
232 );
233 headerBorder.PointerMoved(
234 winrt::Microsoft::
UI::Xaml::Input::PointerEventHandler(
235 [=](IInspectable
const& sender, winrt::Microsoft::
UI::Xaml::Input::PointerRoutedEventArgs
const& args) {
236 if (this->resize) {
237 auto point = args.GetCurrentPoint(this->resizedCol);
238 auto position = point.Position();
239 if (position.
X >
1) {
240 this->resizedCol.Width(position.
X);
241 }
242 }
243 })
244 );
245 headerBorder.PointerEntered(
246 winrt::Microsoft::
UI::Xaml::Input::PointerEventHandler(
247 [=](IInspectable
const& sender, winrt::Microsoft::
UI::Xaml::Input::PointerRoutedEventArgs
const& args) {
248
249 })
250 );
251 headerBorder.PointerExited(
252 winrt::Microsoft::
UI::Xaml::Input::PointerEventHandler(
253 [=](IInspectable
const& sender, winrt::Microsoft::
UI::Xaml::Input::PointerRoutedEventArgs
const& args) {
254
255 })
256 );
257
258
259
260
261 headerBorder.Child(hLabel);
262
263 grid.SetColumn(headerBorder, i);
264 grid.SetRow(headerBorder,
0);
265 grid.Children().Append(headerBorder);
266
267 UiTableColumn h;
268 h.header = headerBorder;
269 header.push_back(h);
270 }
271
272 maxrows =
1;
273 }
274
275 static ULONG64 getsystime() {
276 SYSTEMTIME st;
277 GetSystemTime(&st);
278 return st.wYear *
10000000000000 +
279 st.wMonth *
100000000000 +
280 st.wDay *
1000000000 +
281 st.wHour *
10000000 +
282 st.wMinute *
100000 +
283 st.wSecond *
1000 +
284 st.wMilliseconds;
285 }
286
287 void UiTable::update(UiList* list,
int i) {
288 if (getvalue == nullptr) {
289 return;
290 }
291
292 Thickness b1 = {
1,
1,
0,
1 };
293 Thickness b2 = {
0,
1,
0,
1 };
294 Thickness b3 = {
0,
1,
1,
1 };
295
296 GridLength gl;
297 gl.Value =
0;
298 gl.GridUnitType = GridUnitType::Auto;
299
300
301 int row =
1;
302 void* elm = list->first(list);
303 while (elm) {
304 if (row >= maxrows) {
305 auto rowdef = RowDefinition();
306 rowdef.Height(gl);
307 grid.RowDefinitions().Append(rowdef);
308 maxrows = row;
309 }
310
311 Thickness cellpadding = {
10,
0,
4,
0 };
312
313
314 int model_col =
0;
315 for (
int col =
0; col < header.size(); col++, model_col++) {
316
317
318 Border cellBorder = Border();
319 cellBorder.Background(defaultBrush);
320 cellBorder.BorderBrush(defaultBrush);
321 if (col ==
0) {
322 cellBorder.BorderThickness(b1);
323 }
324 else if (col +
1 == header.size()) {
325 cellBorder.BorderThickness(b3);
326 }
327 else {
328 cellBorder.BorderThickness(b2);
329 }
330
331
332 if (ondragstart) {
333 cellBorder.CanDrag(true);
334 cellBorder.DragStarting([this](IInspectable
const& sender, DragStartingEventArgs args) {
335 UiDnD dnd;
336 dnd.evttype =
0;
337 dnd.dndstartargs = args;
338 dnd.dndcompletedargs = { nullptr };
339 dnd.drageventargs = { nullptr };
340 dnd.data = args.Data();
341
342 UiEvent evt;
343 evt.obj = this->obj;
344 evt.window = evt.obj->window;
345 evt.document = obj->ctx->document;
346 evt.eventdata = &dnd;
347 evt.intval =
0;
348
349 this->ondragstart(&evt, this->ondragstartdata);
350 });
351 cellBorder.DropCompleted([this](IInspectable
const& sender, DropCompletedEventArgs args) {
352 UiDnD dnd;
353 dnd.evttype =
1;
354 dnd.dndstartargs = { nullptr };
355 dnd.dndcompletedargs = args;
356 dnd.drageventargs = { nullptr };
357 dnd.data = { nullptr };
358
359 UiEvent evt;
360 evt.obj = this->obj;
361 evt.window = evt.obj->window;
362 evt.document = obj->ctx->document;
363 evt.eventdata = &dnd;
364 evt.intval =
0;
365
366 if (this->ondragcomplete) {
367 this->ondragcomplete(&evt, this->ondragcompletedata);
368 }
369 });
370 }
371 if (ondrop) {
372 cellBorder.AllowDrop(true);
373 cellBorder.Drop(DragEventHandler([this](winrt::Windows::Foundation::IInspectable
const& sender, DragEventArgs
const& args){
374 UiDnD dnd;
375 dnd.evttype =
2;
376 dnd.dndstartargs = { nullptr };
377 dnd.dndcompletedargs = { nullptr };
378 dnd.drageventargs = args;
379 dnd.dataview = args.DataView();
380
381 UiEvent evt;
382 evt.obj = this->obj;
383 evt.window = evt.obj->window;
384 evt.document = obj->ctx->document;
385 evt.eventdata = &dnd;
386 evt.intval =
0;
387
388 this->ondrop(&evt, this->ondropdata);
389 }));
390 cellBorder.DragOver(DragEventHandler([this](winrt::Windows::Foundation::IInspectable
const& sender, DragEventArgs
const& args){
391 args.AcceptedOperation(winrt::Windows::ApplicationModel::DataTransfer::DataPackageOperation::Copy);
392 }));
393 }
394
395
396
397 UiModelType type = model->types[col];
398 switch (type) {
399 case UI_STRING_FREE:
400 case UI_STRING: {
401 TextBlock cell = TextBlock();
402 cell.Padding(cellpadding);
403 cell.VerticalAlignment(VerticalAlignment::Stretch);
404 char *val = (
char*)getvalue(elm, model_col);
405 textblock_set_str(cell, val);
406 cellBorder.Child(cell);
407 if (type ==
UI_STRING_FREE && val) {
408 free(val);
409 }
410
411 break;
412 }
413 case UI_INTEGER: {
414 TextBlock cell = TextBlock();
415 cell.Padding(cellpadding);
416 cell.VerticalAlignment(VerticalAlignment::Stretch);
417 int *value = (
int*)getvalue(elm, model_col);
418 if (value) {
419 textblock_set_int(cell, *value);
420 }
421 cellBorder.Child(cell);
422 break;
423 }
424 case UI_ICON: {
425 UiIcon* iconConstr = (UiIcon*)getvalue(elm, model_col);
426 if (iconConstr) {
427 IconElement icon = iconConstr->getIcon();
428 cellBorder.Child(icon);
429 }
430 break;
431 }
432 case UI_ICON_TEXT_FREE:
433 case UI_ICON_TEXT: {
434 StackPanel cellPanel = StackPanel();
435 cellPanel.Spacing(
2);
436 cellPanel.Padding(cellpadding);
437 cellPanel.VerticalAlignment(VerticalAlignment::Stretch);
438
439 cellPanel.Orientation(Orientation::Horizontal);
440 UiIcon* iconConstr = (UiIcon*)getvalue(elm, model_col++);
441 char* str = (
char*)getvalue(elm, model_col);
442 if (iconConstr) {
443 IconElement icon = iconConstr->getIcon();
444 cellPanel.Children().Append(icon);
445 }
446 TextBlock cell = TextBlock();
447 textblock_set_str(cell, str);
448 cellPanel.Children().Append(cell);
449 cellBorder.Child(cellPanel);
450 if (type ==
UI_ICON_TEXT_FREE && str) {
451 free(str);
452 }
453 break;
454 }
455 }
456
457
458 cellBorder.PointerPressed(
459 winrt::Microsoft::
UI::Xaml::Input::PointerEventHandler(
460 [=](IInspectable
const& sender, winrt::Microsoft::
UI::Xaml::Input::PointerRoutedEventArgs
const& args) {
461 winrt::Windows::System::VirtualKeyModifiers modifiers = args.KeyModifiers();
462 bool update_selection = true;
463
464 if (modifiers == winrt::Windows::System::VirtualKeyModifiers::Control) {
465
466 if (!is_row_selected(row)) {
467 row_background(row, selectedBrush, selectedBorderBrush);
468 selection.push_back(row);
469 }
470 else {
471 row_background(row, highlightBrush, highlightBrush);
472 remove_from_selection(row);
473 }
474 }
475 else if (modifiers == winrt::Windows::System::VirtualKeyModifiers::None || selection.size() ==
0) {
476
477 if (selection.size() >
0) {
478 change_rows_bg(selection, defaultBrush, defaultBrush);
479 }
480
481 row_background(row, selectedBrush, selectedBorderBrush);
482 selection = { row };
483 if (modifiers == winrt::Windows::System::VirtualKeyModifiers::None) {
484 SYSTEMTIME st;
485 GetSystemTime(&st);
486
487 ULONG64 now = getsystime();
488 ULONG64 tdiff = now - lastPointerPress;
489 if (tdiff < ui_double_click_time && onactivate != nullptr) {
490
491 update_selection = false;
492 lastPointerPress =
0;
493
494 int selectedrow = row -
1;
495
496 UiListSelection selection;
497 selection.count =
1;
498 selection.rows = &selectedrow;
499
500 UiEvent evt;
501 evt.obj = obj;
502 evt.window = obj->window;
503 evt.document = obj->ctx->document;
504 evt.eventdata = &selection;
505 evt.intval = selectedrow;
506 onactivate(&evt, onactivatedata);
507 }
508 else {
509 lastPointerPress = now;
510 }
511 }
512 }
513 else if (modifiers == winrt::Windows::System::VirtualKeyModifiers::Shift) {
514
515 std::sort(selection.begin(), selection.end());
516 int first = selection.front();
517 int last = row;
518 if (first > row) {
519 last = first;
520 first = row;
521 }
522
523
524 change_rows_bg(selection, defaultBrush, defaultBrush);
525
526
527 std::vector<
int> newselection;
528 for (
int s = first; s <= last; s++) {
529 newselection.push_back(s);
530 }
531 selection = newselection;
532 change_rows_bg(selection, selectedBrush, selectedBorderBrush);
533 }
534
535 if (update_selection) {
536 call_handler(onselection, onselectiondata);
537 }
538 })
539 );
540 cellBorder.PointerReleased(
541 winrt::Microsoft::
UI::Xaml::Input::PointerEventHandler(
542 [=](IInspectable
const& sender, winrt::Microsoft::
UI::Xaml::Input::PointerRoutedEventArgs
const& args) {
543
544 })
545 );
546 cellBorder.PointerEntered(
547 winrt::Microsoft::
UI::Xaml::Input::PointerEventHandler(
548 [=](IInspectable
const& sender, winrt::Microsoft::
UI::Xaml::Input::PointerRoutedEventArgs
const& args) {
549 if (!is_row_selected(row)) {
550 row_background(row, highlightBrush, highlightBrush);
551 }
552 })
553 );
554 cellBorder.PointerExited(
555 winrt::Microsoft::
UI::Xaml::Input::PointerEventHandler(
556 [=](IInspectable
const& sender, winrt::Microsoft::
UI::Xaml::Input::PointerRoutedEventArgs
const& args) {
557 if (!is_row_selected(row)) {
558 row_background(row, defaultBrush, defaultBrush);
559 }
560 })
561 );
562
563 grid.SetColumn(cellBorder, col);
564 grid.SetRow(cellBorder, row);
565 grid.Children().Append(cellBorder);
566 }
567
568 row++;
569 elm = list->next(list);
570 }
571 }
572
573 void UiTable::clear() {
574 for (
int i = grid.Children().Size()-
1; i >=
0; i--) {
575 FrameworkElement elm = grid.Children().GetAt(i).as<FrameworkElement>();
576 int child_row = grid.GetRow(elm);
577 if (child_row >
0) {
578 grid.Children().RemoveAt(i);
579 }
580 }
581
582
583 }
584
585 void UiTable::row_background(
int row, winrt::Microsoft::
UI::Xaml::Media::Brush brush, winrt::Microsoft::
UI::Xaml::Media::Brush borderBrush) {
586 Thickness b1 = {
1,
1,
0,
1 };
587 Thickness b2 = {
0,
1,
0,
1 };
588 Thickness b3 = {
0,
1,
1,
1 };
589
590 for (
auto child : grid.Children()) {
591 FrameworkElement elm = child.as<FrameworkElement>();
592 int child_row = grid.GetRow(elm);
593 if (child_row == row) {
594 Border b = elm.as<Border>();
595 b.Background(brush);
596 b.BorderBrush(borderBrush);
597 }
598 }
599 }
600
601 void UiTable::change_rows_bg(std::vector<
int> rows, winrt::Microsoft::
UI::Xaml::Media::Brush brush, winrt::Microsoft::
UI::Xaml::Media::Brush borderBrush) {
602 std::for_each(rows.cbegin(), rows.cend(), [&](
const int& row) {row_background(row, brush, borderBrush); });
603 }
604
605 bool UiTable::is_row_selected(
int row) {
606 return std::find(selection.begin(), selection.end(), row) != selection.end() ? true : false;
607 }
608
609 void UiTable::remove_from_selection(
int row) {
610 selection.erase(std::remove(selection.begin(), selection.end(), row), selection.end());
611 selection.shrink_to_fit();
612 }
613
614 UiListSelection UiTable::uiselection() {
615 std::sort(selection.begin(), selection.end());
616
617 UiListSelection selobj;
618 selobj.count = selection.size();
619 selobj.rows = nullptr;
620 if (selobj.count >
0) {
621 selobj.rows = (
int*)calloc(selobj.count,
sizeof(
int));
622 memcpy(selobj.rows, selection.data(), selobj.count *
sizeof(
int));
623 for (
int i =
0; i < selobj.count; i++) {
624 selobj.rows[i]--;
625 }
626 }
627 return selobj;
628 }
629
630 void UiTable::call_handler(ui_callback cb,
void* cbdata) {
631 if (!cb) {
632 return;
633 }
634
635 UiListSelection selobj = uiselection();
636
637 UiEvent evt;
638 evt.obj = obj;
639 evt.window = obj->window;
640 evt.document = obj->ctx->document;
641 evt.eventdata = &selobj;
642 evt.intval =
0;
643 cb(&evt, cbdata);
644
645 if (selobj.rows) {
646 free(selobj.rows);
647 }
648 }
649