From: Alex Henrie Subject: user32: Correct dialog focus behavior. Message-Id: <20120806171315.91fc82db4c9c2f32399acf41@gmail.com> Date: Mon, 6 Aug 2012 17:13:15 -0600 This patch adds four focus behavior tests to user32 and code to make them pass. Fixes bug 31386. --- dlls/user32/defdlg.c | 21 ++++++++++++++------- dlls/user32/tests/dialog.c | 42 +++++++++++++++++++++++++++++++++++------- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/dlls/user32/defdlg.c b/dlls/user32/defdlg.c index fecd417..e4b55f3 100644 --- a/dlls/user32/defdlg.c +++ b/dlls/user32/defdlg.c @@ -55,9 +55,9 @@ static DLGPROC DEFDLG_GetDlgProc( HWND hwnd ) * DEFDLG_SetFocus * * Set the focus to a control of the dialog, selecting the text if - * the control is an edit dialog. + * the control is an edit dialog that has DLGC_HASSETSEL. */ -static void DEFDLG_SetFocus( HWND hwndDlg, HWND hwndCtrl ) +static void DEFDLG_SetFocus( HWND hwndCtrl ) { if (SendMessageW( hwndCtrl, WM_GETDLGCODE, 0, 0 ) & DLGC_HASSETSEL) SendMessageW( hwndCtrl, EM_SETSEL, 0, -1 ); @@ -83,7 +83,7 @@ static void DEFDLG_SaveFocus( HWND hwnd ) /*********************************************************************** * DEFDLG_RestoreFocus */ -static void DEFDLG_RestoreFocus( HWND hwnd ) +static void DEFDLG_RestoreFocus( HWND hwnd, BOOL justActivate ) { DIALOGINFO *infoPtr; @@ -92,12 +92,19 @@ static void DEFDLG_RestoreFocus( HWND hwnd ) /* Don't set the focus back to controls if EndDialog is already called.*/ if (infoPtr->flags & DF_END) return; if (!IsWindow(infoPtr->hwndFocus) || infoPtr->hwndFocus == hwnd) { + if (justActivate) return; /* If no saved focus control exists, set focus to the first visible, non-disabled, WS_TABSTOP control in the dialog */ infoPtr->hwndFocus = GetNextDlgTabItem( hwnd, 0, FALSE ); + /* If there are no WS_TABSTOP controls, set focus to the first visible, + non-disabled control in the dialog */ + if (!infoPtr->hwndFocus) infoPtr->hwndFocus = GetNextDlgGroupItem( hwnd, 0, FALSE ); if (!IsWindow( infoPtr->hwndFocus )) return; } - DEFDLG_SetFocus( hwnd, infoPtr->hwndFocus ); + if (justActivate) + SetFocus( infoPtr->hwndFocus ); + else + DEFDLG_SetFocus( infoPtr->hwndFocus ); /* This used to set infoPtr->hwndFocus to NULL for no apparent reason, sometimes losing focus when receiving WM_SETFOCUS messages. */ @@ -250,12 +257,12 @@ static LRESULT DEFDLG_Proc( HWND hwnd, UINT msg, WPARAM wParam, return DefWindowProcA( hwnd, msg, wParam, lParam ); case WM_ACTIVATE: - if (wParam) DEFDLG_RestoreFocus( hwnd ); + if (wParam) DEFDLG_RestoreFocus( hwnd, TRUE ); else DEFDLG_SaveFocus( hwnd ); return 0; case WM_SETFOCUS: - DEFDLG_RestoreFocus( hwnd ); + DEFDLG_RestoreFocus( hwnd, FALSE ); return 0; case DM_SETDEFID: @@ -280,7 +287,7 @@ static LRESULT DEFDLG_Proc( HWND hwnd, UINT msg, WPARAM wParam, HWND hwndDest = (HWND)wParam; if (!lParam) hwndDest = GetNextDlgTabItem(hwnd, GetFocus(), wParam); - if (hwndDest) DEFDLG_SetFocus( hwnd, hwndDest ); + if (hwndDest) DEFDLG_SetFocus( hwndDest ); DEFDLG_SetDefButton( hwnd, dlgInfo, hwndDest ); } return 0; diff --git a/dlls/user32/tests/dialog.c b/dlls/user32/tests/dialog.c index 6bd34b3..0ebab4c 100644 --- a/dlls/user32/tests/dialog.c +++ b/dlls/user32/tests/dialog.c @@ -828,7 +828,7 @@ static const char * GetHwndString(HWND hw) return "unknown handle"; } -static void test_initial_focus(void) +static void test_focus(void) { /* Test 1: * This test intentionally returns FALSE in response to WM_INITDIALOG @@ -900,6 +900,8 @@ static void test_initial_focus(void) HRSRC hResource; HANDLE hTemplate; DLGTEMPLATE* pTemplate; + HWND hTextbox; + DWORD selectionStart = 0xdead, selectionEnd = 0xbeef; hResource = FindResourceA(g_hinst,"FOCUS_TEST_DIALOG", RT_DIALOG); hTemplate = LoadResource(g_hinst, hResource); @@ -912,6 +914,20 @@ static void test_initial_focus(void) ok ((g_hwndInitialFocusT1 == 0), "Focus should not be set for an invisible DS_CONTROL dialog %p.\n", g_hwndInitialFocusT1); + /* Also make sure that WM_SETFOCUS selects the textbox's text */ + hTextbox = GetDlgItem(hDlg, 200); + SendMessage(hTextbox, WM_SETTEXT, 0, (LPARAM)"Hello world"); + + SendMessage(hDlg, WM_SETFOCUS, 0, 0); + SendMessage(hTextbox, EM_GETSEL, (WPARAM)&selectionStart, (LPARAM)&selectionEnd); + ok(selectionStart == 0 && selectionEnd == 11, "Text selection after WM_SETFOCUS is [%i, %i) expected [0, 11)\n", selectionStart, selectionEnd); + + /* but WM_ACTIVATE does not */ + SendMessage(hTextbox, EM_SETSEL, 0, 0); + SendMessage(hDlg, WM_ACTIVATE, WA_ACTIVE, 0); + SendMessage(hTextbox, EM_GETSEL, (WPARAM)&selectionStart, (LPARAM)&selectionEnd); + ok(selectionStart == 0 && selectionEnd == 0, "Text selection after WM_ACTIVATE is [%i, %i) expected [0, 0)\n", selectionStart, selectionEnd); + DestroyWindow(hDlg); } @@ -929,13 +945,25 @@ static void test_initial_focus(void) pTemplate = LockResource(hTemplate); hDlg = CreateDialogIndirectParamA(g_hinst, pTemplate, NULL, focusDlgWinProc, 0); - g_hwndInitialFocusT1 = GetFocus(); + ok(hDlg != 0, "Failed to create test dialog.\n"); hLabel = GetDlgItem(hDlg, 200); - ok (hDlg != 0, "Failed to create test dialog.\n"); - ok ((g_hwndInitialFocusT1 == hLabel), - "Focus should have been set to the first control, expected (%p) got (%p).\n", - hLabel, g_hwndInitialFocusT1); + ok(GetFocus() == hLabel, "Focus not set to label, focus=%p dialog=%p label=%p\n", GetFocus(), hDlg, hLabel); + + DestroyWindow(hDlg); + + /* Also check focus after WM_ACTIVATE and WM_SETFOCUS */ + hDlg = CreateDialogIndirectParamA(g_hinst, pTemplate, NULL, NULL, 0); + ok(hDlg != 0, "Failed to create test dialog.\n"); + hLabel = GetDlgItem(hDlg, 200); + + SetFocus(NULL); + SendMessage(hDlg, WM_ACTIVATE, WA_ACTIVE, 0); + ok(GetFocus() == NULL, "Focus set on WM_ACTIVATE, focus=%p dialog=%p label=%p\n", GetFocus(), hDlg, hLabel); + + SetFocus(NULL); + SendMessage(hDlg, WM_SETFOCUS, 0, 0); + ok(GetFocus() == hLabel, "Focus not set to label on WM_SETFOCUS, focus=%p dialog=%p label=%p\n", GetFocus(), hDlg, hLabel); DestroyWindow(hDlg); } @@ -1404,7 +1432,7 @@ START_TEST(dialog) test_GetNextDlgItem(); test_IsDialogMessage(); test_WM_NEXTDLGCTL(); - test_initial_focus(); + test_focus(); test_GetDlgItem(); test_GetDlgItemText(); test_DialogBoxParamA(); -- 1.7.9.5