From: Fabian Maurer Subject: [v4 4/5] comctl32: TaskDialog - Implement main and content text controls Message-Id: <20170315201323.29623-4-dark.shadow4@web.de> Date: Wed, 15 Mar 2017 21:13:22 +0100 In-Reply-To: <20170315201323.29623-1-dark.shadow4@web.de> References: <20170315201323.29623-1-dark.shadow4@web.de> v2: Don't crash if text is an integer resource v3: Rewrite to implement Nikolay Sivov's suggestions v4: Rewrite to fix a bunch of issues and make patchset smaller The dialog also now autosizes to fit the text. Signed-off-by: Fabian Maurer --- dlls/comctl32/taskdialog.c | 139 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 3 deletions(-) diff --git a/dlls/comctl32/taskdialog.c b/dlls/comctl32/taskdialog.c index f080164c20..72e72d5d34 100644 --- a/dlls/comctl32/taskdialog.c +++ b/dlls/comctl32/taskdialog.c @@ -19,11 +19,18 @@ * */ +/* Fonts according to MSDN UI Guidelines: + * Main Instructions - Segoe UI, blue (#003399), 12pt + * Secondary Instructions - Segoe UI, black (#000000), 9pt + * Normal Text - Segoe UI, black (#000000), 9pt + */ + #include #include #include "windef.h" #include "winbase.h" +#include "wingdi.h" #include "winuser.h" #include "commctrl.h" #include "winerror.h" @@ -37,6 +44,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(taskdlg); /* Roughly fitting TaskDialog size */ static const UINT DIALOG_DEFAULT_WIDTH = 180; +static const UINT DIALOG_SPACING = 5; + +#define ID_START 0xF000 + +static const int ID_TEXTMAIN = ID_START + 1; +static const int ID_TEXTCONTENT = ID_START + 2; + typedef struct { DLGITEMTEMPLATE template; @@ -61,8 +75,15 @@ typedef struct UINT title_size; /* Length in bytes including null-terminator */ } dialog_header; +typedef struct +{ + HWND hwnd; + HFONT font_default, font_main; +} taskdialog_info; + #define MEMCPY_MOVEPTR(target, source, size) memcpy(target, source, size); target = (void *)((char *)target + size); #define ALIGN_POINTER(ptr, boundary) ptr = (void *)(((UINT_PTR)(ptr) + (boundary)) & ~(boundary)) +#define STR_EMPTY(str) (str == NULL || str[0] == 0) static void pixels_to_dialogunits(LONG *width, LONG *height) { @@ -98,6 +119,22 @@ static void get_desktop_size(RECT *desktop, HWND hwndWindow) pixels_to_dialogunits(&desktop->right, &desktop->bottom); } +/* used to calculate size for the static controls */ +static RECT text_get_rect(HDC hdc, const WCHAR *text, UINT dialog_width) +{ + RECT rect = {0, 0, dialog_width - DIALOG_SPACING * 2, 0}; /* padding left and right of the control */ + + dialogunits_to_pixels(&rect.right, NULL); + + DrawTextW(hdc, text, -1, &rect, DT_LEFT | DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK); + + pixels_to_dialogunits(&rect.right, &rect.bottom); + + rect.bottom += DIALOG_SPACING; + + return rect; +} + /* Functions for turning our dialog structures into a usable dialog template * We don't load the dialog template from a resource, we instead create it in memory * This way we can easily handle variable control numbers */ @@ -176,12 +213,45 @@ static void controls_add(struct list *controls, WORD id, const WCHAR *class, con list_add_tail(controls, &data->entry); } +/* DialogProc and helper functions */ + +static BOOL CALLBACK SetFont(HWND window, LPARAM font_default) +{ + SendMessageW(window, WM_SETFONT, font_default, TRUE); + return TRUE; +} + static INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { + static const WCHAR taskdialog_info_propnameW[] = {'T','a','s','k','D','i','a','l','o','g','I','n','f','o',0}; + taskdialog_info *dialog_info; + TRACE("hwndDlg=%p msg=0x%04x wparam=%lx lparam=%lx\n", hwndDlg, uMsg, wParam, lParam); + if (uMsg != WM_INITDIALOG && uMsg != WM_NCDESTROY) + dialog_info = GetPropW(hwndDlg, taskdialog_info_propnameW); + switch (uMsg) { + case WM_INITDIALOG: + dialog_info = (taskdialog_info *)lParam; + dialog_info->hwnd = hwndDlg; + SetPropW(hwndDlg, taskdialog_info_propnameW, dialog_info); + + EnumChildWindows(hwndDlg, (WNDENUMPROC)SetFont, (LPARAM)dialog_info->font_default); + SendDlgItemMessageW(hwndDlg, ID_TEXTMAIN, WM_SETFONT, (WPARAM)dialog_info->font_main, TRUE); + break; + case WM_CTLCOLORSTATIC: + if((HWND)lParam == GetDlgItem(hwndDlg, ID_TEXTMAIN)) + { + HDC hdc = (HDC)wParam; + + SetTextColor(hdc, RGB(0, 0x33, 0x99)); + SetBkColor(hdc, GetSysColor(COLOR_3DFACE)); + + return (INT_PTR)GetSysColorBrush(COLOR_3DFACE); + } + break; case WM_COMMAND: if (HIWORD(wParam) == BN_CLICKED) { @@ -190,10 +260,38 @@ static INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARA return TRUE; } break; + case WM_DESTROY: + RemovePropW(hwndDlg, taskdialog_info_propnameW); + break; } return FALSE; } +static void taskdialog_info_init(taskdialog_info *dialog_info, const TASKDIALOGCONFIG *task_config, HDC dc_dummy) +{ + NONCLIENTMETRICSW ncm; + int font_size_default; + int font_size_main; + + ncm.cbSize = sizeof(ncm); + SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0); + + /* Convert pt size to font height needed for CreateFontW */ + font_size_default = -MulDiv ( 9, GetDeviceCaps (dc_dummy, LOGPIXELSY), 72); + font_size_main = -MulDiv (12, GetDeviceCaps (dc_dummy, LOGPIXELSY), 72); + + dialog_info->font_default = CreateFontW (font_size_default, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, + 0, 0, CLEARTYPE_QUALITY, FF_DONTCARE, ncm.lfMessageFont.lfFaceName); + dialog_info->font_main = CreateFontW (font_size_main, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, + 0, 0, CLEARTYPE_QUALITY, FF_DONTCARE, ncm.lfMessageFont.lfFaceName); +} + +static void taskdialog_info_destroy(taskdialog_info *dialog_info) +{ + DeleteObject(dialog_info->font_main); + DeleteObject(dialog_info->font_default); +} + /*********************************************************************** * TaskDialogIndirect [COMCTL32.@] */ @@ -208,16 +306,22 @@ HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *pTaskConfig, int *pnBu DLGTEMPLATE *template_data; dialog_header header = {0}; struct list controls; + taskdialog_info dialog_info; + HDC dc_dummy; TRACE("%p, %p, %p, %p\n", pTaskConfig, pnButton, pnRadioButton, pfVerificationFlagChecked); if (!pTaskConfig || pTaskConfig->cbSize != sizeof(TASKDIALOGCONFIG)) return E_INVALIDARG; + dc_dummy = CreateCompatibleDC(NULL); list_init(&controls); + taskdialog_info_init(&dialog_info, pTaskConfig, dc_dummy); + + SelectObject(dc_dummy, dialog_info.font_default); get_desktop_size(&desktop, pTaskConfig->hwndParent); - dialog_height = 100; + dialog_height = DIALOG_SPACING; dialog_width = pTaskConfig->cxWidth; /* Dialog can't be smaller than default size and not bigger than screen @@ -229,7 +333,33 @@ HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *pTaskConfig, int *pnBu /* Start creating controls */ - controls_add(&controls, IDOK, WC_BUTTONW, text_ok, WS_CHILD | WS_VISIBLE, 105, 85, 40, 10); + if (!IS_INTRESOURCE(pTaskConfig->pszContent) && !STR_EMPTY(pTaskConfig->pszMainInstruction)) + { + RECT rect; + + SelectObject(dc_dummy, dialog_info.font_main); + rect = text_get_rect(dc_dummy, pTaskConfig->pszMainInstruction, dialog_width); + + controls_add(&controls, ID_TEXTMAIN, WC_STATICW, pTaskConfig->pszMainInstruction, + WS_CHILD | WS_VISIBLE, DIALOG_SPACING, dialog_height, rect.right, rect.bottom); + + dialog_height += rect.bottom; + SelectObject(dc_dummy, dialog_info.font_default); + } + + if (!IS_INTRESOURCE(pTaskConfig->pszContent) && !STR_EMPTY(pTaskConfig->pszContent)) + { + RECT rect = text_get_rect(dc_dummy, pTaskConfig->pszContent, dialog_width); + + controls_add(&controls, ID_TEXTCONTENT, WC_STATICW, pTaskConfig->pszContent, + WS_CHILD | WS_VISIBLE, DIALOG_SPACING, dialog_height, rect.right, rect.bottom); + + dialog_height += rect.bottom; + } + + controls_add(&controls, IDOK, WC_BUTTONW, text_ok, + WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, dialog_width - 40 - 10, dialog_height, 40, 10); + dialog_height += DIALOG_SPACING * 2; header.title = pTaskConfig->pszWindowTitle; if (!header.title) @@ -248,12 +378,15 @@ HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *pTaskConfig, int *pnBu /* Turn template information into a dialog template to display it */ template_data = dialog_template_create(header, &controls); - DialogBoxIndirectParamW(pTaskConfig->hInstance, template_data, pTaskConfig->hwndParent, DialogProc, 0); + DialogBoxIndirectParamW(pTaskConfig->hInstance, template_data, pTaskConfig->hwndParent, DialogProc, (LPARAM)&dialog_info); /* Cleanup */ Free(template_data); controls_destroy(&controls); + DeleteDC(dc_dummy); + + taskdialog_info_destroy(&dialog_info); return S_OK; } -- 2.12.0