diff --git a/cockatrice/src/game_graphics/game_scene.cpp b/cockatrice/src/game_graphics/game_scene.cpp index 8de4ddd91..b4c6e1690 100644 --- a/cockatrice/src/game_graphics/game_scene.cpp +++ b/cockatrice/src/game_graphics/game_scene.cpp @@ -1040,6 +1040,7 @@ void GameScene::showCardHoverPopup(CardItem *card) cardHoverWidget = new CardHoverWidget(viewport); connect(cardHoverWidget, &CardHoverWidget::hoverEntered, this, &GameScene::onHoverWidgetEntered); connect(cardHoverWidget, &CardHoverWidget::hoverLeft, this, &GameScene::onHoverWidgetLeft); + connect(cardHoverWidget, &CardHoverWidget::dismissRequested, this, &GameScene::hideCardHoverPopup); // Keep the popup anchored to its card while the view scrolls, zooms, or resizes if (!views().isEmpty()) { diff --git a/cockatrice/src/interface/widgets/cards/card_hover_widget.cpp b/cockatrice/src/interface/widgets/cards/card_hover_widget.cpp index 4e0f93480..27625db52 100644 --- a/cockatrice/src/interface/widgets/cards/card_hover_widget.cpp +++ b/cockatrice/src/interface/widgets/cards/card_hover_widget.cpp @@ -407,10 +407,12 @@ void CardHoverWidget::createPTOverlay() a->actIncT(cardList()); } }); - btnSetPT = createOverlayButton("SET", tr("Set power/toughness..."), [getActions, cardList]() { - if (auto *a = getActions()) { - a->actRequestSetPTDialog(cardList()); - } + btnSetPT = createOverlayButton("SET", tr("Set power/toughness..."), [this, getActions, cardList]() { + runModalAction([getActions, cardList]() { + if (auto *a = getActions()) { + a->actRequestSetPTDialog(cardList()); + } + }); }); btnResetPT = createOverlayButton("RST", tr("Reset power/toughness"), [getActions, cardList]() { if (auto *a = getActions()) { @@ -885,7 +887,10 @@ void CardHoverWidget::buildMainTab(const QList &cardList, const QSt mainPage)); grid.addButton(createGridButton( tr("Set annotation..."), semanticActionIcon(QStringLiteral("icons/pencil"), AppColor::Marked), - [actions, cardList]() { actions->actRequestSetAnnotationDialog(cardList); }, mainPage)); + [this, actions, cardList]() { + runModalAction([actions, cardList]() { actions->actRequestSetAnnotationDialog(cardList); }); + }, + mainPage)); grid.addButton(createGridButton(tr("Turn Over"), semanticActionIcon(QStringLiteral("icons/swap"), AppColor::Hidden), invoke(cmFlip), mainPage)); @@ -1041,7 +1046,10 @@ void CardHoverWidget::buildCounterTab(const QList &cardList, const layout->addWidget(createSectionHeader(tr("Power / Toughness"), counterPage)); layout->addWidget(createSmallButton( - tr("Set power and toughness..."), [actions, cardList]() { actions->actRequestSetPTDialog(cardList); }, + tr("Set power and toughness..."), + [this, actions, cardList]() { + runModalAction([actions, cardList]() { actions->actRequestSetPTDialog(cardList); }); + }, counterPage)); layout->addWidget(createSmallButton( tr("Reset power and toughness"), [actions, cardList]() { actions->actResetPT(cardList); }, counterPage)); @@ -1065,7 +1073,10 @@ void CardHoverWidget::buildCounterTab(const QList &cardList, const tr("Remove"), [actions, cardList, i]() { actions->actRemoveCardCounter(cardList, i); }, counterPage)); } row->addWidget(createSmallButton( - tr("Set..."), [actions, cardList, i]() { actions->actRequestSetCardCounterDialog(cardList, i); }, + tr("Set..."), + [this, actions, cardList, i]() { + runModalAction([actions, cardList, i]() { actions->actRequestSetCardCounterDialog(cardList, i); }); + }, counterPage)); layout->addLayout(row); } @@ -1108,14 +1119,19 @@ void CardHoverWidget::buildTokenTab(CardItem *card, bool canModify) CardItem *source = card; layout->addWidget(createSmallButton( tr("Token: ") + relatedName, - [actions, source, captured]() { - actions->actRequestCreateRelatedFromRelationDialog(source->getState(), captured); + [this, actions, source, captured]() { + runModalAction([actions, source, captured]() { + actions->actRequestCreateRelatedFromRelationDialog(source->getState(), captured); + }); }, tokenPage)); } if (hasMulti && known > 0) { layout->addWidget(createSmallButton( - tr("All tokens"), [actions, card]() { actions->actCreateAllRelatedCards(card->getState()); }, + tr("All tokens"), + [this, actions, card]() { + runModalAction([actions, card]() { actions->actCreateAllRelatedCards(card->getState()); }); + }, tokenPage)); } anyToken = known > 0; @@ -1613,6 +1629,23 @@ bool CardHoverWidget::isClosing() const return fadeOut->state() == QAbstractAnimation::Running; } +void CardHoverWidget::runModalAction(const std::function &action) +{ + // The panel is a Qt::ToolTip (Qt::Popup | Qt::Sheet): it is always on top and + // holds an input grab. A modal dialog opened while it is still up lands behind + // it and cannot be activated. Ask the scene to dismiss the panel so its hover + // timers stop and the covered card fades back in, then drop the window + // synchronously to release the grab. + emit dismissRequested(); + fadeIn->stop(); + fadeOut->stop(); + hide(); + + // Defer the dialog to the next event-loop turn: the popup grab is not fully + // released until after the current mouse event returns. + QTimer::singleShot(0, this, action); +} + // ---------- Event filter ---------- bool CardHoverWidget::eventFilter(QObject *obj, QEvent *event) diff --git a/cockatrice/src/interface/widgets/cards/card_hover_widget.h b/cockatrice/src/interface/widgets/cards/card_hover_widget.h index e8366b124..acd9d4cc6 100644 --- a/cockatrice/src/interface/widgets/cards/card_hover_widget.h +++ b/cockatrice/src/interface/widgets/cards/card_hover_widget.h @@ -81,6 +81,8 @@ protected: signals: void hoverEntered(); void hoverLeft(); + /** @brief Asks the scene to dismiss the panel before a modal dialog opens. */ + void dismissRequested(); private slots: void onCardChanged(); @@ -167,6 +169,16 @@ private: QWidget *makeStackPage(QVBoxLayout *&layout); void showPage(int index); + /** + * @brief Dismisses the panel, then runs an action that opens a modal dialog. + * + * A Qt::ToolTip window is always on top and holds the input grab, so a modal + * dialog opened from inside the panel lands behind it and cannot be + * activated. Hide the panel synchronously, then defer the action so the grab + * is released before the dialog's event loop starts. + */ + void runModalAction(const std::function &action); + QLabel *createSectionHeader(const QString &text, QWidget *parent); QLabel *createNoteLabel(const QString &text, QWidget *parent);