Fix handling events from their items in submenu itself
This previously worked in wxGTK, but not in wxMSW and even under wxGTK it could be surprising that the submenu got the event, but its parent menu did not. Make things consistent between the platforms and send the event to the menu directly containing it first, but then also to its parent menu(s). Document the new behaviour and verify that it works as intended with a new unit test. Closes #18202.
This commit is contained in:
@@ -112,6 +112,7 @@ All (GUI):
|
|||||||
- Add support for style="page-break-inside:avoid" to <div> in wxHTML.
|
- Add support for style="page-break-inside:avoid" to <div> in wxHTML.
|
||||||
- Support strike-through in wxDataViewItem attributes (approach, Igor Korot).
|
- Support strike-through in wxDataViewItem attributes (approach, Igor Korot).
|
||||||
- Allow distinguishing between user- and script-opened windows in wxWebView.
|
- Allow distinguishing between user- and script-opened windows in wxWebView.
|
||||||
|
- Allow binding to events generated by their items in submenus too.
|
||||||
|
|
||||||
wxGTK:
|
wxGTK:
|
||||||
|
|
||||||
|
@@ -482,18 +482,20 @@ public:
|
|||||||
|
|
||||||
@section menu_eventhandling Event handling
|
@section menu_eventhandling Event handling
|
||||||
|
|
||||||
If the menu is part of a menubar, then wxMenuBar event processing is used.
|
Event handlers for the commands generated by the menu items can be
|
||||||
|
connected directly to the menu object itself using wxEvtHandler::Bind(). If
|
||||||
|
this menu is a submenu of another one, the events from its items can also
|
||||||
|
be processed in the parent menu and so on, recursively.
|
||||||
|
|
||||||
With a popup menu (see wxWindow::PopupMenu), there is a variety of ways to
|
If the menu is part of a menu bar, then events can also be handled in
|
||||||
handle a menu selection event (@c wxEVT_MENU):
|
wxMenuBar object.
|
||||||
- Provide @c EVT_MENU handlers in the window which pops up the menu, or in an
|
|
||||||
ancestor of that window (the simplest method);
|
|
||||||
- Derive a new class from wxMenu and define event table entries using the @c EVT_MENU macro;
|
|
||||||
- Set a new event handler for wxMenu, through wxEvtHandler::SetNextHandler,
|
|
||||||
specifying an object whose class has @c EVT_MENU entries;
|
|
||||||
|
|
||||||
Note that instead of static @c EVT_MENU macros you can also use dynamic
|
Finally, menu events can also be handled in the associated window, which is
|
||||||
connection; see @ref overview_events_bind.
|
either the wxFrame associated with the menu bar this menu belongs to or the
|
||||||
|
window for which wxWindow::PopupMenu() was called for the popup menus.
|
||||||
|
|
||||||
|
See @ref overview_events_bind for how to bind event handlers to the various
|
||||||
|
objects.
|
||||||
|
|
||||||
@library{wxcore}
|
@library{wxcore}
|
||||||
@category{menus}
|
@category{menus}
|
||||||
|
@@ -648,11 +648,12 @@ bool wxMenuBase::DoProcessEvent(wxMenuBase* menu, wxEvent& event, wxWindow* win)
|
|||||||
{
|
{
|
||||||
event.SetEventObject(menu);
|
event.SetEventObject(menu);
|
||||||
|
|
||||||
if ( menu )
|
wxMenuBar* const mb = menu ? menu->GetMenuBar() : NULL;
|
||||||
{
|
|
||||||
wxMenuBar* const mb = menu->GetMenuBar();
|
|
||||||
|
|
||||||
// Try the menu's event handler first
|
// Process event in the menu itself and all its parent menus, if it's a
|
||||||
|
// submenu, first.
|
||||||
|
for ( ; menu; menu = menu->GetParent() )
|
||||||
|
{
|
||||||
wxEvtHandler *handler = menu->GetEventHandler();
|
wxEvtHandler *handler = menu->GetEventHandler();
|
||||||
if ( handler )
|
if ( handler )
|
||||||
{
|
{
|
||||||
@@ -666,19 +667,19 @@ bool wxMenuBase::DoProcessEvent(wxMenuBase* menu, wxEvent& event, wxWindow* win)
|
|||||||
if ( handler->SafelyProcessEvent(event) )
|
if ( handler->SafelyProcessEvent(event) )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If this menu is part of the menu bar, try the event there. this
|
// If this menu is part of the menu bar, try the event there.
|
||||||
if ( mb )
|
if ( mb )
|
||||||
{
|
{
|
||||||
if ( mb->HandleWindowEvent(event) )
|
if ( mb->HandleWindowEvent(event) )
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// If this already propagated it upwards to the window containing
|
// If this already propagated it upwards to the window containing
|
||||||
// the menu bar, we don't have to handle it in this window again
|
// the menu bar, we don't have to handle it in this window again
|
||||||
// below.
|
// below.
|
||||||
if ( event.ShouldPropagate() )
|
if ( event.ShouldPropagate() )
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try the window the menu was popped up from.
|
// Try the window the menu was popped up from.
|
||||||
|
@@ -795,7 +795,7 @@ bool wxMenu::MSWCommand(WXUINT WXUNUSED(param), WXWORD id_)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SendEvent(id, checked);
|
item->GetMenu()->SendEvent(id, checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@@ -445,11 +445,14 @@ wxMenu* CreateTestMenu(wxFrame* frame)
|
|||||||
// reliable than using wxUIActionSimulator and currently works in all ports as
|
// reliable than using wxUIActionSimulator and currently works in all ports as
|
||||||
// they all call wxMenuBase::SendEvent() from their respective menu event
|
// they all call wxMenuBase::SendEvent() from their respective menu event
|
||||||
// handlers.
|
// handlers.
|
||||||
#define ASSERT_MENU_EVENT_RESULT(menu, result) \
|
#define ASSERT_MENU_EVENT_RESULT_FOR(cmd, menu, result) \
|
||||||
g_str.clear(); \
|
g_str.clear(); \
|
||||||
menu->SendEvent(wxID_APPLY); \
|
menu->SendEvent(cmd); \
|
||||||
CHECK( g_str == result )
|
CHECK( g_str == result )
|
||||||
|
|
||||||
|
#define ASSERT_MENU_EVENT_RESULT(menu, result) \
|
||||||
|
ASSERT_MENU_EVENT_RESULT_FOR(wxID_APPLY, menu, result)
|
||||||
|
|
||||||
void EventPropagationTestCase::MenuEvent()
|
void EventPropagationTestCase::MenuEvent()
|
||||||
{
|
{
|
||||||
wxFrame* const frame = static_cast<wxFrame*>(wxTheApp->GetTopWindow());
|
wxFrame* const frame = static_cast<wxFrame*>(wxTheApp->GetTopWindow());
|
||||||
@@ -472,6 +475,17 @@ void EventPropagationTestCase::MenuEvent()
|
|||||||
ASSERT_MENU_EVENT_RESULT( menu, "aomA" );
|
ASSERT_MENU_EVENT_RESULT( menu, "aomA" );
|
||||||
|
|
||||||
|
|
||||||
|
// Check that a handler can also be attached to a submenu.
|
||||||
|
wxMenu* const submenu = new wxMenu;
|
||||||
|
submenu->Append(wxID_ABOUT);
|
||||||
|
menu->Append(wxID_ANY, "Submenu", submenu);
|
||||||
|
|
||||||
|
TestMenuEvtHandler hs('s'); // 's' for "submenu"
|
||||||
|
submenu->SetNextHandler(&hs);
|
||||||
|
wxON_BLOCK_EXIT_OBJ1( *submenu,
|
||||||
|
wxEvtHandler::SetNextHandler, (wxEvtHandler*)NULL );
|
||||||
|
ASSERT_MENU_EVENT_RESULT_FOR( wxID_ABOUT, submenu, "aosomA" );
|
||||||
|
|
||||||
// Test that the event handler associated with the menu bar gets the event.
|
// Test that the event handler associated with the menu bar gets the event.
|
||||||
TestMenuEvtHandler hb('b'); // 'b' for "menu Bar"
|
TestMenuEvtHandler hb('b'); // 'b' for "menu Bar"
|
||||||
mb->PushEventHandler(&hb);
|
mb->PushEventHandler(&hb);
|
||||||
|
Reference in New Issue
Block a user