@@ -2519,6 +2519,299 @@ void DisplayServerWindows::enable_for_stealing_focus(OS::ProcessID pid) {
2519
2519
AllowSetForegroundWindow (pid);
2520
2520
}
2521
2521
2522
+ static HRESULT CALLBACK win32_task_dialog_callback (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LONG_PTR lpRefData) {
2523
+ if (msg == TDN_CREATED) {
2524
+ // To match the input text dialog.
2525
+ SendMessageW (hwnd, WM_SETICON, ICON_BIG, 0 );
2526
+ SendMessageW (hwnd, WM_SETICON, ICON_SMALL, 0 );
2527
+ }
2528
+
2529
+ return 0 ;
2530
+ }
2531
+
2532
+ Error DisplayServerWindows::dialog_show (String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) {
2533
+ _THREAD_SAFE_METHOD_
2534
+
2535
+ TASKDIALOGCONFIG config;
2536
+ ZeroMemory (&config, sizeof (TASKDIALOGCONFIG));
2537
+ config.cbSize = sizeof (TASKDIALOGCONFIG);
2538
+
2539
+ Char16String title = p_title.utf16 ();
2540
+ Char16String message = p_description.utf16 ();
2541
+ List<Char16String> buttons;
2542
+ for (String s : p_buttons) {
2543
+ buttons.push_back (s.utf16 ());
2544
+ }
2545
+
2546
+ config.pszWindowTitle = (LPCWSTR)(title.get_data ());
2547
+ config.pszContent = (LPCWSTR)(message.get_data ());
2548
+
2549
+ const int button_count = MIN (buttons.size (), 8 );
2550
+ config.cButtons = button_count;
2551
+
2552
+ // No dynamic stack array size :(
2553
+ TASKDIALOG_BUTTON *tbuttons = button_count != 0 ? (TASKDIALOG_BUTTON *)alloca (sizeof (TASKDIALOG_BUTTON) * button_count) : nullptr ;
2554
+ if (tbuttons) {
2555
+ for (int i = 0 ; i < button_count; i++) {
2556
+ tbuttons[i].nButtonID = i;
2557
+ tbuttons[i].pszButtonText = (LPCWSTR)(buttons[i].get_data ());
2558
+ }
2559
+ }
2560
+ config.pButtons = tbuttons;
2561
+ config.pfCallback = win32_task_dialog_callback;
2562
+
2563
+ HMODULE comctl = LoadLibraryW (L" comctl32.dll" );
2564
+ if (comctl) {
2565
+ typedef HRESULT (WINAPI * TaskDialogIndirectPtr)(const TASKDIALOGCONFIG *pTaskConfig, int *pnButton, int *pnRadioButton, BOOL *pfVerificationFlagChecked);
2566
+
2567
+ TaskDialogIndirectPtr task_dialog_indirect = (TaskDialogIndirectPtr)GetProcAddress (comctl, " TaskDialogIndirect" );
2568
+ if (task_dialog_indirect) {
2569
+ int button_pressed;
2570
+ if (FAILED (task_dialog_indirect (&config, &button_pressed, nullptr , nullptr ))) {
2571
+ return FAILED;
2572
+ }
2573
+
2574
+ if (!p_callback.is_null ()) {
2575
+ Variant button = button_pressed;
2576
+ const Variant *args[1 ] = { &button };
2577
+ Variant ret;
2578
+ Callable::CallError ce;
2579
+ p_callback.callp (args, 1 , ret, ce);
2580
+ if (ce.error != Callable::CallError::CALL_OK) {
2581
+ ERR_PRINT (vformat (" Failed to execute dialog callback: %s." , Variant::get_callable_error_text (p_callback, args, 1 , ce)));
2582
+ }
2583
+ }
2584
+
2585
+ return OK;
2586
+ }
2587
+ FreeLibrary (comctl);
2588
+ }
2589
+
2590
+ ERR_PRINT (" Unable to create native dialog." );
2591
+ return FAILED;
2592
+ }
2593
+
2594
+ struct Win32InputTextDialogInit {
2595
+ const char16_t *title;
2596
+ const char16_t *description;
2597
+ const char16_t *partial;
2598
+ const Callable &callback;
2599
+ };
2600
+
2601
+ static constexpr int scale_with_dpi (int p_pos, int p_dpi) {
2602
+ return IsProcessDPIAware () ? (p_pos * p_dpi / 96 ) : p_pos;
2603
+ }
2604
+
2605
+ static INT_PTR input_text_dialog_init (HWND hWnd, UINT code, WPARAM wParam, LPARAM lParam) {
2606
+ Win32InputTextDialogInit init = *(Win32InputTextDialogInit *)lParam;
2607
+ SetWindowLongPtrW (hWnd, GWLP_USERDATA, (LONG_PTR)&init.callback ); // Set dialog callback.
2608
+
2609
+ SetWindowTextW (hWnd, (LPCWSTR)init.title );
2610
+
2611
+ const int dpi = DisplayServerWindows::get_singleton ()->screen_get_dpi ();
2612
+
2613
+ const int margin = scale_with_dpi (7 , dpi);
2614
+ const SIZE dlg_size = { scale_with_dpi (300 , dpi), scale_with_dpi (50 , dpi) };
2615
+
2616
+ int str_len = lstrlenW ((LPCWSTR)init.description );
2617
+ SIZE str_size = { dlg_size.cx , 0 };
2618
+ if (str_len > 0 ) {
2619
+ HDC hdc = GetDC (nullptr );
2620
+ RECT trect = { margin, margin, margin + dlg_size.cx , margin + dlg_size.cy };
2621
+ SelectObject (hdc, (HFONT)SendMessageW (hWnd, WM_GETFONT, 0 , 0 ));
2622
+
2623
+ // `+ margin` adds some space between the static text and the edit field.
2624
+ // Don't scale this with DPI because DPI is already handled by DrawText.
2625
+ str_size.cy = DrawTextW (hdc, (LPCWSTR)init.description , str_len, &trect, DT_LEFT | DT_WORDBREAK | DT_CALCRECT) + margin;
2626
+
2627
+ ReleaseDC (nullptr , hdc);
2628
+ }
2629
+
2630
+ RECT crect, wrect;
2631
+ GetClientRect (hWnd, &crect);
2632
+ GetWindowRect (hWnd, &wrect);
2633
+ int sw = GetSystemMetrics (SM_CXSCREEN);
2634
+ int sh = GetSystemMetrics (SM_CYSCREEN);
2635
+ int new_width = dlg_size.cx + margin * 2 + wrect.right - wrect.left - crect.right ;
2636
+ int new_height = dlg_size.cy + margin * 2 + wrect.bottom - wrect.top - crect.bottom + str_size.cy ;
2637
+
2638
+ MoveWindow (hWnd, (sw - new_width) / 2 , (sh - new_height) / 2 , new_width, new_height, true );
2639
+
2640
+ HWND ok_button = GetDlgItem (hWnd, 1 );
2641
+ MoveWindow (ok_button,
2642
+ dlg_size.cx + margin - scale_with_dpi (65 , dpi),
2643
+ dlg_size.cy + str_size.cy + margin - scale_with_dpi (20 , dpi),
2644
+ scale_with_dpi (65 , dpi), scale_with_dpi (20 , dpi), true );
2645
+
2646
+ HWND description = GetDlgItem (hWnd, 3 );
2647
+ MoveWindow (description, margin, margin, dlg_size.cx , str_size.cy , true );
2648
+ SetWindowTextW (description, (LPCWSTR)init.description );
2649
+
2650
+ HWND text_edit = GetDlgItem (hWnd, 2 );
2651
+ MoveWindow (text_edit, margin, str_size.cy + margin, dlg_size.cx , scale_with_dpi (20 , dpi), true );
2652
+ SetWindowTextW (text_edit, (LPCWSTR)init.partial );
2653
+
2654
+ return TRUE ;
2655
+ }
2656
+
2657
+ static INT_PTR input_text_dialog_cmd_proc (HWND hWnd, UINT code, WPARAM wParam, LPARAM lParam) {
2658
+ if (LOWORD (wParam) == 1 ) {
2659
+ HWND text_edit = GetDlgItem (hWnd, 2 );
2660
+ ERR_FAIL_NULL_V (text_edit, false );
2661
+
2662
+ Char16String text;
2663
+ text.resize (GetWindowTextLengthW (text_edit) + 1 );
2664
+ GetWindowTextW (text_edit, (LPWSTR)text.get_data (), text.size ());
2665
+
2666
+ const Callable *callback = (const Callable *)GetWindowLongPtrW (hWnd, GWLP_USERDATA);
2667
+ if (callback && callback->is_valid ()) {
2668
+ Variant v_result = String ((const wchar_t *)text.get_data ());
2669
+ Variant ret;
2670
+ Callable::CallError ce;
2671
+ const Variant *args[1 ] = { &v_result };
2672
+
2673
+ callback->callp (args, 1 , ret, ce);
2674
+ if (ce.error != Callable::CallError::CALL_OK) {
2675
+ ERR_PRINT (vformat (" Failed to execute input dialog callback: %s." , Variant::get_callable_error_text (*callback, args, 1 , ce)));
2676
+ }
2677
+ }
2678
+
2679
+ return EndDialog (hWnd, 0 );
2680
+ }
2681
+
2682
+ return false ;
2683
+ }
2684
+
2685
+ static INT_PTR CALLBACK input_text_dialog_proc (HWND hWnd, UINT code, WPARAM wParam, LPARAM lParam) {
2686
+ switch (code) {
2687
+ case WM_INITDIALOG:
2688
+ return input_text_dialog_init (hWnd, code, wParam, lParam);
2689
+
2690
+ case WM_COMMAND:
2691
+ return input_text_dialog_cmd_proc (hWnd, code, wParam, lParam);
2692
+
2693
+ default :
2694
+ return FALSE ;
2695
+ }
2696
+ }
2697
+
2698
+ Error DisplayServerWindows::dialog_input_text (String p_title, String p_description, String p_partial, const Callable &p_callback) {
2699
+ #pragma pack(push, 1)
2700
+
2701
+ // NOTE: Use default/placeholder coordinates here. Windows uses its own coordinate system
2702
+ // specifically for dialogs which relies on font sizes instead of pixels.
2703
+ const struct {
2704
+ WORD dlgVer; // must be 1
2705
+ WORD signature; // must be 0xFFFF
2706
+ DWORD helpID;
2707
+ DWORD exStyle;
2708
+ DWORD style;
2709
+ WORD cDlgItems;
2710
+ short x;
2711
+ short y;
2712
+ short cx;
2713
+ short cy;
2714
+ WCHAR menu[1 ]; // must be 0
2715
+ WCHAR windowClass[7 ]; // must be "#32770" -- the default window class for dialogs
2716
+ WCHAR title[1 ]; // must be 0
2717
+ WORD pointsize;
2718
+ WORD weight;
2719
+ BYTE italic ;
2720
+ BYTE charset;
2721
+ WCHAR font[13 ]; // must be "MS Shell Dlg"
2722
+ } template_base = {
2723
+ 1 , 0xFFFF , 0 , 0 ,
2724
+ DS_SYSMODAL | DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU,
2725
+ 3 , 0 , 0 , 20 , 20 , L" " , L" #32770" , L" " , 8 , FW_NORMAL, 0 , DEFAULT_CHARSET, L" MS Shell Dlg"
2726
+ };
2727
+
2728
+ const struct {
2729
+ DWORD helpID;
2730
+ DWORD exStyle;
2731
+ DWORD style;
2732
+ short x;
2733
+ short y;
2734
+ short cx;
2735
+ short cy;
2736
+ DWORD id;
2737
+ WCHAR windowClass[7 ]; // must be "Button"
2738
+ WCHAR title[3 ]; // must be "OK"
2739
+ WORD extraCount;
2740
+ } ok_button = {
2741
+ 0 , 0 , WS_VISIBLE | BS_DEFPUSHBUTTON, 0 , 0 , 50 , 14 , 1 , WC_BUTTONW, L" OK" , 0
2742
+ };
2743
+ const struct {
2744
+ DWORD helpID;
2745
+ DWORD exStyle;
2746
+ DWORD style;
2747
+ short x;
2748
+ short y;
2749
+ short cx;
2750
+ short cy;
2751
+ DWORD id;
2752
+ WCHAR windowClass[5 ]; // must be "Edit"
2753
+ WCHAR title[1 ]; // must be 0
2754
+ WORD extraCount;
2755
+ } text_field = {
2756
+ 0 , 0 , WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, 0 , 0 , 250 , 14 , 2 , WC_EDITW, L" " , 0
2757
+ };
2758
+ const struct {
2759
+ DWORD helpID;
2760
+ DWORD exStyle;
2761
+ DWORD style;
2762
+ short x;
2763
+ short y;
2764
+ short cx;
2765
+ short cy;
2766
+ DWORD id;
2767
+ WCHAR windowClass[7 ]; // must be "Static"
2768
+ WCHAR title[1 ]; // must be 0
2769
+ WORD extraCount;
2770
+ } static_text = {
2771
+ 0 , 0 , WS_VISIBLE, 0 , 0 , 250 , 14 , 3 , WC_STATICW, L" " , 0
2772
+ };
2773
+
2774
+ #pragma pack(pop)
2775
+
2776
+ // Dialog template
2777
+ const size_t data_size = sizeof (template_base) + (sizeof (template_base) % 4 ) +
2778
+ sizeof (ok_button) + (sizeof (ok_button) % 4 ) +
2779
+ sizeof (text_field) + (sizeof (text_field) % 4 ) +
2780
+ sizeof (static_text) + (sizeof (static_text) % 4 );
2781
+
2782
+ void *data_template = memalloc (data_size);
2783
+ ERR_FAIL_NULL_V_MSG (data_template, FAILED, " Unable to allocate memory for the dialog template." );
2784
+ ZeroMemory (data_template, data_size);
2785
+
2786
+ char *current_block = (char *)data_template;
2787
+ CopyMemory (current_block, &template_base, sizeof (template_base));
2788
+ current_block += sizeof (template_base) + (sizeof (template_base) % 4 );
2789
+ CopyMemory (current_block, &ok_button, sizeof (ok_button));
2790
+ current_block += sizeof (ok_button) + (sizeof (ok_button) % 4 );
2791
+ CopyMemory (current_block, &text_field, sizeof (text_field));
2792
+ current_block += sizeof (text_field) + (sizeof (text_field) % 4 );
2793
+ CopyMemory (current_block, &static_text, sizeof (static_text));
2794
+
2795
+ Char16String title16 = p_title.utf16 ();
2796
+ Char16String description16 = p_description.utf16 ();
2797
+ Char16String partial16 = p_partial.utf16 ();
2798
+
2799
+ Win32InputTextDialogInit init = {
2800
+ title16.get_data (), description16.get_data (), partial16.get_data (), p_callback
2801
+ };
2802
+
2803
+ // No modal dialogs for specific windows? Assume main window here.
2804
+ INT_PTR ret = DialogBoxIndirectParamW (hInstance, (LPDLGTEMPLATEW)data_template, nullptr , (DLGPROC)input_text_dialog_proc, (LPARAM)(&init));
2805
+
2806
+ Error result = ret != -1 ? OK : FAILED;
2807
+ memfree (data_template);
2808
+
2809
+ if (result == FAILED) {
2810
+ ERR_PRINT (" Unable to create native dialog." );
2811
+ }
2812
+ return result;
2813
+ }
2814
+
2522
2815
int DisplayServerWindows::keyboard_get_layout_count () const {
2523
2816
return GetKeyboardLayoutList (0 , nullptr );
2524
2817
}
@@ -5285,6 +5578,23 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
5285
5578
}
5286
5579
}
5287
5580
5581
+ HMODULE comctl32 = LoadLibraryW (L" comctl32.dll" );
5582
+ if (comctl32) {
5583
+ typedef BOOL (WINAPI * InitCommonControlsExPtr)(_In_ const INITCOMMONCONTROLSEX *picce);
5584
+ InitCommonControlsExPtr init_common_controls_ex = (InitCommonControlsExPtr)GetProcAddress (comctl32, " InitCommonControlsEx" );
5585
+
5586
+ // Fails if the incorrect version was loaded. Probably not a big enough deal to print an error about.
5587
+ if (init_common_controls_ex) {
5588
+ INITCOMMONCONTROLSEX icc = {};
5589
+ icc.dwICC = ICC_STANDARD_CLASSES;
5590
+ icc.dwSize = sizeof (INITCOMMONCONTROLSEX);
5591
+ if (!init_common_controls_ex (&icc)) {
5592
+ WARN_PRINT (" Unable to initialize Windows common controls. Native dialogs may not work properly." );
5593
+ }
5594
+ }
5595
+ FreeLibrary (comctl32);
5596
+ }
5597
+
5288
5598
memset (&wc, 0 , sizeof (WNDCLASSEXW));
5289
5599
wc.cbSize = sizeof (WNDCLASSEXW);
5290
5600
wc.style = CS_OWNDC | CS_DBLCLKS;
0 commit comments