Ensure that Enter key presses are never stolen from wxButton in wxMSW.

This commit fixes the following bug: when an in-place editor control containing
an embedded button was used in wxDataViewCtrl, pressing Enter on the button
would close the editor, accepting changes, instead as (generic) wxDataViewCtrl
intercepts WXK_RETURN in its EVT_CHAR_HOOK handler. To prevent this from
happening, wxButton now handles EVT_CHAR_HOOK itself and never lets the parent
window intercept it if it's for WXK_RETURN. To ensure that normal
wxEVT_KEY_DOWN and wxEVT_CHAR are still generated in this case, wxButton
handler calls the new wxKeyEvent::DoAllowNextEvent() method that was added to
allow suppressing EVT_CHAR_HOOK only, without affecting the subsequent events.
DoAllowNextEvent() is currently only used in wxMSW but support for it was also
added to wxGTK and (both) wxOSX ports.

See #9102.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@69984 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2011-12-11 17:03:56 +00:00
parent 91614f1aa3
commit 4cf1a9bf4a
8 changed files with 93 additions and 12 deletions

View File

@@ -1707,6 +1707,15 @@ public:
// Get Y position // Get Y position
wxCoord GetY() const { return m_y; } wxCoord GetY() const { return m_y; }
// Can be called from wxEVT_CHAR_HOOK handler to allow generation of normal
// key events even though the event had been handled (by default they would
// not be generated in this case).
void DoAllowNextEvent() { m_allowNext = true; }
// Return the value of the "allow next" flag, for internal use only.
bool IsNextEventAllowed() const { return m_allowNext; }
virtual wxEvent *Clone() const { return new wxKeyEvent(*this); } virtual wxEvent *Clone() const { return new wxKeyEvent(*this); }
virtual wxEventCategory GetEventCategory() const { return wxEVT_CATEGORY_USER_INPUT; } virtual wxEventCategory GetEventCategory() const { return wxEVT_CATEGORY_USER_INPUT; }
@@ -1750,6 +1759,8 @@ private:
{ {
if ( m_eventType == wxEVT_CHAR_HOOK ) if ( m_eventType == wxEVT_CHAR_HOOK )
m_propagationLevel = wxEVENT_PROPAGATE_MAX; m_propagationLevel = wxEVENT_PROPAGATE_MAX;
m_allowNext = false;
} }
// Copy only the event data present in this class, this is used by // Copy only the event data present in this class, this is used by
@@ -1768,6 +1779,11 @@ private:
#endif #endif
} }
// If this flag is true, the normal key events should still be generated
// even if wxEVT_CHAR_HOOK had been handled. By default it is false as
// handling wxEVT_CHAR_HOOK suppresses all the subsequent events.
bool m_allowNext;
DECLARE_DYNAMIC_CLASS(wxKeyEvent) DECLARE_DYNAMIC_CLASS(wxKeyEvent)
}; };

View File

@@ -77,6 +77,9 @@ private:
m_authNeeded = false; m_authNeeded = false;
} }
void OnCharHook(wxKeyEvent& event);
wxDECLARE_EVENT_TABLE();
wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxButton); wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxButton);
}; };

View File

@@ -1373,13 +1373,19 @@ enum wxKeyCategoryFlags
events and so gives the parent window an opportunity to modify the events and so gives the parent window an opportunity to modify the
keyboard handling of its children, e.g. it is used internally by keyboard handling of its children, e.g. it is used internally by
wxWidgets in some ports to intercept pressing Esc key in any child of a wxWidgets in some ports to intercept pressing Esc key in any child of a
dialog to close the dialog itself when it's pressed. If the event is dialog to close the dialog itself when it's pressed. By default, if
handled, i.e. the handler doesn't call wxEvent::Skip(), neither @c this event is handled, i.e. the handler doesn't call wxEvent::Skip(),
wxEVT_KEY_DOWN nor @c wxEVT_CHAR events will be generated (although @c neither @c wxEVT_KEY_DOWN nor @c wxEVT_CHAR events will be generated
wxEVT_KEY_UP still will be). Notice that this event is not generated (although @c wxEVT_KEY_UP still will be), i.e. it replaces the normal
when the mouse is captured as it is considered that the window which key events. However by calling the special DoAllowNextEvent() method
has the capture should receive all the keyboard events too without you can handle @c wxEVT_CHAR_HOOK and still allow normal events
allowing its parent wxTopLevelWindow to interfere with their processing. generation. This is something that is rarely useful but can be required
if you need to prevent a parent @c wxEVT_CHAR_HOOK handler from running
without suppressing the normal key events. Finally notice that this
event is not generated when the mouse is captured as it is considered
that the window which has the capture should receive all the keyboard
events too without allowing its parent wxTopLevelWindow to interfere
with their processing.
@endEventTable @endEventTable
@see wxKeyboardState @see wxKeyboardState
@@ -1522,6 +1528,34 @@ public:
Returns the Y position (in client coordinates) of the event. Returns the Y position (in client coordinates) of the event.
*/ */
wxCoord GetY() const; wxCoord GetY() const;
/**
Allow normal key events generation.
Can be called from @c wxEVT_CHAR_HOOK handler to indicate that the
generation of normal events should @em not be suppressed, as it happens
by default when this event is handled.
The intended use of this method is to allow some window object to
prevent @c wxEVT_CHAR_HOOK handler in its parent window from running by
defining its own handler for this event. Without calling this method,
this would result in not generating @c wxEVT_KEY_DOWN nor @c wxEVT_CHAR
events at all but by calling it you can ensure that these events would
still be generated, even if @c wxEVT_CHAR_HOOK event was handled.
@since 2.9.3
*/
void DoAllowNextEvent();
/**
Returns @true if DoAllowNextEvent() had been called, @false by default.
This method is used by wxWidgets itself to determine whether the normal
key events should be generated after @c wxEVT_CHAR_HOOK processing.
@since 2.9.3
*/
bool IsNextEventAllowed() const;
}; };

