[PATCH v2 3/6] comctl32/taskdialog: Add support for expando button.

Zhiyi Zhang zzhang at codeweavers.com
Wed Jul 18 22:04:15 CDT 2018


Signed-off-by: Zhiyi Zhang <zzhang at codeweavers.com>
---
 dlls/comctl32/comctl32.h   |   4 +
 dlls/comctl32/comctl32.rc  |   6 ++
 dlls/comctl32/taskdialog.c | 163 ++++++++++++++++++++++++++++++++++++-
 3 files changed, 170 insertions(+), 3 deletions(-)

diff --git a/dlls/comctl32/comctl32.h b/dlls/comctl32/comctl32.h
index eef2072e18..67da0e1677 100644
--- a/dlls/comctl32/comctl32.h
+++ b/dlls/comctl32/comctl32.h
@@ -107,6 +107,10 @@ extern HBRUSH  COMCTL32_hPattern55AABrush DECLSPEC_HIDDEN;
 #define IDS_BUTTON_CANCEL 3004
 #define IDS_BUTTON_CLOSE  3005
 
+/* Task dialog expando control default text */
+#define IDS_TD_EXPANDED   3020
+#define IDS_TD_COLLAPSED  3021
+
 #define WM_SYSTIMER     0x0118
 
 enum combobox_state_flags
diff --git a/dlls/comctl32/comctl32.rc b/dlls/comctl32/comctl32.rc
index cea815a347..3f8e148bfa 100644
--- a/dlls/comctl32/comctl32.rc
+++ b/dlls/comctl32/comctl32.rc
@@ -56,6 +56,12 @@ STRINGTABLE
     IDS_BUTTON_CLOSE  "&Close"
 }
 
+STRINGTABLE
+{
+    IDS_TD_EXPANDED   "Hide details"
+    IDS_TD_COLLAPSED  "See details"
+}
+
 IDD_PROPSHEET DIALOG 0, 0, 220, 140
 STYLE DS_CONTEXTHELP | DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE
 CAPTION "Properties for %s"
diff --git a/dlls/comctl32/taskdialog.c b/dlls/comctl32/taskdialog.c
index db278cac46..54ad58b340 100644
--- a/dlls/comctl32/taskdialog.c
+++ b/dlls/comctl32/taskdialog.c
@@ -42,6 +42,8 @@ static const UINT DIALOG_MIN_WIDTH = 240;
 static const UINT DIALOG_SPACING = 5;
 static const UINT DIALOG_BUTTON_WIDTH = 50;
 static const UINT DIALOG_BUTTON_HEIGHT = 14;
+static const UINT DIALOG_EXPANDO_ICON_WIDTH = 10;
+static const UINT DIALOG_EXPANDO_ICON_HEIGHT = 10;
 static const UINT DIALOG_TIMER_MS = 200;
 
 static const UINT ID_TIMER = 1;
@@ -63,6 +65,7 @@ struct taskdialog_info
     HWND *command_links;
     INT command_link_count;
     HWND expanded_info;
+    HWND expando_button;
     HWND *buttons;
     INT button_count;
     HWND default_button;
@@ -76,6 +79,8 @@ struct taskdialog_info
     } m;
     INT selected_radio_id;
     BOOL expanded;
+    WCHAR *expanded_text;
+    WCHAR *collapsed_text;
 };
 
 struct button_layout_info
@@ -85,6 +90,7 @@ struct button_layout_info
 };
 
 static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, HWND hwnd);
