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:
@@ -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.
|
||||||
|
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;
|
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
|
||||||
|
@@ -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
|
||||||
|
Reference in New Issue
Block a user