From 5a949efc5c9ebe5e455d5a82492a6fd303f29991 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 25 Dec 2017 19:29:25 +0100 Subject: [PATCH] Fix sending wxEVT_TEXT_ENTER when using auto-completion in wxMSW We need to explicitly generate this event from the char hook handler as we don't get the normal WM_CHAR for it, it is apparently intercepted by the window proc installed by the auto-completing code, so check if wxTE_PROCESS_ENTER is used for the text entry and call a special new MSWProcessSpecialKey() method to do the right thing if it is. Similarly, handle Tab presses correctly if wxTE_PROCESS_TAB is used. Closes #12613. --- docs/changes.txt | 1 + include/wx/msw/combobox.h | 8 +++ include/wx/msw/textctrl.h | 4 ++ include/wx/msw/textentry.h | 10 ++++ src/msw/combobox.cpp | 105 ++++++++++++++++++++++--------------- src/msw/textctrl.cpp | 13 +++++ src/msw/textentry.cpp | 54 ++++++++++++++++--- 7 files changed, 144 insertions(+), 51 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index 676a77fc19..3f776b10b2 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -232,6 +232,7 @@ wxMSW: - Implement SetIcon(), SetPosition(), GetPosition() for native wxProgressDialog. - Fix focus-related problems when using native wxProgressDialog. - Fix crash when reparenting the currently focused window to another TLW. +- Fix sending wxEVT_TEXT_ENTER when using auto-completion (Dubby). wxOSX: diff --git a/include/wx/msw/combobox.h b/include/wx/msw/combobox.h index 329c10adb1..027b7c277e 100644 --- a/include/wx/msw/combobox.h +++ b/include/wx/msw/combobox.h @@ -158,6 +158,14 @@ private: virtual wxWindow *GetEditableWindow() wxOVERRIDE; virtual WXHWND GetEditHWND() const wxOVERRIDE; + // Common part of MSWProcessEditMsg() and MSWProcessSpecialKey(), return + // true if the key was processed. + bool MSWProcessEditSpecialKey(WXWPARAM vkey); + +#if wxUSE_OLE + virtual void MSWProcessSpecialKey(wxKeyEvent& event) wxOVERRIDE; +#endif // wxUSE_OLE + // common part of all ctors void Init() { diff --git a/include/wx/msw/textctrl.h b/include/wx/msw/textctrl.h index a3d5870532..4fe3a470e6 100644 --- a/include/wx/msw/textctrl.h +++ b/include/wx/msw/textctrl.h @@ -277,6 +277,10 @@ private: // the simple EDIT controls virtual WXHWND GetEditHWND() const wxOVERRIDE { return m_hWnd; } +#if wxUSE_OLE + virtual void MSWProcessSpecialKey(wxKeyEvent& event) wxOVERRIDE; +#endif // wxUSE_OLE + void OnKeyDown(wxKeyEvent& event); // Used by EN_MAXTEXT handler to increase the size limit (will do nothing diff --git a/include/wx/msw/textentry.h b/include/wx/msw/textentry.h index 8f9d92ba95..5d3a0d9e8a 100644 --- a/include/wx/msw/textentry.h +++ b/include/wx/msw/textentry.h @@ -86,6 +86,16 @@ private: virtual WXHWND GetEditHWND() const = 0; #if wxUSE_OLE + // This method is called to process special keys such as Return and Tab + // before they're consumed by the auto-completer. Notice that it is only + // called if we do need to process the key, i.e. if the corresponding + // wxTE_PROCESS_XXX style is set in the associated object. + // + // It is not pure virtual because it won't get called if the derived class + // doesn't use auto-completer, but it does need to be overridden if it can + // be called and the default implementation asserts if this is not the case. + virtual void MSWProcessSpecialKey(wxKeyEvent& event); + // Get the auto-complete object creating it if necessary. Returns NULL if // creating it failed. wxTextAutoCompleteData *GetOrCreateCompleter(); diff --git a/src/msw/combobox.cpp b/src/msw/combobox.cpp index 47b288f86b..999cac1717 100644 --- a/src/msw/combobox.cpp +++ b/src/msw/combobox.cpp @@ -218,54 +218,63 @@ WXLRESULT wxComboBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara return wxChoice::MSWWindowProc(nMsg, wParam, lParam); } +bool wxComboBox::MSWProcessEditSpecialKey(WXWPARAM vkey) +{ + // for compatibility with wxTextCtrl, generate a special message + // when Enter is pressed + switch ( vkey ) + { + case VK_RETURN: + { + if (SendMessage(GetHwnd(), CB_GETDROPPEDSTATE, 0, 0)) + break; + + wxCommandEvent event(wxEVT_TEXT_ENTER, m_windowId); + + const int sel = GetSelection(); + event.SetInt(sel); + event.SetString(GetValue()); + InitCommandEventWithItems(event, sel); + + if ( ProcessCommand(event) ) + { + // don't let the event through to the native control + // because it doesn't need it and may generate an annoying + // beep if it gets it + return true; + } + } + break; + + case VK_TAB: + // If we have wxTE_PROCESS_ENTER style, we get all char + // events, including those for TAB which are usually used + // for keyboard navigation, but we should not process them + // unless we also have wxTE_PROCESS_TAB style. + if ( !HasFlag(wxTE_PROCESS_TAB) ) + { + int flags = 0; + if ( !wxIsShiftDown() ) + flags |= wxNavigationKeyEvent::IsForward; + if ( wxIsCtrlDown() ) + flags |= wxNavigationKeyEvent::WinChange; + if ( Navigate(flags) ) + return true; + } + break; + } + + return false; +} + bool wxComboBox::MSWProcessEditMsg(WXUINT msg, WXWPARAM wParam, WXLPARAM lParam) { switch ( msg ) { case WM_CHAR: - // for compatibility with wxTextCtrl, generate a special message - // when Enter is pressed - switch ( wParam ) - { - case VK_RETURN: - { - if (SendMessage(GetHwnd(), CB_GETDROPPEDSTATE, 0, 0)) - return false; - - wxCommandEvent event(wxEVT_TEXT_ENTER, m_windowId); - - const int sel = GetSelection(); - event.SetInt(sel); - event.SetString(GetValue()); - InitCommandEventWithItems(event, sel); - - if ( ProcessCommand(event) ) - { - // don't let the event through to the native control - // because it doesn't need it and may generate an annoying - // beep if it gets it - return true; - } - } - break; - - case VK_TAB: - // If we have wxTE_PROCESS_ENTER style, we get all char - // events, including those for TAB which are usually used - // for keyboard navigation, but we should not process them - // unless we also have wxTE_PROCESS_TAB style. - if ( !HasFlag(wxTE_PROCESS_TAB) ) - { - int flags = 0; - if ( !wxIsShiftDown() ) - flags |= wxNavigationKeyEvent::IsForward; - if ( wxIsCtrlDown() ) - flags |= wxNavigationKeyEvent::WinChange; - if ( Navigate(flags) ) - return true; - } - break; - } + if ( MSWProcessEditSpecialKey(wParam) ) + return true; + break; } if ( ShouldForwardFromEditToCombo(msg) ) @@ -383,6 +392,16 @@ bool wxComboBox::MSWShouldPreProcessMessage(WXMSG *pMsg) return wxChoice::MSWShouldPreProcessMessage(pMsg); } +#if wxUSE_OLE + +void wxComboBox::MSWProcessSpecialKey(wxKeyEvent& event) +{ + if ( !MSWProcessEditSpecialKey(event.GetRawKeyCode()) ) + event.Skip(); +} + +#endif // wxUSE_OLE + WXHWND wxComboBox::GetEditHWNDIfAvailable() const { WinStruct info; diff --git a/src/msw/textctrl.cpp b/src/msw/textctrl.cpp index ddc02dec39..2f7bc4d21a 100644 --- a/src/msw/textctrl.cpp +++ b/src/msw/textctrl.cpp @@ -2103,6 +2103,19 @@ void wxTextCtrl::OnChar(wxKeyEvent& event) event.Skip(); } +#if wxUSE_OLE + +void wxTextCtrl::MSWProcessSpecialKey(wxKeyEvent& event) +{ + // It is not a good idea, in general, to manually call another event + // handler, but here we need to do exactly the same thing as in OnChar() + // above, so it doesn't seem to make much sense to add another function to + // forward to when we can just call it directly. + OnChar(event); +} + +#endif // wxUSE_OLE + void wxTextCtrl::OnKeyDown(wxKeyEvent& event) { // richedit control doesn't send WM_PASTE, WM_CUT and WM_COPY messages diff --git a/src/msw/textentry.cpp b/src/msw/textentry.cpp index 97956296ab..46246846ca 100644 --- a/src/msw/textentry.cpp +++ b/src/msw/textentry.cpp @@ -573,21 +573,54 @@ private: void OnCharHook(wxKeyEvent& event) { - // If the autocomplete drop-down list is currently displayed when the - // user presses Escape, we need to dismiss it manually from here as - // Escape could be eaten by something else (e.g. EVT_CHAR_HOOK in the - // dialog that this control is found in) otherwise. - if ( event.GetKeyCode() == WXK_ESCAPE ) + // We need to override the default handling of some keys here. + bool specialKey = false; + switch ( event.GetKeyCode() ) { + case WXK_RETURN: + if ( m_win->HasFlag(wxTE_PROCESS_ENTER) ) + specialKey = true; + break; + + case WXK_TAB: + if ( m_win->HasFlag(wxTE_PROCESS_TAB) ) + specialKey = true; + break; + + case WXK_ESCAPE: + specialKey = true; + break; + } + + if ( specialKey ) + { + // Check if the drop down is currently open. DWORD dwFlags = 0; if ( SUCCEEDED(m_autoCompleteDropDown->GetDropDownStatus(&dwFlags, NULL)) && dwFlags == ACDD_VISIBLE ) { - ::SendMessage(GetHwndOf(m_win), WM_KEYDOWN, WXK_ESCAPE, 0); + if ( event.GetKeyCode() == WXK_ESCAPE ) + { + // We need to dismiss the drop-down manually as Escape + // could be eaten by something else (e.g. EVT_CHAR_HOOK in + // the dialog that this control is found in) otherwise. + ::SendMessage(GetHwndOf(m_win), WM_KEYDOWN, WXK_ESCAPE, 0); - // Do not skip the event in this case, we've already handled it. - return; + // Do not skip the event in this case, we've already handled it. + return; + } + } + else // Drop down is not open. + { + // In this case we need to handle Return and Tab as both of + // them are simply eaten by the auto completer and never reach + // us at all otherwise. + if ( event.GetKeyCode() != WXK_ESCAPE ) + { + m_entry->MSWProcessSpecialKey(event); + return; + } } } @@ -804,6 +837,11 @@ bool wxTextEntry::DoAutoCompleteFileNames(int flags) #endif // wxUSE_DYNLIB_CLASS +void wxTextEntry::MSWProcessSpecialKey(wxKeyEvent& WXUNUSED(event)) +{ + wxFAIL_MSG(wxS("Must be overridden if can be called")); +} + wxTextAutoCompleteData *wxTextEntry::GetOrCreateCompleter() { if ( !m_autoCompleteData )