+static void taskdialog_layout(struct taskdialog_info *dialog_info);
 
 static void taskdialog_du_to_px(struct taskdialog_info *dialog_info, LONG *width, LONG *height)
 {
@@ -222,11 +228,25 @@ static HRESULT taskdialog_notify(struct taskdialog_info *dialog_info, UINT notif
                : S_OK;
 }
 
+static void taskdialog_toggle_expando_control(struct taskdialog_info *dialog_info)
+{
+    dialog_info->expanded = !dialog_info->expanded;
+    ShowWindow(dialog_info->expanded_info, dialog_info->expanded ? SW_SHOWDEFAULT : SW_HIDE);
+    taskdialog_layout(dialog_info);
+}
+
 static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, HWND hwnd)
 {
     INT command_id = GetWindowLongW(hwnd, GWLP_ID);
     HWND radio_button;
 
+    if (hwnd == dialog_info->expando_button)
+    {
+        taskdialog_toggle_expando_control(dialog_info);
+        taskdialog_notify(dialog_info, TDN_EXPANDO_BUTTON_CLICKED, dialog_info->expanded, 0);
+        return;
+    }
+
     radio_button = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count, command_id);
     if (radio_button)
     {
@@ -357,6 +377,49 @@ static void taskdialog_get_radio_button_size(struct taskdialog_info *dialog_info
     ReleaseDC(hwnd, hdc);
 }
 
+static void taskdialog_get_expando_size(struct taskdialog_info *dialog_info, HWND hwnd, SIZE *size)
+{
+    DWORD style = DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK;
+    HFONT hfont, old_hfont;
+    HDC hdc;
+    RECT rect = {0};
+    LONG icon_width, icon_height, text_offset;
+    LONG max_width, max_text_height;
+
+    hdc = GetDC(hwnd);
+    hfont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0);
+    old_hfont = SelectObject(hdc, hfont);
+
+    icon_width = DIALOG_EXPANDO_ICON_WIDTH;
+    icon_height = DIALOG_EXPANDO_ICON_HEIGHT;
+    taskdialog_du_to_px(dialog_info, &icon_width, &icon_height);
+
+    GetCharWidthW(hdc, '0', '0', &text_offset);
+    text_offset /= 2;
+
+    if (dialog_info->taskconfig->dwFlags & TDF_RTL_LAYOUT)
+        style |= DT_RIGHT | DT_RTLREADING;
+    else
+        style |= DT_LEFT;
+
+    max_width = DIALOG_MIN_WIDTH / 2;
+    taskdialog_du_to_px(dialog_info, &max_width, NULL);
+
+    rect.right = max_width - icon_width - text_offset;
+    max_text_height = DrawTextW(hdc, dialog_info->expanded_text, -1, &rect, style);
+    size->cy = max(max_text_height, icon_height);
+    size->cx = rect.right - rect.left;
+
+    rect.right = max_width - icon_width - text_offset;
+    max_text_height = DrawTextW(hdc, dialog_info->collapsed_text, -1, &rect, style);
+    size->cy = max(size->cy, max_text_height);
+    size->cx = max(size->cx, rect.right - rect.left);
+    size->cx = min(size->cx, max_width);
+
+    if (old_hfont) SelectObject(hdc, old_hfont);
+    ReleaseDC(hwnd, hdc);
+}
+
 static ULONG_PTR taskdialog_get_standard_icon(LPCWSTR icon)
 {
     if (icon == TD_WARNING_ICON)
@@ -544,6 +607,35 @@ static void taskdialog_add_expanded_info(struct taskdialog_info *dialog_info)
     ShowWindow(dialog_info->expanded_info, dialog_info->expanded ? SW_SHOWDEFAULT : SW_HIDE);
 }
 
