|
1 /* |
|
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
|
3 * |
|
4 * Copyright 2023 Olaf Wintermann. All rights reserved. |
|
5 * |
|
6 * Redistribution and use in source and binary forms, with or without |
|
7 * modification, are permitted provided that the following conditions are met: |
|
8 * |
|
9 * 1. Redistributions of source code must retain the above copyright |
|
10 * notice, this list of conditions and the following disclaimer. |
|
11 * |
|
12 * 2. Redistributions in binary form must reproduce the above copyright |
|
13 * notice, this list of conditions and the following disclaimer in the |
|
14 * documentation and/or other materials provided with the distribution. |
|
15 * |
|
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
26 * POSSIBILITY OF SUCH DAMAGE. |
|
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 // TODO: |
|
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, L"%d", 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 // create widgets and wrapper obj |
|
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 // grid styling |
|
102 winrt::Windows::UI::Color bg = { 255, 255, 255, 255 }; // test color |
|
103 SolidColorBrush brush = SolidColorBrush(bg); |
|
104 grid.Background(brush); |
|
105 |
|
106 // add columns from args.model |
|
107 uitable->add_header(args.model); |
|
108 |
|
109 // bind var |
|
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 // create toolkit wrapper object and register destructor |
|
120 UIElement elm = scrollW; |
|
121 UiWidget* widget = new UiWidget(elm); |
|
122 ui_context_add_widget_destructor(current->ctx, widget); |
|
123 |
|
124 // add scrollW to current container |
|
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 }; // default |
|
153 defaultBrush = SolidColorBrush(defaultBg); |
|
154 |
|
155 winrt::Windows::UI::Color selectedBg = { 255, 204, 232, 255 }; // test color |
|
156 selectedBrush = SolidColorBrush(selectedBg); |
|
157 |
|
158 winrt::Windows::UI::Color selectedFg = { 255, 0, 90, 158 }; // test color |
|
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 // key event for hanling the table cursor or enter |
|
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 // add header row definition |
|
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 // add grid column definition |
|
194 auto colDef = ColumnDefinition(); |
|
195 colDef.Width(gl); |
|
196 grid.ColumnDefinitions().Append(colDef); |
|
197 |
|
198 // header column border |
|
199 Border headerBorder = Border(); |
|
200 Thickness border = { 0,0,1,0 }; |
|
201 headerBorder.BorderThickness(border); |
|
202 headerBorder.BorderBrush(borderBrush); |
|
203 |
|
204 // add text |
|
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 // event handler for highlighting and column resizing |
|
212 headerBorder.PointerPressed( |
|
213 winrt::Microsoft::UI::Xaml::Input::PointerEventHandler( |
|
214 [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) { |
|
215 // the last column doesn't need resize capabilities |
|
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 // TODO: background |
|
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 // TODO: background |
|
255 }) |
|
256 ); |
|
257 |
|
258 |
|
259 |
|
260 // add controls |
|
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 }; // first col |
|
293 Thickness b2 = { 0, 1, 0, 1 }; // middle |
|
294 Thickness b3 = { 0, 1, 1, 1 }; // last col |
|
295 |
|
296 GridLength gl; |
|
297 gl.Value = 0; |
|
298 gl.GridUnitType = GridUnitType::Auto; |
|
299 |
|
300 // iterate model |
|
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 // model column, usually the same as col, however UI_ICON_TEXT uses two columns in the model |
|
314 int model_col = 0; |
|
315 for (int col = 0; col < header.size(); col++, model_col++) { |
|
316 // create ui elements with the correct cell border |
|
317 // dependeing on the column |
|
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 // dnd |
|
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 // set the cell value |
|
396 // depending on the type, we create different cell controls |
|
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 // event handler |
|
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 // add/remove current row |
|
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 // no modifier or shift is pressed but there is no selection |
|
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 // two pointer presse events in short time and we have an onactivate handler |
|
491 update_selection = false; // we don't want an additional selection event |
|
492 lastPointerPress = 0; // reset double-click |
|
493 |
|
494 int selectedrow = row - 1; // subtract header row |
|
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 // select everything between the first selection and the current row |
|
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 // clear previous selection |
|
524 change_rows_bg(selection, defaultBrush, defaultBrush); |
|
525 |
|
526 // create new selection |
|
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 // TODO: should we clean row definitions? |
|
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 }; // first col |
|
587 Thickness b2 = { 0, 1, 0, 1 }; // middle |
|
588 Thickness b3 = { 0, 1, 1, 1 }; // last col |
|
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 } |