Better fix for duplicate wxContextMenuEvent generation under MSW.

Fix the bug with multiple wxContextMenuEvent being generated for a single
WM_CONTEXTMENU without breaking context menus for wxTextCtrl (and all the
other native controls). Do this by ensuring that WM_CONTEXTMENU is still
passed to DefWindowProc() if we don't process it instead of just being eaten
completely in any case.

Also add a unit test checking for this bug to ensure it stays fixed.

See #13683.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74329 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2013-07-02 20:24:22 +00:00
parent bdbdb4d181
commit dbd5b2ce42
2 changed files with 85 additions and 19 deletions

View File

@@ -3433,32 +3433,31 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result,
#if !defined(__WXWINCE__) #if !defined(__WXWINCE__)
case WM_CONTEXTMENU: case WM_CONTEXTMENU:
{ {
// As with WM_HELP above, this message is propagated upwards // Ignore the events that are propagated from a child window by
// the parent chain by DefWindowProc() itself, so we should // DefWindowProc(): as wxContextMenuEvent is already propagated
// always mark it as processed to prevent it from doing this // upwards the window hierarchy by us, not doing this would
// as this would result in duplicate calls to event handlers. // result in duplicate events being sent.
processed = true; WXHWND hWnd = (WXHWND)wParam;
if ( hWnd != m_hWnd )
{
wxWindowMSW *win = FindItemByHWND(hWnd);
if ( win && IsDescendant(win) )
{
// We had already generated wxContextMenuEvent when we
// got WM_CONTEXTMENU for that window.
processed = true;
break;
}
}
// we don't convert from screen to client coordinates as // we don't convert from screen to client coordinates as
// the event may be handled by a parent window // the event may be handled by a parent window
wxPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); wxPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU, GetId(), pt); wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU, GetId(), pt);
evtCtx.SetEventObject(this);
// we could have got an event from our child, reflect it back processed = HandleWindowEvent(evtCtx);
// to it if this is the case
wxWindowMSW *win = NULL;
WXHWND hWnd = (WXHWND)wParam;
if ( hWnd != m_hWnd )
{
win = FindItemByHWND(hWnd);
}
if ( !win )
win = this;
evtCtx.SetEventObject(win);
win->HandleWindowEvent(evtCtx);
} }
break; break;
#endif #endif

View File

@@ -29,6 +29,7 @@
#include "wx/menu.h" #include "wx/menu.h"
#include "wx/scopedptr.h" #include "wx/scopedptr.h"
#include "wx/scopeguard.h" #include "wx/scopeguard.h"
#include "wx/uiaction.h"
// FIXME: Currently under OS X testing paint event doesn't work because neither // FIXME: Currently under OS X testing paint event doesn't work because neither
// calling Refresh()+Update() nor even sending wxPaintEvent directly to // calling Refresh()+Update() nor even sending wxPaintEvent directly to
@@ -242,6 +243,7 @@ private:
CPPUNIT_TEST( ScrollWindowWithHandler ); CPPUNIT_TEST( ScrollWindowWithHandler );
CPPUNIT_TEST( MenuEvent ); CPPUNIT_TEST( MenuEvent );
CPPUNIT_TEST( DocView ); CPPUNIT_TEST( DocView );
WXUISIM_TEST( ContextMenuEvent );
CPPUNIT_TEST_SUITE_END(); CPPUNIT_TEST_SUITE_END();
void OneHandler(); void OneHandler();
@@ -253,6 +255,7 @@ private:
void ScrollWindowWithHandler(); void ScrollWindowWithHandler();
void MenuEvent(); void MenuEvent();
void DocView(); void DocView();
void ContextMenuEvent();
DECLARE_NO_COPY_CLASS(EventPropagationTestCase) DECLARE_NO_COPY_CLASS(EventPropagationTestCase)
}; };
@@ -573,3 +576,67 @@ void EventPropagationTestCase::DocView()
// get the event in order. // get the event in order.
ASSERT_MENU_EVENT_RESULT( menuChild, "advmcpA" ); ASSERT_MENU_EVENT_RESULT( menuChild, "advmcpA" );
} }
#if wxUSE_UIACTIONSIMULATOR
class ContextMenuTestWindow : public wxWindow
{
public:
ContextMenuTestWindow(wxWindow *parent, char tag)
: wxWindow(parent, wxID_ANY),
m_tag(tag)
{
Connect(wxEVT_CONTEXT_MENU,
wxContextMenuEventHandler(ContextMenuTestWindow::OnMenu));
}
private:
void OnMenu(wxContextMenuEvent& event)
{
g_str += m_tag;
event.Skip();
}
const char m_tag;
wxDECLARE_NO_COPY_CLASS(ContextMenuTestWindow);
};
void EventPropagationTestCase::ContextMenuEvent()
{
ContextMenuTestWindow * const
parent = new ContextMenuTestWindow(wxTheApp->GetTopWindow(), 'p');
wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
ContextMenuTestWindow * const
child = new ContextMenuTestWindow(parent, 'c');
parent->SetSize(100, 100);
child->SetSize(0, 0, 50, 50);
child->SetFocus();
wxUIActionSimulator sim;
const wxPoint origin = parent->ClientToScreen(wxPoint(0, 0));
// Right clicking in the child should generate an event for it and the
// parent.
g_str.clear();
sim.MouseMove(origin + wxPoint(10, 10));
sim.MouseClick(wxMOUSE_BTN_RIGHT);
// At least with MSW, for WM_CONTEXTMENU to be synthesized by the system
// from the right mouse click event, we must dispatch the mouse messages.
wxYield();
CPPUNIT_ASSERT_EQUAL( "cp", g_str );
// Right clicking outside the child should generate the event just in the
// parent.
g_str.clear();
sim.MouseMove(origin + wxPoint(60, 60));
sim.MouseClick(wxMOUSE_BTN_RIGHT);
wxYield();
CPPUNIT_ASSERT_EQUAL( "p", g_str );
}
#endif // wxUSE_UIACTIONSIMULATOR