+static void taskdialog_add_expando_button(struct taskdialog_info *dialog_info)
+{
+    const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+    const WCHAR *textW;
+
+    if (!taskconfig->pszExpandedInformation) return;
+
+    if (!taskconfig->pszCollapsedControlText && !taskconfig->pszExpandedControlText)
+    {
+        dialog_info->expanded_text = taskdialog_gettext(dialog_info, FALSE, MAKEINTRESOURCEW(IDS_TD_EXPANDED));
+        dialog_info->collapsed_text = taskdialog_gettext(dialog_info, FALSE, MAKEINTRESOURCEW(IDS_TD_COLLAPSED));
+    }
+    else
+    {
+        textW = taskconfig->pszExpandedControlText ? taskconfig->pszExpandedControlText
+                                                   : taskconfig->pszCollapsedControlText;
+        dialog_info->expanded_text = taskdialog_gettext(dialog_info, TRUE, textW);
+        textW = taskconfig->pszCollapsedControlText ? taskconfig->pszCollapsedControlText
+                                                    : taskconfig->pszExpandedControlText;
+        dialog_info->collapsed_text = taskdialog_gettext(dialog_info, TRUE, textW);
+    }
+
+    textW = dialog_info->expanded ? dialog_info->expanded_text : dialog_info->collapsed_text;
+
+    dialog_info->expando_button = CreateWindowW(WC_BUTTONW, textW, WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_OWNERDRAW, 0,
+                                                0, 0, 0, dialog_info->hwnd, 0, 0, 0);
+    SendMessageW(dialog_info->expando_button, WM_SETFONT, (WPARAM)dialog_info->font, 0);
+}
+
 static void taskdialog_add_button(struct taskdialog_info *dialog_info, HWND *button, INT_PTR id, const WCHAR *text,
                                   BOOL custom_button)
 {
@@ -622,6 +714,7 @@ static void taskdialog_layout(struct taskdialog_info *dialog_info)
     LONG screen_width, dialog_width, dialog_height = 0;
     LONG h_spacing, v_spacing;
     LONG main_icon_right, main_icon_bottom;
+    LONG expando_right, expando_bottom;
     struct button_layout_info *button_layout_infos;
     LONG button_min_width, button_height;
     LONG *line_widths, line_count, align;
@@ -700,6 +793,19 @@ static void taskdialog_layout(struct taskdialog_info *dialog_info)
 
     dialog_height = max(dialog_height, main_icon_bottom);
 
+    expando_right = 0;
+    expando_bottom = dialog_height;
+    /* Expando control */
+    if (dialog_info->expando_button)
+    {
+        x = h_spacing;
+        y = dialog_height + v_spacing;
+        taskdialog_get_expando_size(dialog_info, dialog_info->expando_button, &size);
+        SetWindowPos(dialog_info->expando_button, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+        expando_right = x + size.cx;
+        expando_bottom = y + size.cy;
+    }
+
     /* Common and custom buttons */
     button_layout_infos = Alloc(dialog_info->button_count * sizeof(*button_layout_infos));
     line_widths = Alloc(dialog_info->button_count * sizeof(*line_widths));
@@ -709,12 +815,13 @@ static void taskdialog_layout(struct taskdialog_info *dialog_info)
     taskdialog_du_to_px(dialog_info, &button_min_width, &button_height);
     for (i = 0; i < dialog_info->button_count; i++)
     {
-        taskdialog_get_label_size(dialog_info, dialog_info->buttons[i], dialog_width - h_spacing * 2, &size, FALSE);
+        taskdialog_get_label_size(dialog_info, dialog_info->buttons[i], dialog_width - expando_right - h_spacing * 2,
+                                  &size, FALSE);
         button_layout_infos[i].width = max(size.cx, button_min_width);
     }
 
     /* Separate buttons into lines */
-    x = h_spacing;
+    x = expando_right + h_spacing;
     for (i = 0, line_count = 0; i < dialog_info->button_count; i++)
     {
         button_layout_infos[i].line = line_count;
@@ -723,7 +830,7 @@ static void taskdialog_layout(struct taskdialog_info *dialog_info)
 
         if ((i + 1 < dialog_info->button_count) && (x + button_layout_infos[i + 1].width + h_spacing >= dialog_width))
         {
-            x = h_spacing;
+            x = expando_right + h_spacing;
             line_count++;
         }
     }
@@ -778,6 +885,7 @@ static void taskdialog_layout(struct taskdialog_info *dialog_info)
 
     /* Add height for last row button and spacing */
     dialog_height += size.cy + v_spacing;
+    dialog_height = max(dialog_height, expando_bottom);
 
     Free(button_layout_infos);
     Free(line_widths);
@@ -802,6 +910,41 @@ static void taskdialog_layout(struct taskdialog_info *dialog_info)
         SetWindowPos(dialog_info->hwnd, 0, 0, 0, dialog_width, dialog_height, SWP_NOMOVE | SWP_NOZORDER);
 }
 
+static void taskdialog_draw_expando_control(struct taskdialog_info *dialog_info, LPDRAWITEMSTRUCT dis)
+{
+    HWND hwnd;
+    HDC hdc;
+    RECT rect = {0};
+    WCHAR *text;
+    LONG icon_width, icon_height, text_offset;
+    UINT style = DFCS_FLAT;
+    BOOL draw_focus;
+
+    hdc = dis->hDC;
+    hwnd = dis->hwndItem;
+
+    SendMessageW(hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0);
+
+    icon_width = DIALOG_EXPANDO_ICON_WIDTH;
+    icon_height = DIALOG_EXPANDO_ICON_HEIGHT;
+    taskdialog_du_to_px(dialog_info, &icon_width, &icon_height);
+    rect.right = icon_width;
+    rect.bottom = icon_height;
+    style |= dialog_info->expanded ? DFCS_SCROLLUP : DFCS_SCROLLDOWN;
+    DrawFrameControl(hdc, &rect, DFC_SCROLL, style);
+
+    GetCharWidthW(hdc, '0', '0', &text_offset);
+    text_offset /= 2;
+
+    rect = dis->rcItem;
+    rect.left += icon_width + text_offset;
+    text = dialog_info->expanded ? dialog_info->expanded_text : dialog_info->collapsed_text;
+    DrawTextW(hdc, text, -1, &rect, DT_WORDBREAK | DT_END_ELLIPSIS | DT_EXPANDTABS);
+
+    draw_focus = (dis->itemState & ODS_FOCUS) && !(dis->itemState & ODS_NOFOCUSRECT);
+    if(draw_focus) DrawFocusRect(hdc, &rect);
+}
+
 static void taskdialog_init(struct taskdialog_info *dialog_info, HWND hwnd)
 {
     const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
@@ -839,6 +982,7 @@ static void taskdialog_init(struct taskdialog_info *dialog_info, HWND hwnd)
     taskdialog_add_progress_bar(dialog_info);
     taskdialog_add_radio_buttons(dialog_info);
     taskdialog_add_command_links(dialog_info);
+    taskdialog_add_expando_button(dialog_info);
     taskdialog_add_buttons(dialog_info);
 
     /* Set default button */
@@ -860,6 +1004,8 @@ static void taskdialog_destroy(struct taskdialog_info *dialog_info)
     Free(dialog_info->buttons);
     Free(dialog_info->radio_buttons);
     Free(dialog_info->command_links);
+    Free(dialog_info->expanded_text);
+    Free(dialog_info->collapsed_text);
 }
 
 static INT_PTR CALLBACK taskdialog_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
@@ -959,6 +1105,17 @@ static INT_PTR CALLBACK taskdialog_proc(HWND hwnd, UINT msg, WPARAM wParam, LPAR
             }
             return FALSE;
         }
+        case WM_DRAWITEM:
+        {
+            LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
+            if (dis->hwndItem == dialog_info->expando_button)
+            {
+                taskdialog_draw_expando_control(dialog_info, dis);
+                SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, TRUE);
+                break;
+            }
+            return FALSE;
+        }
         case WM_DESTROY:
             taskdialog_notify(dialog_info, TDN_DESTROYED, 0, 0);
             RemovePropW(hwnd, taskdialog_info_propnameW);
-- 
2.18.0





More information about the wine-devel mailing list