|
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 "table.h" |
|
30 #include "container.h" |
|
31 #include "util.h" |
|
32 |
|
33 #include "../common/context.h" |
|
34 #include "../common/object.h" |
|
35 |
|
36 #include <winrt/Microsoft.UI.Xaml.Data.h> |
|
37 #include <winrt/Microsoft.UI.Xaml.Media.h> |
|
38 #include <winrt/Microsoft.UI.Xaml.Input.h> |
|
39 |
|
40 using namespace winrt; |
|
41 using namespace Microsoft::UI::Xaml; |
|
42 using namespace Microsoft::UI::Xaml::Controls; |
|
43 using namespace Windows::UI::Xaml::Interop; |
|
44 using namespace winrt::Windows::Foundation; |
|
45 using namespace winrt::Microsoft::UI::Xaml::Controls::Primitives; |
|
46 using namespace winrt::Microsoft::UI::Xaml::Media; |
|
47 using namespace winrt::Windows::UI::Xaml::Input; |
|
48 |
|
49 extern "C" void reg_table_destructor(UiContext * ctx, UiTable * table) { |
|
50 // TODO: |
|
51 } |
|
52 |
|
53 UIEXPORT UIWIDGET ui_table_create(UiObject* obj, UiListArgs args) { |
|
54 if (!args.model) { |
|
55 return nullptr; |
|
56 } |
|
57 |
|
58 UiObject* current = uic_current_obj(obj); |
|
59 |
|
60 // create widgets and wrapper obj |
|
61 ScrollViewer scrollW = ScrollViewer(); |
|
62 Grid grid = Grid(); |
|
63 scrollW.Content(grid); |
|
64 UiTable* uitable = new UiTable(scrollW, grid); |
|
65 reg_table_destructor(current->ctx, uitable); |
|
66 |
|
67 |
|
68 uitable->getvalue = args.model->getvalue ? args.model->getvalue : args.getvalue; |
|
69 |
|
70 // grid styling |
|
71 winrt::Windows::UI::Color bg = { 233, 233, 255, 255 }; // test color |
|
72 SolidColorBrush brush = SolidColorBrush(bg); |
|
73 grid.Background(brush); |
|
74 |
|
75 // add columns from args.model |
|
76 uitable->add_header(args.model); |
|
77 |
|
78 // bind var |
|
79 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST); |
|
80 if (var) { |
|
81 UiList* list = (UiList*)var->value; |
|
82 list->update = ui_table_update; |
|
83 list->obj = uitable; |
|
84 uitable->update(list, 0); |
|
85 } |
|
86 |
|
87 // create toolkit wrapper object and register destructor |
|
88 UIElement elm = scrollW; |
|
89 UiWidget* widget = new UiWidget(elm); |
|
90 ui_context_add_widget_destructor(current->ctx, widget); |
|
91 |
|
92 // add scrollW to current container |
|
93 UI_APPLY_LAYOUT1(current, args); |
|
94 |
|
95 current->container->Add(scrollW, false); |
|
96 |
|
97 return widget; |
|
98 } |
|
99 |
|
100 extern "C" void ui_table_update(UiList * list, int i) { |
|
101 UiTable* table = (UiTable*)list->obj; |
|
102 table->clear(); |
|
103 table->update(list, i); |
|
104 } |
|
105 |
|
106 UiTable::UiTable(winrt::Microsoft::UI::Xaml::Controls::ScrollViewer scrollW, winrt::Microsoft::UI::Xaml::Controls::Grid grid) { |
|
107 this->scrollw = scrollw; |
|
108 this->grid = grid; |
|
109 |
|
110 winrt::Windows::UI::Color highlightBg = { 120, 120, 255, 255 }; // test color |
|
111 highlightBrush = SolidColorBrush(highlightBg); |
|
112 |
|
113 winrt::Windows::UI::Color defaultBg = { 0, 0, 0, 0 }; // default |
|
114 defaultBrush = SolidColorBrush(defaultBg); |
|
115 |
|
116 winrt::Windows::UI::Color selectedBg = { 255, 120, 120, 255 }; // test color |
|
117 selectedBrush = SolidColorBrush(selectedBg); |
|
118 |
|
119 winrt::Windows::UI::Color selectedFg = { 255, 20, 20, 255 }; // test color |
|
120 selectedBorderBrush = SolidColorBrush(selectedFg); |
|
121 } |
|
122 |
|
123 void UiTable::add_header(UiModel* model) { |
|
124 GridLength gl; |
|
125 gl.Value = 0; |
|
126 gl.GridUnitType = GridUnitType::Auto; |
|
127 |
|
128 // add header row definition |
|
129 auto headerRowDef = RowDefinition(); |
|
130 headerRowDef.Height(gl); |
|
131 grid.RowDefinitions().Append(headerRowDef); |
|
132 |
|
133 |
|
134 for (int i = 0; i < model->columns;i++) { |
|
135 char* title = model->titles[i]; |
|
136 UiModelType type = model->types[i]; |
|
137 |
|
138 // add grid column definition |
|
139 auto colDef = ColumnDefinition(); |
|
140 colDef.Width(gl); |
|
141 grid.ColumnDefinitions().Append(colDef); |
|
142 |
|
143 // add button |
|
144 auto button = Button(); |
|
145 wchar_t* wlabel = str2wstr(title, nullptr); |
|
146 button.Content(box_value(wlabel)); |
|
147 free(wlabel); |
|
148 |
|
149 // some styling for the button |
|
150 Thickness border = { 0,0,1,0 }; |
|
151 CornerRadius corner = { 0,0,0,0 }; |
|
152 button.BorderThickness(border); |
|
153 button.CornerRadius(corner); |
|
154 |
|
155 grid.SetColumn(button, i); |
|
156 grid.SetRow(button, 0); |
|
157 grid.Children().Append(button); |
|
158 |
|
159 UiTableColumn h; |
|
160 h.header = button; |
|
161 header.push_back(h); |
|
162 } |
|
163 |
|
164 maxrows = 1; |
|
165 } |
|
166 |
|
167 void UiTable::update(UiList* list, int i) { |
|
168 if (getvalue == nullptr) { |
|
169 return; |
|
170 } |
|
171 |
|
172 Thickness b1 = { 1, 1, 0, 1 }; // first col |
|
173 Thickness b2 = { 0, 1, 0, 1 }; // middle |
|
174 Thickness b3 = { 0, 1, 1, 1 }; // last col |
|
175 |
|
176 GridLength gl; |
|
177 gl.Value = 0; |
|
178 gl.GridUnitType = GridUnitType::Auto; |
|
179 |
|
180 int row = 1; |
|
181 void* elm = list->first(list); |
|
182 while (elm) { |
|
183 if (row >= maxrows) { |
|
184 auto rowdef = RowDefinition(); |
|
185 rowdef.Height(gl); |
|
186 grid.RowDefinitions().Append(rowdef); |
|
187 maxrows = row; |
|
188 } |
|
189 |
|
190 for (int col = 0; col < header.size(); col++) { |
|
191 Border cellBorder = Border(); |
|
192 cellBorder.Background(defaultBrush); |
|
193 TextBlock cell = TextBlock(); |
|
194 cellBorder.Child(cell); |
|
195 cellBorder.BorderBrush(defaultBrush); |
|
196 if (col == 0) { |
|
197 cellBorder.BorderThickness(b1); |
|
198 } |
|
199 else if (col + 1 == header.size()) { |
|
200 cellBorder.BorderThickness(b3); |
|
201 } |
|
202 else { |
|
203 cellBorder.BorderThickness(b2); |
|
204 } |
|
205 |
|
206 char* value = (char*)getvalue(elm, col); |
|
207 if (value) { |
|
208 wchar_t* wstr = str2wstr(value, nullptr); |
|
209 cell.Text(winrt::hstring(wstr)); |
|
210 free(wstr); |
|
211 } |
|
212 Thickness padding = { 10,0,4,0 }; |
|
213 cell.Padding(padding); |
|
214 cell.VerticalAlignment(VerticalAlignment::Stretch); |
|
215 |
|
216 // event handler |
|
217 cellBorder.PointerPressed( |
|
218 winrt::Microsoft::UI::Xaml::Input::PointerEventHandler( |
|
219 [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) { |
|
220 if (selection > 0) { |
|
221 row_background(selection, defaultBrush, defaultBrush); |
|
222 } |
|
223 row_background(row, selectedBrush, selectedBorderBrush); |
|
224 selection = row; |
|
225 }) |
|
226 ); |
|
227 cellBorder.PointerReleased( |
|
228 winrt::Microsoft::UI::Xaml::Input::PointerEventHandler( |
|
229 [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) { |
|
230 |
|
231 }) |
|
232 ); |
|
233 cellBorder.PointerEntered( |
|
234 winrt::Microsoft::UI::Xaml::Input::PointerEventHandler( |
|
235 [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) { |
|
236 if (selection != row) { |
|
237 row_background(row, highlightBrush, highlightBrush); |
|
238 } |
|
239 }) |
|
240 ); |
|
241 cellBorder.PointerExited( |
|
242 winrt::Microsoft::UI::Xaml::Input::PointerEventHandler( |
|
243 [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) { |
|
244 if (selection != row) { |
|
245 row_background(row, defaultBrush, defaultBrush); |
|
246 } |
|
247 }) |
|
248 ); |
|
249 |
|
250 grid.SetColumn(cellBorder, col); |
|
251 grid.SetRow(cellBorder, row); |
|
252 grid.Children().Append(cellBorder); |
|
253 } |
|
254 |
|
255 row++; |
|
256 elm = list->next(list); |
|
257 } |
|
258 } |
|
259 |
|
260 void UiTable::clear() { |
|
261 for (int i = grid.Children().Size()-1; i >= 0; i--) { |
|
262 FrameworkElement elm = grid.Children().GetAt(i).as<FrameworkElement>(); |
|
263 int child_row = grid.GetRow(elm); |
|
264 if (child_row > 0) { |
|
265 grid.Children().RemoveAt(i); |
|
266 } |
|
267 } |
|
268 } |
|
269 |
|
270 void UiTable::row_background(int row, winrt::Microsoft::UI::Xaml::Media::Brush brush, winrt::Microsoft::UI::Xaml::Media::Brush borderBrush) { |
|
271 Thickness b1 = { 1, 1, 0, 1 }; // first col |
|
272 Thickness b2 = { 0, 1, 0, 1 }; // middle |
|
273 Thickness b3 = { 0, 1, 1, 1 }; // last col |
|
274 |
|
275 for (auto child : grid.Children()) { |
|
276 FrameworkElement elm = child.as<FrameworkElement>(); |
|
277 int child_row = grid.GetRow(elm); |
|
278 if (child_row == row) { |
|
279 Border b = elm.as<Border>(); |
|
280 b.Background(brush); |
|
281 b.BorderBrush(borderBrush); |
|
282 } |
|
283 } |
|
284 } |