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.
This commit is contained in:
Vadim Zeitlin
2017-12-25 19:29:25 +01:00
parent f32edbe1fc
commit 5a949efc5c
7 changed files with 144 additions and 51 deletions

View File

@@ -232,6 +232,7 @@ wxMSW:
- Implement SetIcon(), SetPosition(), GetPosition() for native wxProgressDialog. - Implement SetIcon(), SetPosition(), GetPosition() for native wxProgressDialog.
- Fix focus-related problems when using native wxProgressDialog. - Fix focus-related problems when using native wxProgressDialog.
- Fix crash when reparenting the currently focused window to another TLW. - Fix crash when reparenting the currently focused window to another TLW.
- Fix sending wxEVT_TEXT_ENTER when using auto-completion (Dubby).
wxOSX: wxOSX:

View File

@@ -158,6 +158,14 @@ private:
virtual wxWindow *GetEditableWindow() wxOVERRIDE; virtual wxWindow *GetEditableWindow() wxOVERRIDE;
virtual WXHWND GetEditHWND() const 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 // common part of all ctors
void Init() void Init()
{ {

View File

@@ -277,6 +277,10 @@ private:
// the simple EDIT controls // the simple EDIT controls
virtual WXHWND GetEditHWND() const wxOVERRIDE { return m_hWnd; } virtual WXHWND GetEditHWND() const wxOVERRIDE { return m_hWnd; }
#if wxUSE_OLE
virtual void MSWProcessSpecialKey(wxKeyEvent& event) wxOVERRIDE;
#endif // wxUSE_OLE
void OnKeyDown(wxKeyEvent& event); void OnKeyDown(wxKeyEvent& event);
// Used by EN_MAXTEXT handler to increase the size limit (will do nothing // Used by EN_MAXTEXT handler to increase the size limit (will do nothing

View File

@@ -86,6 +86,16 @@ private:
virtual WXHWND GetEditHWND() const = 0; virtual WXHWND GetEditHWND() const = 0;
#if wxUSE_OLE #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 // Get the auto-complete object creating it if necessary. Returns NULL if
// creating it failed. // creating it failed.
wxTextAutoCompleteData *GetOrCreateCompleter(); wxTextAutoCompleteData *GetOrCreateCompleter();

View File

@@ -218,54 +218,63 @@ WXLRESULT wxComboBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPara
return wxChoice::MSWWindowProc(nMsg, wParam, lParam); 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) bool wxComboBox::MSWProcessEditMsg(WXUINT msg, WXWPARAM wParam, WXLPARAM lParam)
{ {
switch ( msg ) switch ( msg )
{ {
case WM_CHAR: case WM_CHAR:
// for compatibility with wxTextCtrl, generate a special message if ( MSWProcessEditSpecialKey(wParam) )
// when Enter is pressed return true;
switch ( wParam ) break;
{
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 ( ShouldForwardFromEditToCombo(msg) ) if ( ShouldForwardFromEditToCombo(msg) )
@@ -383,6 +392,16 @@ bool wxComboBox::MSWShouldPreProcessMessage(WXMSG *pMsg)
return wxChoice::MSWShouldPreProcessMessage(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 WXHWND wxComboBox::GetEditHWNDIfAvailable() const
{ {
WinStruct<COMBOBOXINFO> info; WinStruct<COMBOBOXINFO> info;

View File

@@ -2103,6 +2103,19 @@ void wxTextCtrl::OnChar(wxKeyEvent& event)
event.Skip(); 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) void wxTextCtrl::OnKeyDown(wxKeyEvent& event)
{ {
// richedit control doesn't send WM_PASTE, WM_CUT and WM_COPY messages // richedit control doesn't send WM_PASTE, WM_CUT and WM_COPY messages

View File

@@ -573,21 +573,54 @@ private:
void OnCharHook(wxKeyEvent& event) void OnCharHook(wxKeyEvent& event)
{ {
// If the autocomplete drop-down list is currently displayed when the // We need to override the default handling of some keys here.
// user presses Escape, we need to dismiss it manually from here as bool specialKey = false;
// Escape could be eaten by something else (e.g. EVT_CHAR_HOOK in the switch ( event.GetKeyCode() )
// dialog that this control is found in) otherwise.
if ( event.GetKeyCode() == WXK_ESCAPE )
{ {
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; DWORD dwFlags = 0;
if ( SUCCEEDED(m_autoCompleteDropDown->GetDropDownStatus(&dwFlags, if ( SUCCEEDED(m_autoCompleteDropDown->GetDropDownStatus(&dwFlags,
NULL)) NULL))
&& dwFlags == ACDD_VISIBLE ) && 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. // Do not skip the event in this case, we've already handled it.
return; 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 #endif // wxUSE_DYNLIB_CLASS
void wxTextEntry::MSWProcessSpecialKey(wxKeyEvent& WXUNUSED(event))
{
wxFAIL_MSG(wxS("Must be overridden if can be called"));
}
wxTextAutoCompleteData *wxTextEntry::GetOrCreateCompleter() wxTextAutoCompleteData *wxTextEntry::GetOrCreateCompleter()
{ {
if ( !m_autoCompleteData ) if ( !m_autoCompleteData )