After the recent changes to the event processing logic, forwarding an event from one event handler to another one stopped working correctly because the per-event "process here only" flag prevented it from following the event handler chain after forwarding. This notably broke keyboard navigation in wxComboCtrl under MSW in wx itself and probably quite a few of other things in user code. Fix this by replacing the boolean flag with a pointer to the handler to which the processing of this event should be restricted. This allows the full processing to still take place if an event is forwarded to another handler. So wxEvent::ShouldProcessHereOnly() is now called ShouldProcessOnlyIn() and takes a wxEvtHandler parameter. This made appear a problem in wxScrollHelperEvtHandler code that was hidden by the bug above: the events were still processed multiple times in it. To fix this, also add wxEvent::DidntHonourProcessOnlyIn() and take it into account in the base class code. Did I mention that wxScrollHelperEvtHandler must die? Add another unit test checking that forwarding works correctly. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@64464 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
347 lines
9.0 KiB
C++
347 lines
9.0 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Name: tests/events/propagation.cpp
|
|
// Purpose: Test events propagation
|
|
// Author: Vadim Zeitlin
|
|
// Created: 2009-01-16
|
|
// RCS-ID: $Id$
|
|
// Copyright: (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org>
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#include "testprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/app.h"
|
|
#include "wx/event.h"
|
|
#include "wx/scrolwin.h"
|
|
#include "wx/window.h"
|
|
#endif // WX_PRECOMP
|
|
|
|
#include "wx/scopeguard.h"
|
|
|
|
namespace
|
|
{
|
|
|
|
// this string will record the execution of all handlers
|
|
wxString g_str;
|
|
|
|
// a custom event
|
|
wxDEFINE_EVENT(TEST_EVT, wxCommandEvent);
|
|
|
|
// a custom event handler tracing the propagation of the events of the
|
|
// specified types
|
|
template <class Event>
|
|
class TestEvtHandlerBase : public wxEvtHandler
|
|
{
|
|
public:
|
|
TestEvtHandlerBase(wxEventType evtType, char tag)
|
|
: m_evtType(evtType),
|
|
m_tag(tag)
|
|
{
|
|
Connect(evtType,
|
|
static_cast<wxEventFunction>(&TestEvtHandlerBase::OnTest));
|
|
}
|
|
|
|
// override ProcessEvent() to confirm that it is called for all event
|
|
// handlers in the chain
|
|
virtual bool ProcessEvent(wxEvent& event)
|
|
{
|
|
if ( event.GetEventType() == m_evtType )
|
|
g_str += 'o'; // "o" == "overridden"
|
|
|
|
return wxEvtHandler::ProcessEvent(event);
|
|
}
|
|
|
|
private:
|
|
void OnTest(wxEvent& event)
|
|
{
|
|
g_str += m_tag;
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
const wxEventType m_evtType;
|
|
const char m_tag;
|
|
|
|
wxDECLARE_NO_COPY_TEMPLATE_CLASS(TestEvtHandlerBase, Event);
|
|
};
|
|
|
|
struct TestEvtHandler : TestEvtHandlerBase<wxCommandEvent>
|
|
{
|
|
TestEvtHandler(char tag)
|
|
: TestEvtHandlerBase<wxCommandEvent>(TEST_EVT, tag)
|
|
{
|
|
}
|
|
};
|
|
|
|
struct TestPaintEvtHandler : TestEvtHandlerBase<wxPaintEvent>
|
|
{
|
|
TestPaintEvtHandler(char tag)
|
|
: TestEvtHandlerBase<wxPaintEvent>(wxEVT_PAINT, tag)
|
|
{
|
|
}
|
|
};
|
|
|
|
// a window handling the test event
|
|
class TestWindow : public wxWindow
|
|
{
|
|
public:
|
|
TestWindow(wxWindow *parent, char tag)
|
|
: wxWindow(parent, wxID_ANY),
|
|
m_tag(tag)
|
|
{
|
|
Connect(TEST_EVT, wxCommandEventHandler(TestWindow::OnTest));
|
|
}
|
|
|
|
private:
|
|
void OnTest(wxCommandEvent& event)
|
|
{
|
|
g_str += m_tag;
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
const char m_tag;
|
|
|
|
DECLARE_NO_COPY_CLASS(TestWindow)
|
|
};
|
|
|
|
// a scroll window handling paint event: we want to have a special test case
|
|
// for this because the event propagation is complicated even further than
|
|
// usual here by the presence of wxScrollHelperEvtHandler in the event handlers
|
|
// chain and the fact that OnDraw() virtual method must be called if EVT_PAINT
|
|
// is not handled
|
|
class TestScrollWindow : public wxScrolledWindow
|
|
{
|
|
public:
|
|
TestScrollWindow(wxWindow *parent)
|
|
: wxScrolledWindow(parent, wxID_ANY)
|
|
{
|
|
Connect(wxEVT_PAINT, wxPaintEventHandler(TestScrollWindow::OnPaint));
|
|
}
|
|
|
|
virtual void OnDraw(wxDC& WXUNUSED(dc))
|
|
{
|
|
g_str += 'D'; // draw
|
|
}
|
|
|
|
private:
|
|
void OnPaint(wxPaintEvent& event)
|
|
{
|
|
g_str += 'P'; // paint
|
|
event.Skip();
|
|
}
|
|
|
|
wxDECLARE_NO_COPY_CLASS(TestScrollWindow);
|
|
};
|
|
|
|
int DoFilterEvent(wxEvent& event)
|
|
{
|
|
if ( event.GetEventType() == TEST_EVT )
|
|
g_str += 'a';
|
|
|
|
return -1;
|
|
}
|
|
|
|
bool DoProcessEvent(wxEvent& event)
|
|
{
|
|
if ( event.GetEventType() == TEST_EVT )
|
|
g_str += 'A';
|
|
|
|
return false;
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
// --------------------------------------------------------------------------
|
|
// test class
|
|
// --------------------------------------------------------------------------
|
|
|
|
class EventPropagationTestCase : public CppUnit::TestCase
|
|
{
|
|
public:
|
|
EventPropagationTestCase() {}
|
|
|
|
virtual void setUp();
|
|
virtual void tearDown();
|
|
|
|
private:
|
|
CPPUNIT_TEST_SUITE( EventPropagationTestCase );
|
|
CPPUNIT_TEST( OneHandler );
|
|
CPPUNIT_TEST( TwoHandlers );
|
|
CPPUNIT_TEST( WindowWithoutHandler );
|
|
CPPUNIT_TEST( WindowWithHandler );
|
|
CPPUNIT_TEST( ForwardEvent );
|
|
CPPUNIT_TEST( ScrollWindowWithoutHandler );
|
|
CPPUNIT_TEST( ScrollWindowWithHandler );
|
|
CPPUNIT_TEST_SUITE_END();
|
|
|
|
void OneHandler();
|
|
void TwoHandlers();
|
|
void WindowWithoutHandler();
|
|
void WindowWithHandler();
|
|
void ForwardEvent();
|
|
void ScrollWindowWithoutHandler();
|
|
void ScrollWindowWithHandler();
|
|
|
|
DECLARE_NO_COPY_CLASS(EventPropagationTestCase)
|
|
};
|
|
|
|
// register in the unnamed registry so that these tests are run by default
|
|
CPPUNIT_TEST_SUITE_REGISTRATION( EventPropagationTestCase );
|
|
|
|
// also include in it's own registry so that these tests can be run alone
|
|
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( EventPropagationTestCase, "EventPropagationTestCase" );
|
|
|
|
void EventPropagationTestCase::setUp()
|
|
{
|
|
SetFilterEventFunc(DoFilterEvent);
|
|
SetProcessEventFunc(DoProcessEvent);
|
|
|
|
g_str.clear();
|
|
}
|
|
|
|
void EventPropagationTestCase::tearDown()
|
|
{
|
|
SetFilterEventFunc(NULL);
|
|
SetProcessEventFunc(NULL);
|
|
}
|
|
|
|
void EventPropagationTestCase::OneHandler()
|
|
{
|
|
wxCommandEvent event(TEST_EVT);
|
|
TestEvtHandler h1('1');
|
|
h1.ProcessEvent(event);
|
|
CPPUNIT_ASSERT_EQUAL( "oa1A", g_str );
|
|
}
|
|
|
|
void EventPropagationTestCase::TwoHandlers()
|
|
{
|
|
wxCommandEvent event(TEST_EVT);
|
|
TestEvtHandler h1('1');
|
|
TestEvtHandler h2('2');
|
|
h1.SetNextHandler(&h2);
|
|
h2.SetPreviousHandler(&h1);
|
|
h1.ProcessEvent(event);
|
|
CPPUNIT_ASSERT_EQUAL( "oa1o2A", g_str );
|
|
}
|
|
|
|
void EventPropagationTestCase::WindowWithoutHandler()
|
|
{
|
|
wxCommandEvent event(TEST_EVT);
|
|
TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
|
|
wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
|
|
|
|
TestWindow * const child = new TestWindow(parent, 'c');
|
|
|
|
child->GetEventHandler()->ProcessEvent(event);
|
|
CPPUNIT_ASSERT_EQUAL( "acpA", g_str );
|
|
}
|
|
|
|
void EventPropagationTestCase::WindowWithHandler()
|
|
{
|
|
wxCommandEvent event(TEST_EVT);
|
|
TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
|
|
wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
|
|
|
|
TestWindow * const child = new TestWindow(parent, 'c');
|
|
|
|
TestEvtHandler h1('1');
|
|
child->PushEventHandler(&h1);
|
|
wxON_BLOCK_EXIT_OBJ1( *child, wxWindow::PopEventHandler, false );
|
|
TestEvtHandler h2('2');
|
|
child->PushEventHandler(&h2);
|
|
wxON_BLOCK_EXIT_OBJ1( *child, wxWindow::PopEventHandler, false );
|
|
|
|
child->HandleWindowEvent(event);
|
|
CPPUNIT_ASSERT_EQUAL( "oa2o1cpA", g_str );
|
|
}
|
|
|
|
void EventPropagationTestCase::ForwardEvent()
|
|
{
|
|
// The idea of this test is to check that the events explicitly forwarded
|
|
// to another event handler still get pre/post-processed as usual as this
|
|
// used to be broken by the fixes trying to avoid duplicate processing.
|
|
TestWindow * const win = new TestWindow(wxTheApp->GetTopWindow(), 'w');
|
|
wxON_BLOCK_EXIT_OBJ0( *win, wxWindow::Destroy );
|
|
|
|
TestEvtHandler h1('1');
|
|
win->PushEventHandler(&h1);
|
|
wxON_BLOCK_EXIT_OBJ1( *win, wxWindow::PopEventHandler, false );
|
|
|
|
class ForwardEvtHandler : public wxEvtHandler
|
|
{
|
|
public:
|
|
ForwardEvtHandler(wxEvtHandler& h) : m_h(&h) { }
|
|
|
|
virtual bool ProcessEvent(wxEvent& event)
|
|
{
|
|
g_str += 'f';
|
|
|
|
return m_h->ProcessEvent(event);
|
|
}
|
|
|
|
private:
|
|
wxEvtHandler *m_h;
|
|
} f(h1);
|
|
|
|
// First send the event directly to f.
|
|
wxCommandEvent event1(TEST_EVT);
|
|
f.ProcessEvent(event1);
|
|
CPPUNIT_ASSERT_EQUAL( "foa1wA", g_str );
|
|
g_str.clear();
|
|
|
|
// And then also test sending it to f indirectly.
|
|
wxCommandEvent event2(TEST_EVT);
|
|
TestEvtHandler h2('2');
|
|
h2.SetNextHandler(&f);
|
|
h2.ProcessEvent(event2);
|
|
CPPUNIT_ASSERT_EQUAL( "oa2fo1wAA", g_str );
|
|
}
|
|
|
|
void EventPropagationTestCase::ScrollWindowWithoutHandler()
|
|
{
|
|
TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
|
|
wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
|
|
|
|
TestScrollWindow * const win = new TestScrollWindow(parent);
|
|
|
|
wxPaintEvent event(win->GetId());
|
|
win->ProcessWindowEvent(event);
|
|
CPPUNIT_ASSERT_EQUAL( "PD", g_str );
|
|
|
|
g_str.clear();
|
|
wxCommandEvent eventCmd(TEST_EVT);
|
|
win->HandleWindowEvent(eventCmd);
|
|
CPPUNIT_ASSERT_EQUAL( "apA", g_str );
|
|
}
|
|
|
|
void EventPropagationTestCase::ScrollWindowWithHandler()
|
|
{
|
|
TestWindow * const parent = new TestWindow(wxTheApp->GetTopWindow(), 'p');
|
|
wxON_BLOCK_EXIT_OBJ0( *parent, wxWindow::Destroy );
|
|
|
|
TestScrollWindow * const win = new TestScrollWindow(parent);
|
|
|
|
TestPaintEvtHandler h('h');
|
|
win->PushEventHandler(&h);
|
|
wxON_BLOCK_EXIT_OBJ1( *win, wxWindow::PopEventHandler, false );
|
|
|
|
wxPaintEvent event(win->GetId());
|
|
win->ProcessWindowEvent(event);
|
|
CPPUNIT_ASSERT_EQUAL( "ohPD", g_str );
|
|
|
|
g_str.clear();
|
|
wxCommandEvent eventCmd(TEST_EVT);
|
|
win->HandleWindowEvent(eventCmd);
|
|
CPPUNIT_ASSERT_EQUAL( "apA", g_str );
|
|
}
|
|
|