Fix event handling order in doc/view framework.
Ensure that the events are always (provided there is an open document) processed in the following order: 1. wxDocument 2. wxView 3. wxDocManager 4. wxDocChildFrame 5. wxDocParentFrame 6. wxApp Do this by forwarding the events from wxDocParentFrame to wxDocChildFrame first and forward them from there to wxDocManager which -- and this part remains unchanged -- in turn forwards them to the active wxView which finally forwards them to wxDocument. This requires another condition in the event handling code as we still must forward from wxDocParentFrame to wxDocManager itself if there are no active children at all, but this is the only way to have the same event order in all cases, whether the event is originally received by wxDocChildFrame or wxDocParentFrame. Document this and add a unit test verifying that things indeed work like this. See #14314. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@73928 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
#include "wx/window.h"
|
||||
#endif // WX_PRECOMP
|
||||
|
||||
#include "wx/docmdi.h"
|
||||
#include "wx/frame.h"
|
||||
#include "wx/menu.h"
|
||||
#include "wx/scopedptr.h"
|
||||
@@ -100,6 +101,26 @@ struct TestPaintEvtHandler : TestEvtHandlerBase<wxPaintEvent>
|
||||
}
|
||||
};
|
||||
|
||||
// Another custom event handler, suitable for use with Connect().
|
||||
struct TestEvtSink : wxEvtHandler
|
||||
{
|
||||
TestEvtSink(char tag)
|
||||
: m_tag(tag)
|
||||
{
|
||||
}
|
||||
|
||||
void Handle(wxEvent& event)
|
||||
{
|
||||
g_str += m_tag;
|
||||
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
const char m_tag;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(TestEvtSink);
|
||||
};
|
||||
|
||||
// a window handling the test event
|
||||
class TestWindow : public wxWindow
|
||||
{
|
||||
@@ -195,6 +216,7 @@ private:
|
||||
CPPUNIT_TEST( ScrollWindowWithoutHandler );
|
||||
CPPUNIT_TEST( ScrollWindowWithHandler );
|
||||
CPPUNIT_TEST( MenuEvent );
|
||||
CPPUNIT_TEST( DocView );
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
void OneHandler();
|
||||
@@ -205,6 +227,7 @@ private:
|
||||
void ScrollWindowWithoutHandler();
|
||||
void ScrollWindowWithHandler();
|
||||
void MenuEvent();
|
||||
void DocView();
|
||||
|
||||
DECLARE_NO_COPY_CLASS(EventPropagationTestCase)
|
||||
};
|
||||
@@ -434,3 +457,95 @@ void EventPropagationTestCase::MenuEvent()
|
||||
|
||||
ASSERT_MENU_EVENT_RESULT( menu, "aomobowA" );
|
||||
}
|
||||
|
||||
// Minimal viable implementations of wxDocument and wxView.
|
||||
class EventTestDocument : public wxDocument
|
||||
{
|
||||
public:
|
||||
EventTestDocument() { }
|
||||
|
||||
wxDECLARE_DYNAMIC_CLASS(EventTestDocument);
|
||||
};
|
||||
|
||||
class EventTestView : public wxView
|
||||
{
|
||||
public:
|
||||
EventTestView() { }
|
||||
|
||||
virtual void OnDraw(wxDC*) { }
|
||||
|
||||
wxDECLARE_DYNAMIC_CLASS(EventTestView);
|
||||
};
|
||||
|
||||
wxIMPLEMENT_DYNAMIC_CLASS(EventTestDocument, wxDocument);
|
||||
wxIMPLEMENT_DYNAMIC_CLASS(EventTestView, wxView);
|
||||
|
||||
void EventPropagationTestCase::DocView()
|
||||
{
|
||||
// Set up the parent frame and its menu bar.
|
||||
wxDocManager docManager;
|
||||
|
||||
wxScopedPtr<wxDocMDIParentFrame>
|
||||
parent(new wxDocMDIParentFrame(&docManager, NULL, wxID_ANY, "Parent"));
|
||||
|
||||
wxMenu* const menu = CreateTestMenu(parent.get());
|
||||
|
||||
|
||||
// Set up the event handlers.
|
||||
TestEvtSink sinkDM('m');
|
||||
docManager.Connect(wxEVT_MENU,
|
||||
wxEventHandler(TestEvtSink::Handle), NULL, &sinkDM);
|
||||
|
||||
TestEvtSink sinkParent('p');
|
||||
parent->Connect(wxEVT_MENU,
|
||||
wxEventHandler(TestEvtSink::Handle), NULL, &sinkParent);
|
||||
|
||||
|
||||
// Check that wxDocManager and wxFrame get the event in order.
|
||||
ASSERT_MENU_EVENT_RESULT( menu, "ampA" );
|
||||
|
||||
|
||||
// Now check what happens if we have an active document.
|
||||
wxDocTemplate docTemplate(&docManager, "Test", "", "", "",
|
||||
"Test Document", "Test View",
|
||||
wxCLASSINFO(EventTestDocument),
|
||||
wxCLASSINFO(EventTestView));
|
||||
wxDocument* const doc = docTemplate.CreateDocument("");
|
||||
wxView* const view = doc->GetFirstView();
|
||||
|
||||
wxScopedPtr<wxFrame>
|
||||
child(new wxDocMDIChildFrame(doc, view, parent.get(), wxID_ANY, "Child"));
|
||||
|
||||
wxMenu* const menuChild = CreateTestMenu(child.get());
|
||||
|
||||
#ifdef __WXGTK__
|
||||
// There are a lot of hacks related to child frame menu bar handling in
|
||||
// wxGTK and, in particular, the code in src/gtk/mdi.cpp relies on getting
|
||||
// idle events to really put everything in place. Moreover, as wxGTK uses
|
||||
// GtkNotebook as its MDI pages container, the frame must be shown for all
|
||||
// this to work as gtk_notebook_set_current_page() doesn't do anything if
|
||||
// called for a hidden window (this incredible fact cost me quite some time
|
||||
// to find empirically -- only to notice its confirmation in GTK+
|
||||
// documentation immediately afterwards). So just do whatever it takes to
|
||||
// make things work "as usual".
|
||||
child->Show();
|
||||
parent->Show();
|
||||
wxYield();
|
||||
#endif // __WXGTK__
|
||||
|
||||
TestEvtSink sinkDoc('d');
|
||||
doc->Connect(wxEVT_MENU,
|
||||
wxEventHandler(TestEvtSink::Handle), NULL, &sinkDoc);
|
||||
|
||||
TestEvtSink sinkView('v');
|
||||
view->Connect(wxEVT_MENU,
|
||||
wxEventHandler(TestEvtSink::Handle), NULL, &sinkView);
|
||||
|
||||
TestEvtSink sinkChild('c');
|
||||
child->Connect(wxEVT_MENU,
|
||||
wxEventHandler(TestEvtSink::Handle), NULL, &sinkChild);
|
||||
|
||||
// Check that wxDocument, wxView, wxDocManager, child frame and the parent
|
||||
// get the event in order.
|
||||
ASSERT_MENU_EVENT_RESULT( menuChild, "advmcpA" );
|
||||
}
|
||||
|
Reference in New Issue
Block a user