Merge branch 'aui-button-event'

Restore generation of wxEVT_AUINOTEBOOK_BUTTON events.

See #26978.
This commit is contained in:
Vadim Zeitlin
2026-09-10 17:58:22 +02:00
5 changed files with 147 additions and 24 deletions
+5
View File
@@ -149,6 +149,11 @@ Changes in behaviour not resulting in compilation errors
values was previously silently allowed but couldn't work correctly, so now
attempting to do it will assert. Don't set these values for the central pane.
- Handling wxEVT_AUINOTEBOOK_BUTTON events without calling event.Skip() now
consistently prevents the default handling from taking place, for whichever
button that was pressed. Check the button ID and call Skip() if you want the
the default handling to take place.
Changes in behaviour which may result in build errors
-----------------------------------------------------
+5 -1
View File
@@ -666,6 +666,10 @@ protected:
// control: notice that this is _not_ the same as the index of the page, in
// general, m_tabs.GetIdxFromWindow(wxAuiTabCtrl::GetWindowFromIdx()) must
// be used to get it.
//
// They all return void, except OnTabButton() which returns true if the
// application had handled the event generated by it without skipping it,
// in which case the button must not be handled by wxAuiTabCtrl neither.
friend class wxAuiTabEventSource;
void OnTabClicked(wxAuiTabCtrl* ctrl, int tabIdx);
@@ -673,7 +677,7 @@ protected:
void OnTabDragMotion(wxAuiTabCtrl* ctrl, int tabIdx);
void OnTabEndDrag(wxAuiTabCtrl* ctrl, int tabIdx);
void OnTabCancelDrag(wxAuiTabCtrl* ctrl, int tabIdx);
void OnTabButton(wxAuiTabCtrl* ctrl, int tabIdx, int button_id);
bool OnTabButton(wxAuiTabCtrl* ctrl, int tabIdx, int button_id);
void OnTabMiddleDown(wxAuiTabCtrl* ctrl, int tabIdx);
void OnTabMiddleUp(wxAuiTabCtrl* ctrl, int tabIdx);
void OnTabRightDown(wxAuiTabCtrl* ctrl, int tabIdx);
+16 -2
View File
@@ -232,7 +232,14 @@ struct wxAuiNotebookPosition
@event{EVT_AUINOTEBOOK_PAGE_CHANGING(id, func)}
The page selection is about to be changed. Processes a @c wxEVT_AUINOTEBOOK_PAGE_CHANGING event. This event can be vetoed.
@event{EVT_AUINOTEBOOK_BUTTON(id, func)}
The window list button has been pressed. Processes a @c wxEVT_AUINOTEBOOK_BUTTON event.
A button in the tab control, e.g. the close or the window list button,
has been pressed. Processes a @c wxEVT_AUINOTEBOOK_BUTTON event. Use
wxCommandEvent::GetInt() to retrieve the id of the button, which is one
of @c wxAUI_BUTTON_XXX constants. Handling this event without skipping
it prevents the default action associated with the button, e.g. closing
the page for @c wxAUI_BUTTON_CLOSE or showing the list of the pages for
@c wxAUI_BUTTON_WINDOWLIST, from taking place, so the handler must call
wxEvent::Skip() if this is not desired.
@event{EVT_AUINOTEBOOK_BEGIN_DRAG(id, func)}
Dragging is about to begin. Processes a @c wxEVT_AUINOTEBOOK_BEGIN_DRAG event.
@event{EVT_AUINOTEBOOK_END_DRAG(id, func)}
@@ -1323,7 +1330,14 @@ using wxAuiDefaultTabArt = wxAuiFlatTabArt;
@event{EVT_AUINOTEBOOK_PAGE_CHANGING(id, func)}
The page selection is about to be changed. Processes a @c wxEVT_AUINOTEBOOK_PAGE_CHANGING event. This event can be vetoed.
@event{EVT_AUINOTEBOOK_BUTTON(id, func)}
The window list button has been pressed. Processes a @c wxEVT_AUINOTEBOOK_BUTTON event.
A button in the tab control, e.g. the close or the window list button,
has been pressed. Processes a @c wxEVT_AUINOTEBOOK_BUTTON event. Use
wxCommandEvent::GetInt() to retrieve the id of the button, which is one
of @c wxAUI_BUTTON_XXX constants. Handling this event without skipping
it prevents the default action associated with the button, e.g. closing
the page for @c wxAUI_BUTTON_CLOSE or showing the list of the pages for
@c wxAUI_BUTTON_WINDOWLIST, from taking place, so the handler must call
wxEvent::Skip() if this is not desired.
@event{EVT_AUINOTEBOOK_BEGIN_DRAG(id, func)}
Dragging is about to begin. Processes a @c wxEVT_AUINOTEBOOK_BEGIN_DRAG event.
@event{EVT_AUINOTEBOOK_END_DRAG(id, func)}
+50 -21
View File
@@ -120,9 +120,9 @@ private:
ctrl->GetBook()->OnTabCancelDrag(ctrl, tabIdx);
}
static void TabButton(wxAuiTabCtrl* ctrl, int tabIdx, int button_id)
static bool TabButton(wxAuiTabCtrl* ctrl, int tabIdx, int button_id)
{
ctrl->GetBook()->OnTabButton(ctrl, tabIdx, button_id);
return ctrl->GetBook()->OnTabButton(ctrl, tabIdx, button_id);
}
static void TabMiddleDown(wxAuiTabCtrl* ctrl, int tabIdx)
@@ -1704,6 +1704,17 @@ wxAuiTabCtrl::UpdateButtonStateAndRefresh(wxAuiTabContainerButton& button,
void wxAuiTabCtrl::OnButton(int tabIdx, int button)
{
// Let wxAuiNotebook generate the event for this button click and perform
// the default action for the buttons handled at its level, such as closing
// or pinning the tab.
//
// It only returns true the application handled the event without skipping
// it, which means that it handled the button completely on its own.
if (wxAuiTabEventSource::TabButton(this, tabIdx, button))
return;
// For mostly historical (but also convenience) reasons, some buttons are
// handled here instead of in wxAuiNotebook.
if (button == wxAUI_BUTTON_LEFT || button == wxAUI_BUTTON_RIGHT)
{
if (button == wxAUI_BUTTON_LEFT)
@@ -1731,10 +1742,6 @@ void wxAuiTabCtrl::OnButton(int tabIdx, int button)
wxAuiTabEventSource::TabClicked(this, idx);
}
}
else
{
wxAuiTabEventSource::TabButton(this, tabIdx, button);
}
}
void wxAuiTabCtrl::OnSetFocus(wxFocusEvent& WXUNUSED(event))
@@ -3685,32 +3692,50 @@ void wxAuiNotebook::OnNavigationKeyNotebook(wxNavigationKeyEvent& event)
}
}
void wxAuiNotebook::OnTabButton(wxAuiTabCtrl* tabs, int tabIdx, int button_id)
bool wxAuiNotebook::OnTabButton(wxAuiTabCtrl* tabs, int tabIdx, int button_id)
{
int selection = tabIdx;
if (selection == -1 && button_id == wxAUI_BUTTON_CLOSE)
{
// if the close button is to the right, use the active
// page selection to determine which page to close
selection = tabs->GetActivePage();
}
// Notify the application about the button click.
{
wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_BUTTON, m_windowId);
e.SetSelection(selection != -1
? m_tabs.GetIdxFromWindow(tabs->GetWindowFromIdx(selection))
: wxNOT_FOUND
);
e.SetInt(button_id);
e.SetEventObject(this);
// Note that if the application handles this event without skipping
// it, we don't do anything else, neither here nor in wxAuiTabCtrl:
// this is compatible with the previous versions, in which handling
// this event prevented our own handler, executed after the application
// one, from running at all.
if (ProcessWindowEvent(e))
return true;
}
if (button_id == wxAUI_BUTTON_CLOSE)
{
int selection = tabIdx;
if (selection == -1)
{
// if the close button is to the right, use the active
// page selection to determine which page to close
selection = tabs->GetActivePage();
}
if (selection != -1)
{
wxWindow* close_wnd = tabs->GetWindowFromIdx(selection);
// ask owner if it's ok to close the tab
wxAuiNotebookEvent e(wxEVT_AUINOTEBOOK_PAGE_CLOSE, m_windowId);
e.SetSelection(m_tabs.GetIdxFromWindow(close_wnd));
const int idx = m_tabs.GetIdxFromWindow(close_wnd);
e.SetSelection(idx);
e.SetOldSelection(selection);
e.SetEventObject(this);
ProcessWindowEvent(e);
if (!e.IsAllowed())
return;
return false;
#if wxUSE_MDI
@@ -3722,7 +3747,8 @@ void wxAuiNotebook::OnTabButton(wxAuiTabCtrl* tabs, int tabIdx, int button_id)
#endif
{
int main_idx = m_tabs.GetIdxFromWindow(close_wnd);
wxCHECK_RET( main_idx != wxNOT_FOUND, wxT("no page to delete?") );
wxCHECK_MSG( main_idx != wxNOT_FOUND, false,
wxT("no page to delete?") );
DeletePage(main_idx);
}
@@ -3736,8 +3762,9 @@ void wxAuiNotebook::OnTabButton(wxAuiTabCtrl* tabs, int tabIdx, int button_id)
}
else if (button_id == wxAUI_BUTTON_PIN)
{
// For now we don't send any event, this can be always added later if
// necessary.
// Note that we don't send any event specific to pinning the tab, the
// generic button event above is the only notification for it, but such
// event could be always added later if necessary.
wxWindow* const wnd = tabs->GetWindowFromIdx(tabIdx);
const auto idx = m_tabs.GetIdxFromWindow(wnd);
@@ -3758,11 +3785,13 @@ void wxAuiNotebook::OnTabButton(wxAuiTabCtrl* tabs, int tabIdx, int button_id)
break;
}
wxCHECK_RET(newKind != wxAuiTabKind::Locked,
wxCHECK_MSG(newKind != wxAuiTabKind::Locked, false,
"locked pages shouldn't have pin button");
SetPageKind(idx, newKind);
}
return false;
}
+71
View File
@@ -56,6 +56,7 @@ public:
{
}
using wxAuiNotebook::OnTabButton;
using wxAuiNotebook::OnTabMiddleDown;
using wxAuiNotebook::OnTabMiddleUp;
using wxAuiNotebook::OnTabRightDown;
@@ -415,6 +416,76 @@ TEST_CASE("wxAuiNotebook::SplitTabEventSelections", "[aui]")
CHECK( selections[0] == 1 );
}
// This tests for the problem of https://github.com/wxWidgets/wxWidgets/issues/26801
TEST_CASE("wxAuiNotebook::ButtonEvent", "[aui]")
{
TestAuiNotebook nb;
wxPanel *p1 = new wxPanel(&nb);
wxPanel *p2 = new wxPanel(&nb);
REQUIRE( nb.AddPage(p1, "Page 1") );
REQUIRE( nb.AddPage(p2, "Page 2") );
// Split the notebook to check that the event uses the index of the page in
// the notebook and not its position in its own tab control.
nb.Split(1, wxRIGHT);
wxAuiTabCtrl *tabCtrl = nullptr;
int tabIdx = wxNOT_FOUND;
REQUIRE( nb.FindTab(p2, &tabCtrl, &tabIdx) );
REQUIRE( tabCtrl );
REQUIRE( tabIdx == 0 );
int numEvents = 0;
int selection = wxNOT_FOUND;
int button = wxID_NONE;
bool skip = true;
nb.Bind(wxEVT_AUINOTEBOOK_BUTTON,
[&](wxAuiNotebookEvent& event)
{
numEvents++;
selection = event.GetSelection();
button = event.GetInt();
event.Skip(skip);
});
// Note that this event must be skipped by the handler if the default
// action, e.g. closing the page, is still to be performed.
SECTION( "Custom button" )
{
nb.OnTabButton(tabCtrl, tabIdx, wxAUI_BUTTON_CUSTOM1);
CHECK( numEvents == 1 );
CHECK( selection == 1 );
CHECK( button == wxAUI_BUTTON_CUSTOM1 );
}
SECTION( "Close button" )
{
nb.OnTabButton(tabCtrl, tabIdx, wxAUI_BUTTON_CLOSE);
CHECK( numEvents == 1 );
CHECK( selection == 1 );
CHECK( button == wxAUI_BUTTON_CLOSE );
CHECK( nb.GetPageCount() == 1 );
}
SECTION( "Close button not skipped" )
{
skip = false;
nb.OnTabButton(tabCtrl, tabIdx, wxAUI_BUTTON_CLOSE);
CHECK( numEvents == 1 );
// Handling the event without skipping it prevents the page from being
// closed, as this was the case in the previous versions too.
CHECK( nb.GetPageCount() == 2 );
}
}
TEST_CASE_METHOD(AuiNotebookTestCase, "wxAuiNotebook::Layout", "[aui]")
{
const auto addPage = [this](int n)