comctl32/taskdialog: Add support for verification box.

Signed-off-by: Zhiyi Zhang <zzhang@codeweavers.com>
Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Zhiyi Zhang 2018-07-19 11:04:24 +08:00 committed by Alexandre Julliard
parent c1b1af5af9
commit bf6cde92fb
2 changed files with 166 additions and 28 deletions

View File

@ -66,6 +66,7 @@ struct taskdialog_info
INT command_link_count;
HWND expanded_info;
HWND expando_button;
HWND verification_box;
HWND *buttons;
INT button_count;
HWND default_button;
@ -78,6 +79,7 @@ struct taskdialog_info
LONG v_spacing;
} m;
INT selected_radio_id;
BOOL verification_checked;
BOOL expanded;
WCHAR *expanded_text;
WCHAR *collapsed_text;
@ -247,6 +249,13 @@ static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, HWND
return;
}
if (hwnd == dialog_info->verification_box)
{
dialog_info->verification_checked = !dialog_info->verification_checked;
taskdialog_notify(dialog_info, TDN_VERIFICATION_CLICKED, dialog_info->verification_checked, 0);
return;
}
radio_button = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count, command_id);
if (radio_button)
{
@ -636,6 +645,26 @@ static void taskdialog_add_expando_button(struct taskdialog_info *dialog_info)
SendMessageW(dialog_info->expando_button, WM_SETFONT, (WPARAM)dialog_info->font, 0);
}
static void taskdialog_add_verification_box(struct taskdialog_info *dialog_info)
{
const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
static const DWORD style = BS_AUTOCHECKBOX | BS_MULTILINE | BS_LEFT | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP;
WCHAR *textW;
/* TDF_VERIFICATION_FLAG_CHECKED works even if pszVerificationText is not set */
if (taskconfig->dwFlags & TDF_VERIFICATION_FLAG_CHECKED) dialog_info->verification_checked = TRUE;
if (!taskconfig->pszVerificationText) return;
textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pszVerificationText);
dialog_info->verification_box = CreateWindowW(WC_BUTTONW, textW, style, 0, 0, 0, 0, dialog_info->hwnd, 0, 0, 0);
SendMessageW(dialog_info->verification_box, WM_SETFONT, (WPARAM)dialog_info->font, 0);
Free(textW);
if (taskconfig->dwFlags & TDF_VERIFICATION_FLAG_CHECKED)
SendMessageW(dialog_info->verification_box, BM_SETCHECK, BST_CHECKED, 0);
}
static void taskdialog_add_button(struct taskdialog_info *dialog_info, HWND *button, INT_PTR id, const WCHAR *text,
BOOL custom_button)
{
@ -806,6 +835,19 @@ static void taskdialog_layout(struct taskdialog_info *dialog_info)
expando_bottom = y + size.cy;
}
/* Verification box */
if (dialog_info->verification_box)
{
x = h_spacing;
y = expando_bottom + v_spacing;
size.cx = DIALOG_MIN_WIDTH / 2;
taskdialog_du_to_px(dialog_info, &size.cx, NULL);
taskdialog_get_radio_button_size(dialog_info, dialog_info->verification_box, size.cx, &size);
SetWindowPos(dialog_info->verification_box, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
expando_right = max(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));
@ -983,6 +1025,7 @@ static void taskdialog_init(struct taskdialog_info *dialog_info, HWND hwnd)
taskdialog_add_radio_buttons(dialog_info);
taskdialog_add_command_links(dialog_info);
taskdialog_add_expando_button(dialog_info);
taskdialog_add_verification_box(dialog_info);
taskdialog_add_buttons(dialog_info);
/* Set default button */
@ -1063,6 +1106,19 @@ static INT_PTR CALLBACK taskdialog_proc(HWND hwnd, UINT msg, WPARAM wParam, LPAR
case TDM_ENABLE_RADIO_BUTTON:
taskdialog_enable_radio_button(dialog_info, wParam, lParam);
break;
case TDM_CLICK_VERIFICATION:
{
BOOL checked = (BOOL)wParam;
BOOL focused = (BOOL)lParam;
dialog_info->verification_checked = checked;
if (dialog_info->verification_box)
{
SendMessageW(dialog_info->verification_box, BM_SETCHECK, checked ? BST_CHECKED : BST_UNCHECKED, 0);
taskdialog_notify(dialog_info, TDN_VERIFICATION_CLICKED, checked, 0);
if (focused) SetFocus(dialog_info->verification_box);
}
break;
}
case WM_INITDIALOG:
dialog_info = (struct taskdialog_info *)lParam;
@ -1151,7 +1207,7 @@ HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *taskconfig, int *butto
if (button) *button = ret;
if (radio_button) *radio_button = dialog_info.selected_radio_id;
if (verification_flag_checked) *verification_flag_checked = TRUE;
if (verification_flag_checked) *verification_flag_checked = dialog_info.verification_checked;
return S_OK;
}

View File

@ -229,6 +229,48 @@ static const struct message_info msg_return_press_negative_id_radio_button[] =
{ 0 }
};
static const struct message_info msg_return_default_verification_unchecked[] =
{
{ TDN_CREATED, 0, 0, S_OK, msg_send_click_ok },
{ TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
{ 0 }
};
static const struct message_info msg_return_default_verification_checked[] =
{
{ TDN_CREATED, 0, 0, S_OK, msg_send_click_ok },
{ TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
{ 0 }
};
static const struct message_info msg_uncheck_verification[] =
{
{ TDM_CLICK_VERIFICATION, FALSE, 0 },
{ 0 }
};
static const struct message_info msg_return_verification_unchecked[] =
{
{ TDN_CREATED, 0, 0, S_OK, msg_uncheck_verification },
{ TDN_VERIFICATION_CLICKED, FALSE, 0, S_OK, msg_send_click_ok },
{ TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
{ 0 }
};
static const struct message_info msg_check_verification[] =
{
{ TDM_CLICK_VERIFICATION, TRUE, 0 },
{ 0 }
};
static const struct message_info msg_return_verification_checked[] =
{
{ TDN_CREATED, 0, 0, S_OK, msg_check_verification },
{ TDN_VERIFICATION_CLICKED, TRUE, 0, S_OK, msg_send_click_ok },
{ TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
{ 0 }
};
static void init_test_message(UINT message, WPARAM wParam, LPARAM lParam, struct message *msg)
{
msg->message = WM_TD_CALLBACK;
@ -239,17 +281,18 @@ static void init_test_message(UINT message, WPARAM wParam, LPARAM lParam, struct
msg->stage = 0;
}
#define run_test(info, expect_button, expect_radio_button, seq, context) \
run_test_(info, expect_button, expect_radio_button, seq, context, \
ARRAY_SIZE(seq) - 1, __FILE__, __LINE__)
#define run_test(info, expect_button, expect_radio_button, verification_checked, seq, context) \
run_test_(info, expect_button, expect_radio_button, verification_checked, seq, context, \
ARRAY_SIZE(seq) - 1, __FILE__, __LINE__)
static void run_test_(TASKDIALOGCONFIG *info, int expect_button, int expect_radio_button,
static void run_test_(TASKDIALOGCONFIG *info, int expect_button, int expect_radio_button, BOOL verification_checked,
const struct message_info *test_messages, const char *context, int test_messages_len,
const char *file, int line)
{
struct message *msg, *msg_start;
int ret_button = 0;
int ret_radio = 0;
BOOL ret_verification = FALSE;
HRESULT hr;
int i;
@ -266,7 +309,7 @@ static void run_test_(TASKDIALOGCONFIG *info, int expect_button, int expect_radi
current_message_info = test_messages;
flush_sequences(sequences, NUM_MSG_SEQUENCES);
hr = pTaskDialogIndirect(info, &ret_button, &ret_radio, NULL);
hr = pTaskDialogIndirect(info, &ret_button, &ret_radio, &ret_verification);
ok_(file, line)(hr == S_OK, "TaskDialogIndirect() failed, got %#x.\n", hr);
ok_sequence_(sequences, TASKDIALOG_SEQ_INDEX, msg_start, context, FALSE, file, line);
@ -331,7 +374,7 @@ static void test_callback(void)
info.pfCallback = taskdialog_callback_proc;
info.lpCallbackData = test_ref_data;
run_test(&info, IDOK, 0, msg_return_press_ok, "Press VK_RETURN.");
run_test(&info, IDOK, 0, FALSE, msg_return_press_ok, "Press VK_RETURN.");
}
static void test_buttons(void)
@ -376,16 +419,16 @@ static void test_buttons(void)
info.nDefaultButton = 0; /* Should default to first created button */
info.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_YES_BUTTON | TDCBF_NO_BUTTON
| TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON;
run_test(&info, IDOK, 0, msg_return_press_ok, "default button: unset default");
run_test(&info, IDOK, 0, FALSE, msg_return_press_ok, "default button: unset default");
info.dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_NO_BUTTON
| TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON;
run_test(&info, IDYES, 0, msg_return_press_yes, "default button: unset default");
run_test(&info, IDYES, 0, FALSE, msg_return_press_yes, "default button: unset default");
info.dwCommonButtons = TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON;
run_test(&info, IDNO, 0, msg_return_press_no, "default button: unset default");
run_test(&info, IDNO, 0, FALSE, msg_return_press_no, "default button: unset default");
info.dwCommonButtons = TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON;
run_test(&info, IDRETRY, 0, msg_return_press_retry, "default button: unset default");
run_test(&info, IDRETRY, 0, FALSE, msg_return_press_retry, "default button: unset default");
info.dwCommonButtons = TDCBF_CANCEL_BUTTON | TDCBF_CLOSE_BUTTON;
run_test(&info, IDCANCEL, 0, msg_return_press_cancel, "default button: unset default");
run_test(&info, IDCANCEL, 0, FALSE, msg_return_press_cancel, "default button: unset default");
/* Custom buttons could be command links */
for (i = 0; i < ARRAY_SIZE(command_link_flags); i++)
@ -396,30 +439,30 @@ static void test_buttons(void)
info.nDefaultButton = 0xff; /* Random ID, should also default to first created button */
info.cButtons = TEST_NUM_BUTTONS;
info.pButtons = custom_buttons;
run_test(&info, ID_START_BUTTON, 0, msg_return_press_custom1,
run_test(&info, ID_START_BUTTON, 0, FALSE, msg_return_press_custom1,
"default button: invalid default, with common buttons - 1");
info.nDefaultButton = -1; /* Should work despite button ID -1 */
run_test(&info, -1, 0, msg_return_press_custom10, "default button: invalid default, with common buttons - 2");
run_test(&info, -1, 0, FALSE, msg_return_press_custom10, "default button: invalid default, with common buttons - 2");
info.nDefaultButton = -2; /* Should also default to first created button */
run_test(&info, ID_START_BUTTON, 0, msg_return_press_custom1,
run_test(&info, ID_START_BUTTON, 0, FALSE, msg_return_press_custom1,
"default button: invalid default, with common buttons - 3");
/* Test with only custom buttons and invalid default ID */
info.dwCommonButtons = 0;
run_test(&info, ID_START_BUTTON, 0, msg_return_press_custom1,
run_test(&info, ID_START_BUTTON, 0, FALSE, msg_return_press_custom1,
"default button: invalid default, no common buttons");
/* Test with common and custom buttons and valid default ID */
info.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_YES_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON
| TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON;
info.nDefaultButton = IDRETRY;
run_test(&info, IDRETRY, 0, msg_return_press_retry, "default button: valid default - 1");
run_test(&info, IDRETRY, 0, FALSE, msg_return_press_retry, "default button: valid default - 1");
/* Test with common and custom buttons and valid default ID */
info.nDefaultButton = ID_START_BUTTON + 3;
run_test(&info, ID_START_BUTTON + 3, 0, msg_return_press_custom4, "default button: valid default - 2");
run_test(&info, ID_START_BUTTON + 3, 0, FALSE, msg_return_press_custom4, "default button: valid default - 2");
}
/* Test radio buttons */
@ -431,44 +474,52 @@ static void test_buttons(void)
info.pRadioButtons = radio_buttons;
/* Test default first radio button */
run_test(&info, IDOK, ID_START_RADIO_BUTTON, msg_return_default_radio_button_1, "default radio button: default first radio button");
run_test(&info, IDOK, ID_START_RADIO_BUTTON, FALSE, msg_return_default_radio_button_1,
"default radio button: default first radio button");
/* Test default radio button */
info.nDefaultRadioButton = ID_START_RADIO_BUTTON + 1;
run_test(&info, IDOK, info.nDefaultRadioButton, msg_return_default_radio_button_2, "default radio button: default radio button");
run_test(&info, IDOK, info.nDefaultRadioButton, FALSE, msg_return_default_radio_button_2,
"default radio button: default radio button");
/* Test default radio button with -2 */
info.nDefaultRadioButton = -2;
run_test(&info, IDOK, info.nDefaultRadioButton, msg_return_default_radio_button_3, "default radio button: default radio button with id -2");
run_test(&info, IDOK, info.nDefaultRadioButton, FALSE, msg_return_default_radio_button_3,
"default radio button: default radio button with id -2");
/* Test default radio button after clicking the first, messages still work even radio button is disabled */
info.nDefaultRadioButton = ID_START_RADIO_BUTTON + 1;
run_test(&info, IDOK, ID_START_RADIO_BUTTON, msg_return_first_radio_button, "default radio button: radio button after clicking");
run_test(&info, IDOK, ID_START_RADIO_BUTTON, FALSE, msg_return_first_radio_button,
"default radio button: radio button after clicking");
/* Test radio button after disabling and clicking the first */
info.nDefaultRadioButton = ID_START_RADIO_BUTTON + 1;
run_test(&info, IDOK, ID_START_RADIO_BUTTON, msg_return_default_radio_button_clicking_disabled, "default radio button: disable radio button before clicking");
run_test(&info, IDOK, ID_START_RADIO_BUTTON, FALSE, msg_return_default_radio_button_clicking_disabled,
"default radio button: disable radio button before clicking");
/* Test no default radio button, TDF_NO_DEFAULT_RADIO_BUTTON is set, TDN_RADIO_BUTTON_CLICKED will still be received, just radio button not selected */
info.nDefaultRadioButton = ID_START_RADIO_BUTTON;
info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON;
run_test(&info, IDOK, info.nDefaultRadioButton, msg_return_no_default_radio_button_flag, "default radio button: no default radio flag");
run_test(&info, IDOK, info.nDefaultRadioButton, FALSE, msg_return_no_default_radio_button_flag,
"default radio button: no default radio flag");
/* Test no default radio button, TDF_NO_DEFAULT_RADIO_BUTTON is set and nDefaultRadioButton is 0.
* TDN_RADIO_BUTTON_CLICKED will not be sent, and just radio button not selected */
info.nDefaultRadioButton = 0;
info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON;
run_test(&info, IDOK, 0, msg_return_no_default_radio_button_id_and_flag, "default radio button: no default radio id and flag");
run_test(&info, IDOK, 0, FALSE, msg_return_no_default_radio_button_id_and_flag,
"default radio button: no default radio id and flag");
/* Test no default radio button, TDF_NO_DEFAULT_RADIO_BUTTON is set and nDefaultRadioButton is invalid.
* TDN_RADIO_BUTTON_CLICKED will not be sent, and just radio button not selected */
info.nDefaultRadioButton = 0xff;
info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON;
run_test(&info, IDOK, 0, msg_return_no_default_radio_button_id_and_flag, "default radio button: no default flag, invalid id");
run_test(&info, IDOK, 0, FALSE, msg_return_no_default_radio_button_id_and_flag,
"default radio button: no default flag, invalid id");
info.nDefaultRadioButton = 0;
info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON;
run_test(&info, IDOK, -2, msg_return_press_negative_id_radio_button,
run_test(&info, IDOK, -2, FALSE, msg_return_press_negative_id_radio_button,
"radio button: manually click radio button with negative id");
}
@ -481,7 +532,7 @@ static void test_help(void)
info.lpCallbackData = test_ref_data;
info.dwCommonButtons = TDCBF_OK_BUTTON;
run_test(&info, IDOK, 0, msg_got_tdn_help, "send f1");
run_test(&info, IDOK, 0, FALSE, msg_got_tdn_help, "send f1");
}
struct timer_notification_data
@ -601,6 +652,36 @@ static void test_progress_bar(void)
pTaskDialogIndirect(&info, NULL, NULL, NULL);
}
static void test_verification_box(void)
{
TASKDIALOGCONFIG info = {0};
WCHAR textW[] = {'t', 'e', 'x', 't', 0};
info.cbSize = sizeof(TASKDIALOGCONFIG);
info.pfCallback = taskdialog_callback_proc;
info.lpCallbackData = test_ref_data;
info.dwCommonButtons = TDCBF_OK_BUTTON;
/* TDF_VERIFICATION_FLAG_CHECKED works even if pszVerificationText is not set */
run_test(&info, IDOK, 0, FALSE, msg_return_default_verification_unchecked, "default verification box: unchecked");
info.dwFlags = TDF_VERIFICATION_FLAG_CHECKED;
run_test(&info, IDOK, 0, FALSE, msg_return_default_verification_checked, "default verification box: checked");
info.pszVerificationText = textW;
run_test(&info, IDOK, 0, FALSE, msg_return_default_verification_unchecked, "default verification box: unchecked");
info.dwFlags = TDF_VERIFICATION_FLAG_CHECKED;
run_test(&info, IDOK, 0, FALSE, msg_return_default_verification_checked, "default verification box: checked");
run_test(&info, IDOK, 0, FALSE, msg_return_verification_unchecked,
"default verification box: default checked and then unchecked");
info.dwFlags = 0;
run_test(&info, IDOK, 0, FALSE, msg_return_verification_checked,
"default verification box: default unchecked and then checked");
}
START_TEST(taskdialog)
{
ULONG_PTR ctx_cookie;
@ -640,6 +721,7 @@ START_TEST(taskdialog)
test_help();
test_timer();
test_progress_bar();
test_verification_box();
unload_v6_module(ctx_cookie, hCtx);
}