View File

@@ -830,7 +830,8 @@ bool SendCharHookEvent(const wxKeyEvent& event, wxWindow *win)
if ( !g_captureWindow ) if ( !g_captureWindow )
{ {
wxKeyEvent eventCharHook(wxEVT_CHAR_HOOK, event); wxKeyEvent eventCharHook(wxEVT_CHAR_HOOK, event);
if ( win->HandleWindowEvent(eventCharHook) ) if ( win->HandleWindowEvent(eventCharHook)
&& !event.IsNextEventAllowed() )
return true; return true;
} }

View File

@@ -62,6 +62,10 @@
// macros // macros
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
BEGIN_EVENT_TABLE(wxButton, wxButtonBase)
EVT_CHAR_HOOK(wxButton::OnCharHook)
END_EVENT_TABLE()
// ============================================================================ // ============================================================================
// implementation // implementation
// ============================================================================ // ============================================================================
@@ -370,6 +374,25 @@ void wxButton::Command(wxCommandEvent & event)
// event/message handlers // event/message handlers
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void wxButton::OnCharHook(wxKeyEvent& event)
{
// We want to ensure that the button always processes Enter key events
// itself, even if it's inside some control that normally takes over them
// (this happens when the button is part of an in-place editor control for
// example).
if ( event.GetKeyCode() == WXK_RETURN )
{
// We should ensure that subsequent key events are still generated even
// if we did handle EVT_CHAR_HOOK (normally this would suppress their
// generation).
event.DoAllowNextEvent();
}
else
{
event.Skip();
}
}
bool wxButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id)) bool wxButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
{ {
bool processed = false; bool processed = false;

View File

@@ -6644,12 +6644,15 @@ wxKeyboardHook(int nCode, WORD wParam, DWORD lParam)
if ( handler && handler->ProcessEvent(event) ) if ( handler && handler->ProcessEvent(event) )
{ {
// processed if ( !event.IsNextEventAllowed() )
{
// Stop processing of this event.
return 1; return 1;
} }
} }
} }
} }
}
return (int)CallNextHookEx(wxTheKeyboardHook, nCode, wParam, lParam); return (int)CallNextHookEx(wxTheKeyboardHook, nCode, wParam, lParam);
} }

View File

@@ -1650,7 +1650,7 @@ bool wxApp::MacSendCharEvent( wxWindow* focus , long keymessage , long modifiers
{ {
wxKeyEvent eventCharHook(wxEVT_CHAR_HOOK, event); wxKeyEvent eventCharHook(wxEVT_CHAR_HOOK, event);
handled = focus->HandleWindowEvent( eventCharHook ); handled = focus->HandleWindowEvent( eventCharHook );
if ( handled && eventCharHook.GetSkipped() ) if ( handled && eventCharHook.IsNextEventAllowed() )
handled = false ; handled = false ;
} }

View File

@@ -2306,7 +2306,8 @@ bool wxWidgetCocoaImpl::DoHandleKeyEvent(NSEvent *event)
if ( wxevent.GetEventType() == wxEVT_KEY_DOWN ) if ( wxevent.GetEventType() == wxEVT_KEY_DOWN )
{ {
wxKeyEvent eventHook(wxEVT_CHAR_HOOK, wxevent); wxKeyEvent eventHook(wxEVT_CHAR_HOOK, wxevent);
if ( GetWXPeer()->OSXHandleKeyEvent(eventHook) ) if ( GetWXPeer()->OSXHandleKeyEvent(eventHook)
&& !eventHook.IsNextEventAllowed() )
return true; return true;
} }