|
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 |
|
32 #include "window.h" |
|
33 |
|
34 #include "appmenu.h" |
|
35 #include "commandbar.h" |
|
36 #include "container.h" |
|
37 #include "util.h" |
|
38 #include "button.h" |
|
39 |
|
40 #include "../common/context.h" |
|
41 #include "../common/object.h" |
|
42 |
|
43 #include <stdlib.h> |
|
44 |
|
45 #include <cx/mempool.h> |
|
46 |
|
47 #include "MainWindow.xaml.h" |
|
48 |
|
49 |
|
50 #include <Windows.h> |
|
51 #include <shobjidl.h> |
|
52 #include <iostream> |
|
53 |
|
54 using namespace winrt; |
|
55 using namespace Microsoft::UI::Xaml; |
|
56 using namespace Microsoft::UI::Xaml::Controls; |
|
57 using namespace Microsoft::UI::Xaml::Controls::Primitives; |
|
58 using namespace Microsoft::UI::Xaml::XamlTypeInfo; |
|
59 using namespace Microsoft::UI::Xaml::Markup; |
|
60 using namespace Windows::UI::Xaml::Interop; |
|
61 using namespace winrt::Windows::Foundation; |
|
62 using namespace winrt::Windows::Storage::Pickers; |
|
63 |
|
64 UiWindow::UiWindow(winrt::Microsoft::UI::Xaml::Window& win) : window(win) {} |
|
65 |
|
66 UiObject* ui_window(const char* title, void* window_data) { |
|
67 UiObject* obj = ui_simple_window(title, window_data); |
|
68 |
|
69 /* |
|
70 if (uic_get_menu_list()) { |
|
71 // create/add menubar |
|
72 MenuBar mb = ui_create_menubar(obj); |
|
73 mb.VerticalAlignment(VerticalAlignment::Top); |
|
74 obj->container->Add(mb, false); |
|
75 } |
|
76 */ |
|
77 |
|
78 if (uic_toolbar_isenabled()) { |
|
79 // create a grid for the toolbar: ColumnDefinitions="Auto, *, Auto" |
|
80 Grid toolbar_grid = Grid(); |
|
81 GridLength gl; |
|
82 gl.Value = 0; |
|
83 gl.GridUnitType = GridUnitType::Auto; |
|
84 |
|
85 ColumnDefinition coldef0 = ColumnDefinition(); |
|
86 coldef0.Width(gl); |
|
87 toolbar_grid.ColumnDefinitions().Append(coldef0); |
|
88 |
|
89 gl.Value = 1; |
|
90 gl.GridUnitType = GridUnitType::Star; |
|
91 ColumnDefinition coldef1 = ColumnDefinition(); |
|
92 coldef1.Width(gl); |
|
93 toolbar_grid.ColumnDefinitions().Append(coldef1); |
|
94 |
|
95 gl.Value = 0; |
|
96 gl.GridUnitType = GridUnitType::Auto; |
|
97 ColumnDefinition coldef2 = ColumnDefinition(); |
|
98 coldef2.Width(gl); |
|
99 toolbar_grid.ColumnDefinitions().Append(coldef2); |
|
100 |
|
101 // rowdef |
|
102 gl.Value = 0; |
|
103 gl.GridUnitType = GridUnitType::Auto; |
|
104 RowDefinition rowdef = RowDefinition(); |
|
105 rowdef.Height(gl); |
|
106 toolbar_grid.RowDefinitions().Append(rowdef); |
|
107 |
|
108 |
|
109 // create commandbar |
|
110 CxList* def_l = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT); |
|
111 CxList* def_c = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER); |
|
112 CxList* def_r = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT); |
|
113 |
|
114 bool addappmenu = true; |
|
115 if (cxListSize(def_r) > 0) { |
|
116 CommandBar toolbar_r = ui_create_toolbar(obj, def_r, addappmenu); |
|
117 toolbar_grid.SetColumn(toolbar_r, 2); |
|
118 toolbar_grid.SetRow(toolbar_r, 0); |
|
119 toolbar_grid.Children().Append(toolbar_r); |
|
120 addappmenu = false; |
|
121 } |
|
122 if (cxListSize(def_c) > 0) { |
|
123 CommandBar toolbar_c = ui_create_toolbar(obj, def_c, addappmenu); |
|
124 toolbar_c.HorizontalAlignment(HorizontalAlignment::Center); |
|
125 toolbar_grid.SetColumn(toolbar_c, 1); |
|
126 toolbar_grid.SetRow(toolbar_c, 0); |
|
127 toolbar_grid.Children().Append(toolbar_c); |
|
128 addappmenu = false; |
|
129 } |
|
130 if (cxListSize(def_l) > 0) { |
|
131 CommandBar toolbar_l = ui_create_toolbar(obj, def_l, addappmenu); |
|
132 toolbar_grid.SetColumn(toolbar_l, 0); |
|
133 toolbar_grid.SetRow(toolbar_l, 0); |
|
134 toolbar_grid.Children().Append(toolbar_l); |
|
135 } |
|
136 |
|
137 toolbar_grid.VerticalAlignment(VerticalAlignment::Top); |
|
138 obj->container->Add(toolbar_grid, false); |
|
139 } |
|
140 |
|
141 return obj; |
|
142 } |
|
143 |
|
144 UIEXPORT UiObject* ui_simple_window(const char *title, void *window_data) { |
|
145 CxMempool* mp = cxBasicMempoolCreate(256); |
|
146 UiObject* obj = (UiObject*)cxCalloc(mp->allocator, 1, sizeof(UiObject)); |
|
147 |
|
148 obj->ctx = uic_context(obj, mp); |
|
149 obj->window = window_data; |
|
150 |
|
151 Window window = Window(); |
|
152 //Window window = make<winui::implementation::MainWindow>(); |
|
153 |
|
154 winrt::Windows::Foundation::Uri resourceLocator{ L"ms-appx:///MainWindow.xaml" }; |
|
155 Application::LoadComponent(window, resourceLocator, ComponentResourceLocation::Nested); |
|
156 |
|
157 window.ExtendsContentIntoTitleBar(true); |
|
158 |
|
159 Grid grid = Grid(); |
|
160 window.Content(grid); |
|
161 |
|
162 StackPanel titleBar = StackPanel(); |
|
163 Thickness titleBarPadding = { 10, 5, 5, 10 }; |
|
164 titleBar.Padding(titleBarPadding); |
|
165 titleBar.Orientation(Orientation::Horizontal); |
|
166 TextBlock titleLabel = TextBlock(); |
|
167 titleBar.Children().Append(titleLabel); |
|
168 |
|
169 if (title) { |
|
170 wchar_t* wtitle = str2wstr(title, nullptr); |
|
171 window.Title(wtitle); |
|
172 titleLabel.Text(hstring(wtitle)); |
|
173 free(wtitle); |
|
174 } |
|
175 |
|
176 window.SetTitleBar(titleBar); |
|
177 |
|
178 obj->wobj = new UiWindow(window); |
|
179 ui_context_add_window_destructor(obj->ctx, obj->wobj); |
|
180 |
|
181 window.Closed([obj](IInspectable const& sender, WindowEventArgs) { |
|
182 if (obj->ctx->close_callback) { |
|
183 UiEvent evt; |
|
184 evt.obj = obj; |
|
185 evt.document = obj->ctx->document; |
|
186 evt.window = obj->window; |
|
187 evt.eventdata = NULL; |
|
188 evt.intval = 0; |
|
189 obj->ctx->close_callback(&evt, obj->ctx->close_data); |
|
190 } else { |
|
191 ui_context_destroy(obj->ctx); |
|
192 } |
|
193 }); |
|
194 |
|
195 obj->container = new UiBoxContainer(grid, UI_BOX_CONTAINER_VBOX, 0, 0); |
|
196 |
|
197 titleBar.VerticalAlignment(VerticalAlignment::Top); |
|
198 obj->container->Add(titleBar, false); |
|
199 |
|
200 obj->window = window_data; |
|
201 |
|
202 return obj; |
|
203 } |
|
204 |
|
205 static void dialog_button_add_callback(ContentDialog dialog, Button button, int num, UiObject *obj, ui_callback onclick, void *onclickdata) { |
|
206 button.Click([dialog, num, obj, onclick, onclickdata](IInspectable const& sender, RoutedEventArgs) { |
|
207 if (onclick) { |
|
208 UiEvent evt; |
|
209 evt.obj = obj; |
|
210 evt.window = obj->window; |
|
211 evt.document = obj->ctx->document; |
|
212 evt.eventdata = nullptr; |
|
213 evt.intval = num; |
|
214 onclick(&evt, onclickdata); |
|
215 } |
|
216 dialog.Hide(); |
|
217 }); |
|
218 } |
|
219 |
|
220 UIEXPORT UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args) { |
|
221 UiWindow *window = parent->wobj; |
|
222 if (!window) { |
|
223 return NULL; |
|
224 } |
|
225 |
|
226 CxMempool* mp = cxBasicMempoolCreate(256); |
|
227 UiObject* obj = (UiObject*)cxCalloc(mp->allocator, 1, sizeof(UiObject)); |
|
228 |
|
229 obj->ctx = uic_context(obj, mp); |
|
230 |
|
231 ContentDialog dialog = ContentDialog(); |
|
232 UIElement elm = dialog; |
|
233 UiWidget* widget = new UiWidget(elm); |
|
234 ui_context_add_widget_destructor(obj->ctx, widget); |
|
235 obj->widget = widget; |
|
236 |
|
237 if (args.title) { |
|
238 wchar_t* wtitle = str2wstr(args.title, nullptr); |
|
239 dialog.Title(box_value(wtitle)); |
|
240 free(wtitle); |
|
241 } |
|
242 |
|
243 |
|
244 Grid dialogContent = Grid(); |
|
245 GridLength gl; |
|
246 |
|
247 // content row |
|
248 gl.Value = 1; |
|
249 gl.GridUnitType = GridUnitType::Star; |
|
250 RowDefinition rowdef0 = RowDefinition(); |
|
251 rowdef0.Height(gl); |
|
252 dialogContent.RowDefinitions().Append(rowdef0); |
|
253 |
|
254 // button row |
|
255 gl.Value = 0; |
|
256 gl.GridUnitType = GridUnitType::Auto; |
|
257 RowDefinition rowdef1 = RowDefinition(); |
|
258 rowdef1.Height(gl); |
|
259 dialogContent.RowDefinitions().Append(rowdef1); |
|
260 |
|
261 // coldef |
|
262 gl.Value = 1; |
|
263 gl.GridUnitType = GridUnitType::Star; |
|
264 ColumnDefinition coldef = ColumnDefinition(); |
|
265 coldef.Width(gl); |
|
266 dialogContent.ColumnDefinitions().Append(coldef); |
|
267 |
|
268 // content |
|
269 Grid grid = Grid(); |
|
270 grid.SetRow(grid, 0); |
|
271 grid.SetColumn(grid, 0); |
|
272 dialogContent.Children().Append(grid); |
|
273 obj->container = new UiBoxContainer(grid, UI_BOX_CONTAINER_VBOX, 0, 0); |
|
274 |
|
275 // buttons |
|
276 Grid buttons = Grid(); |
|
277 Thickness btnsMargin = { (double)0, (double)10, (double)0, (double)0 }; |
|
278 buttons.Margin(btnsMargin); |
|
279 |
|
280 RowDefinition btnrowdef = RowDefinition(); |
|
281 gl.Value = 0; |
|
282 gl.GridUnitType = GridUnitType::Auto; |
|
283 btnrowdef.Height(gl); |
|
284 buttons.RowDefinitions().Append(btnrowdef); |
|
285 |
|
286 gl.Value = 1; |
|
287 gl.GridUnitType = GridUnitType::Auto; |
|
288 int c = 0; |
|
289 if (args.lbutton1) { |
|
290 ColumnDefinition bcoldef = ColumnDefinition(); |
|
291 bcoldef.Width(gl); |
|
292 buttons.ColumnDefinitions().Append(bcoldef); |
|
293 |
|
294 Button btn = Button(); |
|
295 ui_set_button_label(btn, args.lbutton1, NULL, NULL, UI_LABEL_TEXT); |
|
296 Thickness margin = { (double)5, (double)5, (double)5, (double)5 }; |
|
297 btn.Margin(margin); |
|
298 btn.HorizontalAlignment(HorizontalAlignment::Stretch); |
|
299 dialog_button_add_callback(dialog, btn, 1, obj, args.onclick, args.onclickdata); |
|
300 |
|
301 buttons.SetRow(btn, 0); |
|
302 buttons.SetColumn(btn, c++); |
|
303 buttons.Children().Append(btn); |
|
304 } |
|
305 if (args.lbutton2) { |
|
306 ColumnDefinition bcoldef = ColumnDefinition(); |
|
307 bcoldef.Width(gl); |
|
308 buttons.ColumnDefinitions().Append(bcoldef); |
|
309 |
|
310 Button btn = Button(); |
|
311 ui_set_button_label(btn, args.lbutton2, NULL, NULL, UI_LABEL_TEXT); |
|
312 Thickness margin = { (double)5, (double)5, (double)5, (double)5 }; |
|
313 btn.Margin(margin); |
|
314 btn.HorizontalAlignment(HorizontalAlignment::Stretch); |
|
315 dialog_button_add_callback(dialog, btn, 2, obj, args.onclick, args.onclickdata); |
|
316 |
|
317 buttons.SetRow(btn, 0); |
|
318 buttons.SetColumn(btn, c++); |
|
319 buttons.Children().Append(btn); |
|
320 } |
|
321 if (args.rbutton3) { |
|
322 ColumnDefinition bcoldef = ColumnDefinition(); |
|
323 bcoldef.Width(gl); |
|
324 buttons.ColumnDefinitions().Append(bcoldef); |
|
325 |
|
326 Button btn = Button(); |
|
327 ui_set_button_label(btn, args.rbutton3, NULL, NULL, UI_LABEL_TEXT); |
|
328 Thickness margin = { (double)5, (double)5, (double)5, (double)5 }; |
|
329 btn.Margin(margin); |
|
330 btn.HorizontalAlignment(HorizontalAlignment::Stretch); |
|
331 dialog_button_add_callback(dialog, btn, 3, obj, args.onclick, args.onclickdata); |
|
332 |
|
333 buttons.SetRow(btn, 0); |
|
334 buttons.SetColumn(btn, c++); |
|
335 buttons.Children().Append(btn); |
|
336 } |
|
337 if (args.rbutton4) { |
|
338 ColumnDefinition bcoldef = ColumnDefinition(); |
|
339 bcoldef.Width(gl); |
|
340 buttons.ColumnDefinitions().Append(bcoldef); |
|
341 |
|
342 Button btn = Button(); |
|
343 ui_set_button_label(btn, args.rbutton4, NULL, NULL, UI_LABEL_TEXT); |
|
344 Thickness margin = { (double)5, (double)5, (double)5, (double)5 }; |
|
345 btn.Margin(margin); |
|
346 btn.HorizontalAlignment(HorizontalAlignment::Stretch); |
|
347 dialog_button_add_callback(dialog, btn, 4, obj, args.onclick, args.onclickdata); |
|
348 |
|
349 buttons.SetRow(btn, 0); |
|
350 buttons.SetColumn(btn, c++); |
|
351 buttons.Children().Append(btn); |
|
352 } |
|
353 |
|
354 dialogContent.SetRow(buttons, 1); |
|
355 dialogContent.SetColumn(buttons, 0); |
|
356 dialogContent.Children().Append(buttons); |
|
357 |
|
358 |
|
359 dialog.Content(dialogContent); |
|
360 dialog.XamlRoot(window->window.Content().XamlRoot()); |
|
361 |
|
362 obj->widget->Show = [dialog]() { |
|
363 dialog.ShowAsync(); |
|
364 }; |
|
365 |
|
366 return obj; |
|
367 } |
|
368 |
|
369 void ui_window_size(UiObject *obj, int width, int height) { |
|
370 UIWINDOW win = obj->wobj; |
|
371 if (win) { |
|
372 winrt::Windows::Graphics::SizeInt32 wsize; |
|
373 wsize.Width = width; |
|
374 wsize.Height = height; |
|
375 win->window.AppWindow().Resize(wsize); |
|
376 } |
|
377 } |
|
378 |
|
379 |
|
380 |
|
381 |
|
382 static Windows::Foundation::IAsyncAction create_dialog_async(UiObject *obj, UiDialogArgs args) { |
|
383 UiObject* current = uic_current_obj(obj); |
|
384 Window parentWindow = current->wobj->window; |
|
385 |
|
386 ContentDialog dialog = ContentDialog(); |
|
387 dialog.XamlRoot(parentWindow.Content().XamlRoot()); |
|
388 |
|
389 if (args.title) { |
|
390 wchar_t *str = str2wstr(args.title, nullptr); |
|
391 dialog.Title(winrt::box_value(str)); |
|
392 free(str); |
|
393 } |
|
394 |
|
395 TextBox textfield{ nullptr }; |
|
396 PasswordBox password{ nullptr }; |
|
397 if(args.input || args.password) { |
|
398 StackPanel panel = StackPanel(); |
|
399 panel.Orientation(Orientation::Vertical); |
|
400 if (args.content) { |
|
401 wchar_t *str = str2wstr(args.content, nullptr); |
|
402 TextBlock label = TextBlock(); |
|
403 label.Text(str); |
|
404 panel.Children().Append(label); |
|
405 free(str); |
|
406 } |
|
407 |
|
408 Thickness margin = { 0, 5, 0, 0 }; |
|
409 if (args.password) { |
|
410 password = PasswordBox(); |
|
411 password.Margin(margin); |
|
412 panel.Children().Append(password); |
|
413 } else { |
|
414 textfield = TextBox(); |
|
415 textfield.Margin(margin); |
|
416 panel.Children().Append(textfield); |
|
417 } |
|
418 |
|
419 panel.Margin(margin); |
|
420 |
|
421 dialog.Content(panel); |
|
422 |
|
423 } else { |
|
424 if (args.content) { |
|
425 wchar_t *str = str2wstr(args.content, nullptr); |
|
426 dialog.Content(winrt::box_value(str)); |
|
427 free(str); |
|
428 } |
|
429 } |
|
430 |
|
431 if (args.button1_label) { |
|
432 wchar_t *str = str2wstr(args.button1_label, nullptr); |
|
433 dialog.PrimaryButtonText(winrt::hstring(str)); |
|
434 free(str); |
|
435 dialog.DefaultButton(ContentDialogButton::Primary); |
|
436 } |
|
437 if (args.button2_label) { |
|
438 wchar_t *str = str2wstr(args.button2_label, nullptr); |
|
439 dialog.SecondaryButtonText(winrt::hstring(str)); |
|
440 free(str); |
|
441 } |
|
442 if (args.closebutton_label) { |
|
443 wchar_t *str = str2wstr(args.closebutton_label, nullptr); |
|
444 dialog.CloseButtonText(winrt::hstring(str)); |
|
445 free(str); |
|
446 } |
|
447 |
|
448 ContentDialogResult result = co_await dialog.ShowAsync(); |
|
449 |
|
450 if (args.result) { |
|
451 UiEvent evt; |
|
452 evt.obj = current; |
|
453 evt.document = current->ctx->document; |
|
454 evt.window = current->window; |
|
455 evt.eventdata = NULL; |
|
456 evt.intval = 0; |
|
457 if (result == ContentDialogResult::Primary) { |
|
458 evt.intval = 1; |
|
459 } else if (result == ContentDialogResult::Secondary) { |
|
460 evt.intval = 2; |
|
461 } |
|
462 |
|
463 if (args.password) { |
|
464 std::wstring wstr(password.Password()); |
|
465 char *text = wchar2utf8(wstr.c_str(), wstr.length()); |
|
466 evt.eventdata = text; |
|
467 } else if (args.input) { |
|
468 std::wstring wstr(textfield.Text()); |
|
469 char *text = wchar2utf8(wstr.c_str(), wstr.length()); |
|
470 evt.eventdata = text; |
|
471 } |
|
472 |
|
473 args.result(&evt, args.resultdata); |
|
474 |
|
475 if (evt.eventdata) { |
|
476 free(evt.eventdata); |
|
477 } |
|
478 } |
|
479 } |
|
480 |
|
481 UIEXPORT void ui_dialog_create(UiObject *obj, UiDialogArgs args) { |
|
482 create_dialog_async(obj, args); |
|
483 } |
|
484 |
|
485 |
|
486 |
|
487 // --------------------------------------- File Dialog --------------------------------------- |
|
488 |
|
489 static void filedialog_callback( |
|
490 UiObject *obj, |
|
491 ui_callback file_selected_callback, |
|
492 void *cbdata, |
|
493 winrt::Windows::Foundation::Collections::IVectorView<winrt::Windows::Storage::StorageFile> result) |
|
494 { |
|
495 UiFileList flist; |
|
496 flist.nfiles = result.Size(); |
|
497 flist.files = new char*[flist.nfiles]; |
|
498 |
|
499 int i = 0; |
|
500 for (auto const& file : result) { |
|
501 winrt::hstring path = file.Path(); |
|
502 flist.files[i++] = wchar2utf8(path.c_str(), path.size()); |
|
503 } |
|
504 |
|
505 UiEvent evt; |
|
506 evt.obj = obj; |
|
507 evt.document = obj->ctx->document; |
|
508 evt.window = obj->window; |
|
509 evt.eventdata = &flist; |
|
510 evt.intval = 0; |
|
511 file_selected_callback(&evt, cbdata); |
|
512 |
|
513 for (int i = 0; i < flist.nfiles;i++) { |
|
514 free(flist.files[i]); |
|
515 } |
|
516 delete[] flist.files; |
|
517 } |
|
518 |
|
519 static Windows::Foundation::IAsyncAction open_filedialog_async(UiObject *obj, unsigned int mode, ui_callback file_selected_callback, void *cbdata) { |
|
520 FileOpenPicker openFileDialog = FileOpenPicker(); |
|
521 auto initializeWithWindow { openFileDialog.as<::IInitializeWithWindow>() |
|
522 }; |
|
523 |
|
524 HWND hwnd{ nullptr }; |
|
525 winrt::check_hresult(obj->wobj->window.as<IWindowNative>()->get_WindowHandle(&hwnd)); |
|
526 |
|
527 initializeWithWindow->Initialize(hwnd); |
|
528 |
|
529 openFileDialog.FileTypeFilter().Append(L"*"); |
|
530 |
|
531 if ((mode & UI_FILEDIALOG_SELECT_MULTI) == UI_FILEDIALOG_SELECT_MULTI) { |
|
532 auto files = co_await openFileDialog.PickMultipleFilesAsync(); |
|
533 filedialog_callback(obj, file_selected_callback, cbdata, files); |
|
534 } else { |
|
535 auto file = co_await openFileDialog.PickSingleFileAsync(); |
|
536 auto files = single_threaded_vector<winrt::Windows::Storage::StorageFile>(); |
|
537 files.Append(file); |
|
538 filedialog_callback(obj, file_selected_callback, cbdata, files.GetView()); |
|
539 } |
|
540 } |
|
541 |
|
542 static Windows::Foundation::IAsyncAction save_filedialog_async(UiObject *obj, char *name, ui_callback file_selected_callback, void *cbdata) { |
|
543 IFileSaveDialog *saveFileDialog; |
|
544 |
|
545 HRESULT hr = CoCreateInstance(CLSID_FileSaveDialog, NULL, CLSCTX_ALL, IID_IFileSaveDialog, reinterpret_cast<void**>(&saveFileDialog)); |
|
546 if (FAILED(hr)) |
|
547 { |
|
548 co_return; |
|
549 } |
|
550 |
|
551 if (name) { |
|
552 wchar_t *wname = str2wstr(name, NULL); |
|
553 saveFileDialog->SetFileName(wname); |
|
554 free(wname); |
|
555 free(name); |
|
556 } |
|
557 |
|
558 |
|
559 hr = saveFileDialog->Show(NULL); |
|
560 if (SUCCEEDED(hr)) { |
|
561 IShellItem *item; |
|
562 hr = saveFileDialog->GetResult(&item); |
|
563 if (SUCCEEDED(hr)) { |
|
564 PWSTR wpath; |
|
565 hr = item->GetDisplayName(SIGDN_FILESYSPATH, &wpath); |
|
566 |
|
567 if (SUCCEEDED(hr)) { |
|
568 char *path = wchar2utf8(wpath, lstrlen(wpath)); |
|
569 CoTaskMemFree(wpath); |
|
570 |
|
571 UiFileList flist; |
|
572 flist.nfiles = 1; |
|
573 flist.files = new char*[1]; |
|
574 flist.files[0] = path; |
|
575 |
|
576 UiEvent evt; |
|
577 evt.obj = obj; |
|
578 evt.document = obj->ctx->document; |
|
579 evt.window = obj->window; |
|
580 evt.eventdata = &flist; |
|
581 evt.intval = 0; |
|
582 file_selected_callback(&evt, cbdata); |
|
583 |
|
584 free(path); |
|
585 delete[] flist.files; |
|
586 } |
|
587 item->Release(); |
|
588 } |
|
589 } |
|
590 |
|
591 // cleanup |
|
592 saveFileDialog->Release(); |
|
593 } |
|
594 |
|
595 static Windows::Foundation::IAsyncAction folderdialog_async(UiObject *obj, ui_callback file_selected_callback, void *cbdata) { |
|
596 FolderPicker folderPicker = FolderPicker(); |
|
597 auto initializeWithWindow { folderPicker.as<::IInitializeWithWindow>() |
|
598 }; |
|
599 |
|
600 HWND hwnd{ nullptr }; |
|
601 winrt::check_hresult(obj->wobj->window.as<IWindowNative>()->get_WindowHandle(&hwnd)); |
|
602 |
|
603 initializeWithWindow->Initialize(hwnd); |
|
604 |
|
605 folderPicker.FileTypeFilter().Append(L"*"); |
|
606 |
|
607 auto folder = co_await folderPicker.PickSingleFolderAsync(); |
|
608 if (folder) { |
|
609 winrt::hstring hpath = folder.Path(); |
|
610 char *cpath = wchar2utf8(hpath.c_str(), hpath.size()); |
|
611 |
|
612 UiFileList flist; |
|
613 flist.nfiles = 1; |
|
614 flist.files = &cpath; |
|
615 |
|
616 UiEvent evt; |
|
617 evt.obj = obj; |
|
618 evt.document = obj->ctx->document; |
|
619 evt.window = obj->window; |
|
620 evt.eventdata = &flist; |
|
621 evt.intval = 0; |
|
622 file_selected_callback(&evt, cbdata); |
|
623 } |
|
624 } |
|
625 |
|
626 UIEXPORT void ui_openfiledialog(UiObject *obj, unsigned int mode, ui_callback file_selected_callback, void *cbdata) { |
|
627 if ((mode & UI_FILEDIALOG_SELECT_FOLDER) == UI_FILEDIALOG_SELECT_FOLDER) { |
|
628 folderdialog_async(obj, file_selected_callback, cbdata); |
|
629 } else { |
|
630 open_filedialog_async(obj, mode, file_selected_callback, cbdata); |
|
631 } |
|
632 } |
|
633 |
|
634 UIEXPORT void ui_savefiledialog(UiObject *obj, const char *name, ui_callback file_selected_callback, void *cbdata) { |
|
635 char *n = name ? _strdup(name) : NULL; |
|
636 save_filedialog_async(obj, n, file_selected_callback, cbdata); |
|
637 } |