Delete the replaced card menu in TabGame to fix menu rebuild leak

PlayerMenu creates a new CardMenu on every right-click and TabGame installs
it with QAction::setMenu(), which does not take ownership, so the previous
menu was never freed. TabGame now deletes the menu it replaces and reuses a
single empty placeholder menu instead of allocating one per update. The
installed menu is also released when the tab is destroyed.
This commit is contained in:
DawnFire42 2026-08-10 17:18:13 -04:00 • committed by Lukas Brübach
parent 7b22533646
commit 46a97bf39e
2 changed files with 20 additions and 6 deletions

View file

@ -330,6 +330,9 @@ void TabGame::emitUserEvent()
TabGame::~TabGame() TabGame::~TabGame()
{ {
if (aCardMenu && aCardMenu->menu() && aCardMenu->menu() != emptyCardMenu) {
delete aCardMenu->menu();
}
for (auto &player : game->getPlayerManager()->getPlayers()) { for (auto &player : game->getPlayerManager()->getPlayers()) {
player->clear(); player->clear();
} }
@ -1158,10 +1161,16 @@ QString TabGame::getTabText() const
/** /**
* @param menu The menu to set. Pass in nullptr to set the menu to empty. * @param menu The menu to set. Pass in nullptr to set the menu to empty.
*
* The previous card menu is deleted here: QAction::setMenu() does not take ownership, so whoever installs the menu
* (this widget) has to dispose of the one it replaces. The empty placeholder menu is reused instead of deleted.
*/ */
void TabGame::setCardMenu(CardMenu *menu) void TabGame::setCardMenu(CardMenu *menu)
{ {
if (!aCardMenu) { if (!aCardMenu || !emptyCardMenu) {
if (menu) {
menu->deleteLater();
}
return; return;
} }
@ -1171,10 +1180,11 @@ void TabGame::setCardMenu(CardMenu *menu)
gameView->viewport()->update(); gameView->viewport()->update();
} }
} }
if (menu) {
aCardMenu->setMenu(menu); QMenu *previousMenu = aCardMenu->menu();
} else { aCardMenu->setMenu(menu ? menu : emptyCardMenu);
aCardMenu->setMenu(new QMenu); if (previousMenu && previousMenu != emptyCardMenu && previousMenu != menu) {
previousMenu->deleteLater();
} }
} }
@ -1258,7 +1268,8 @@ void TabGame::createMenuItems()
gameMenu->addSeparator(); gameMenu->addSeparator();
aCardMenu = gameMenu->addMenu(new QMenu(this)); emptyCardMenu = new QMenu(this);
aCardMenu = gameMenu->addMenu(emptyCardMenu);
// Sync the new action with the same state the dock button already shows. // Sync the new action with the same state the dock button already shows.
updateInviteButtonState(); updateInviteButtonState();
@ -1290,6 +1301,7 @@ void TabGame::createReplayMenuItems()
gameMenu->addAction(aLeaveGame); gameMenu->addAction(aLeaveGame);
aCardMenu = nullptr; aCardMenu = nullptr;
emptyCardMenu = nullptr;
addTabMenu(gameMenu); addTabMenu(gameMenu);
} }

View file

@ -101,6 +101,8 @@ private:
QAction *aInviteToGame = nullptr; QAction *aInviteToGame = nullptr;
QList<QAction *> phaseActions; QList<QAction *> phaseActions;
QAction *aCardMenu; QAction *aCardMenu;
/** @brief Placeholder submenu shown when no card is selected; reused instead of reallocated. */
QMenu *emptyCardMenu = nullptr;
/** /**
* @brief The actions associated with managing a QDockWidget * @brief The actions associated with managing a QDockWidget