[Client] Choose a token's printing in the create-token dialog (#7815) (#7866)

* Refactor printing selector onto a host interface

Move the deck-editor coupling out of PrintingSelector behind PrintingSelectorHost so the same widget can run where there is no deck. The deck editor keeps its behaviour through DeckEditorPrintingSelectorHost; a selection-only host hides deck controls, reports a picked printing, and highlights it in the grid.

* Add a printings tab to the token dialog

The token list and the printing selector now share one tabbed surface. Picking a printing sets the provider id used by the live preview and by the created token, while an untouched dialog keeps the existing preference lookup. Names with no printings show a placeholder instead of an empty grid.

* Address printing selector review feedback

Hold the deck editor printing-selector host by value, since its deck editor is known at construction. Emit printingSelected for every left-click pick instead of gating it on selection-only mode; consumers that do not want picks simply do not connect.

* Use the shared layout helper in the token tabs

Replace the hand-built zero-margin layouts with QtUtils::makeLayout, which is the helper the printing selector already uses.
This commit is contained in:
BruebachL 2026-11-10 10:32:46 +01:00 • committed by GitHub
parent 0498ea8907
commit 505c3b62dc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 442 additions and 97 deletions

View file

@ -218,6 +218,7 @@ set(cockatrice_SOURCES
src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp
src/interface/widgets/deck_editor/deck_editor_filter_dock_widget.cpp
src/interface/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp
src/interface/widgets/deck_editor/deck_editor_printing_selector_host.cpp
src/interface/widgets/deck_editor/deck_list_style_proxy.cpp
src/interface/widgets/deck_editor/deck_state_manager.cpp
src/interface/widgets/deck_editor/deck_zone_dialog.cpp

View file

@ -1,8 +1,10 @@
#include "dlg_create_token.h"
#include "../../client/settings/cache_settings.h"
#include "../../interface/widgets/printing_selector/printing_selector.h"
#include "libcockatrice/card/card_info.h"
#include "libcockatrice/card/database/card_database_querier.h"
#include "libcockatrice/card/printing/exact_card.h"
#include "token_preview_widget.h"
#include <QByteArray>
@ -21,10 +23,12 @@
#include <QRadioButton>
#include <QSet>
#include <QSignalBlocker>
#include <QTabWidget>
#include <QTimer>
#include <QTreeView>
#include <QVBoxLayout>
#include <QVariant>
#include <QWidget>
#include <libcockatrice/card/card_localization.h>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/models/database/card_database_model.h>
@ -34,6 +38,7 @@
#include <libcockatrice/settings/interface_settings.h>
#include <libcockatrice/settings/layouts_settings.h>
#include <libcockatrice/utility/card_ref.h>
#include <libcockatrice/utility/qt_utils.h>
#include <libcockatrice/utility/string_limits.h>
#include <qnamespace.h>
@ -150,18 +155,35 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
cardDatabaseDisplayModel->setCardNameSet(QSet<QString>(predefinedTokens.begin(), predefinedTokens.end()));
}
auto *tokenChooseLayout = new QVBoxLayout;
auto *tokenChoosePage = new QWidget;
tokenChoosePage->setObjectName("tokenChoosePage");
auto *tokenChooseLayout = QtUtils::makeLayout<QVBoxLayout>(tokenChoosePage);
tokenChooseLayout->addWidget(chooseTokenFromAllRadioButton);
tokenChooseLayout->addWidget(chooseTokenFromDeckRadioButton);
tokenChooseLayout->addWidget(chooseTokenView);
auto *tokenChooseGroupBox = new QGroupBox(tr("Choose token from list"));
tokenChooseGroupBox->setLayout(tokenChooseLayout);
auto *printingsPage = new QWidget;
printingsPage->setObjectName("tokenPrintingsPage");
auto *printingsLayout = QtUtils::makeLayout<QVBoxLayout>(printingsPage);
printingSelector = new PrintingSelector(printingsPage, &printingSelectorHost);
printingSelector->setObjectName("tokenPrintingSelector");
printingsLayout->addWidget(printingSelector);
connect(printingSelector, &PrintingSelector::printingSelected, this, [this](const ExactCard &card) {
explicitProviderId = card.getPrinting().getUuid();
explicitProviderName = card.getName();
updatePreview();
});
tokenSourceTabs = new QTabWidget;
tokenSourceTabs->setObjectName("tokenSourceTabs");
tokenSourceTabs->addTab(tokenChoosePage, tr("&Tokens"));
tokenSourceTabs->addTab(printingsPage, tr("&Printings"));
auto *hbox = new QGridLayout;
hbox->addWidget(pic, 0, 0, 1, 1);
hbox->addWidget(tokenDataGroupBox, 1, 0, 1, 1);
hbox->addWidget(tokenChooseGroupBox, 0, 1, 2, 1);
hbox->addWidget(tokenSourceTabs, 0, 1, 2, 1);
hbox->setColumnStretch(1, 1);
auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
@ -175,13 +197,15 @@ DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *pa
setWindowTitle(tr("Create token"));
connect(nameEdit, &QLineEdit::textChanged, this, &DlgCreateToken::syncPrintingsToToken);
connect(nameEdit, &QLineEdit::textChanged, this, &DlgCreateToken::updatePreview);
connect(colorEdit, qOverload<int>(&QComboBox::currentIndexChanged), this, &DlgCreateToken::updatePreview);
connect(ptEdit, &QLineEdit::textChanged, this, &DlgCreateToken::updatePreview);
connect(annotationEdit, &QLineEdit::textChanged, this, &DlgCreateToken::updatePreview);
syncPrintingsToToken();
updatePreview();
resize(600, 500);
resize(900, 650);
restoreGeometry(SettingsCache::instance().layouts().getTokenDialogGeometry());
}
@ -228,9 +252,40 @@ void DlgCreateToken::tokenSelectionChanged(const QModelIndex &current, const QMo
// Filtering, changing the deck/all scope, or reloading the database can
// invalidate the current row. None of these actions discard the token draft.
syncPrintingsToToken();
updatePreview();
}
void DlgCreateToken::syncPrintingsToToken()
{
const CardInfoPtr cardInfo = CardDatabaseManager::query()->lookupCardByName(nameEdit->text());
if (!cardInfo) {
explicitProviderId.clear();
explicitProviderName.clear();
} else if (!explicitProviderId.isEmpty() && explicitProviderName != cardInfo->getName()) {
// A pick belongs to the token name it was made for.
explicitProviderId.clear();
explicitProviderName.clear();
}
if (!cardInfo || cardInfo->getSets().empty()) {
printingSelector->setCard(nullptr);
printingSelector->setSelectedProviderId(QString());
printingSelector->setPlaceholderText(tr("This token has no available printings."));
return;
}
printingSelector->setPlaceholderText(QString());
printingSelector->setCard(cardInfo);
const QString providerId =
explicitProviderId.isEmpty()
? SettingsCache::instance().cardOverrides().getCardPreferenceOverride(cardInfo->getName())
: explicitProviderId;
printingSelector->setSelectedProviderId(providerId);
}
void DlgCreateToken::updateSearchFieldWithoutUpdatingFilter(const QString &newValue) const
{
const QSignalBlocker blocker(nameEdit);
@ -275,14 +330,25 @@ void DlgCreateToken::actReject()
TokenInfo DlgCreateToken::tokenDraft() const
{
// Keep an explicit pick only while it still belongs to the current token.
QString providerId = explicitProviderId;
if (!providerId.isEmpty()) {
const CardInfoPtr pickedCard = CardDatabaseManager::query()->lookupCardByName(nameEdit->text());
if (!pickedCard || pickedCard->getName() != explicitProviderName) {
providerId.clear();
}
}
if (providerId.isEmpty()) {
providerId = SettingsCache::instance().cardOverrides().getCardPreferenceOverride(nameEdit->text());
}
TokenInfo token = {.name = nameEdit->text(),
.color = colorEdit->itemData(colorEdit->currentIndex()).toString(),
.pt = ptEdit->text(),
.annotation = annotationEdit->text(),
.destroy = destroyCheckBox->isChecked(),
.faceDown = faceDownCheckBox->isChecked(),
.providerId =
SettingsCache::instance().cardOverrides().getCardPreferenceOverride(nameEdit->text())};
.providerId = providerId};
// Match the normalization applied when the player creates the token, so
// manually typed aliases and the default P/T preview the actual result.

View file

@ -7,6 +7,8 @@
#ifndef DLG_CREATETOKEN_H
#define DLG_CREATETOKEN_H
#include "../../interface/widgets/printing_selector/printing_selector_selection_host.h"
#include <QDialog>
#include <QList>
#include <QString>
@ -19,10 +21,12 @@ class QLineEdit;
class QComboBox;
class QCheckBox;
class QRadioButton;
class QTabWidget;
class QTreeView;
class CardDatabaseModel;
class TokenDisplayModel;
class TokenPreviewWidget;
class PrintingSelector;
class QModelIndex;
class QWidget;
@ -39,6 +43,7 @@ private slots:
void faceDownCheckBoxToggled(bool checked);
void tokenSelectionChanged(const QModelIndex &current, const QModelIndex &previous);
void updateSearch(const QString &search);
void syncPrintingsToToken();
void updatePreview();
void actChooseTokenFromAll(bool checked);
void actChooseTokenFromDeck(bool checked);
@ -57,6 +62,11 @@ private:
QRadioButton *chooseTokenFromAllRadioButton, *chooseTokenFromDeckRadioButton;
TokenPreviewWidget *pic;
QTreeView *chooseTokenView;
QTabWidget *tokenSourceTabs;
PrintingSelector *printingSelector;
PrintingSelectorSelectionHost printingSelectorHost;
QString explicitProviderId;
QString explicitProviderName;
[[nodiscard]] TokenInfo tokenDraft() const;
void updateSearchFieldWithoutUpdatingFilter(const QString &newValue) const;

View file

@ -4,6 +4,7 @@
#include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h"
#include "../printing_selector/printing_selector.h"
#include "deck_editor_deck_dock_widget.h"
#include "deck_editor_printing_selector_host.h"
#include "printing_disabled_info_widget.h"
#include <QFlags>
@ -13,7 +14,7 @@
#include <qnamespace.h>
DeckEditorPrintingSelectorDockWidget::DeckEditorPrintingSelectorDockWidget(AbstractTabDeckEditor *parent)
: QDockWidget(parent), deckEditor(parent)
: QDockWidget(parent), deckEditor(parent), printingSelectorHost(parent)
{
setObjectName("printingSelectorDock");
@ -34,7 +35,7 @@ DeckEditorPrintingSelectorDockWidget::DeckEditorPrintingSelectorDockWidget(Abstr
void DeckEditorPrintingSelectorDockWidget::createPrintingSelectorDock()
{
printingSelector = new PrintingSelector(this, deckEditor);
printingSelector = new PrintingSelector(this, &printingSelectorHost);
printingSelector->setObjectName("printingSelector");
auto *printingSelectorFrame = new QVBoxLayout;
printingSelectorFrame->setObjectName("printingSelectorFrame");

View file

@ -7,6 +7,8 @@
#ifndef DECK_EDITOR_PRINTING_SELECTOR_DOCK_WIDGET_H
#define DECK_EDITOR_PRINTING_SELECTOR_DOCK_WIDGET_H
#include "deck_editor_printing_selector_host.h"
#include <QDockWidget>
#include <qtmetamacros.h>
@ -27,6 +29,7 @@ public:
private:
AbstractTabDeckEditor *deckEditor;
DeckEditorPrintingSelectorHost printingSelectorHost;
QWidget *printingSelectorDockContents;
PrintingDisabledInfoWidget *printingDisabledInfoWidget;

View file

@ -0,0 +1,33 @@
#include "deck_editor_printing_selector_host.h"
#include "../tabs/abstract_tab_deck_editor.h"
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/card/database/card_database_querier.h>
#include <libcockatrice/utility/card_ref.h>
DeckEditorPrintingSelectorHost::DeckEditorPrintingSelectorHost(AbstractTabDeckEditor *_deckEditor)
: deckEditor(_deckEditor)
{
}
DeckStateManager *DeckEditorPrintingSelectorHost::getDeckStateManager() const
{
return deckEditor->deckStateManager;
}
void DeckEditorPrintingSelectorHost::showCard(const ExactCard &card)
{
deckEditor->updateCard(card);
}
void DeckEditorPrintingSelectorHost::showRelatedCard(const QString &cardName)
{
deckEditor->updateCard(CardDatabaseManager::query()->getCard(CardRef{.name = cardName}));
deckEditor->showPrintingSelector();
}
bool DeckEditorPrintingSelectorHost::isSelectionOnly() const
{
return false;
}

View file

@ -0,0 +1,29 @@
/**
* @file deck_editor_printing_selector_host.h
* @ingroup DeckEditorWidgets
* @brief Connects a PrintingSelector to a deck editor tab.
*/
#ifndef DECK_EDITOR_PRINTING_SELECTOR_HOST_H
#define DECK_EDITOR_PRINTING_SELECTOR_HOST_H
#include "../printing_selector/printing_selector_host.h"
class AbstractTabDeckEditor;
/** @brief Deck-editor implementation of PrintingSelectorHost. */
class DeckEditorPrintingSelectorHost final : public PrintingSelectorHost
{
public:
explicit DeckEditorPrintingSelectorHost(AbstractTabDeckEditor *deckEditor);
[[nodiscard]] DeckStateManager *getDeckStateManager() const override;
void showCard(const ExactCard &card) override;
void showRelatedCard(const QString &cardName) override;
[[nodiscard]] bool isSelectionOnly() const override;
private:
AbstractTabDeckEditor *deckEditor;
};
#endif // DECK_EDITOR_PRINTING_SELECTOR_HOST_H

View file

@ -5,7 +5,6 @@
#include "../deck_editor/deck_state_manager.h"
#include "../general/layout_containers/flow_widget.h"
#include "../quick_settings/settings_button_widget.h"
#include "../tabs/abstract_tab_deck_editor.h"
#include "deck_list_model.h"
#include "libcockatrice/card/card_info.h"
#include "libcockatrice/card/printing/exact_card.h"
@ -16,10 +15,10 @@
#include "printing_selector_card_search_widget.h"
#include "printing_selector_card_selection_widget.h"
#include "printing_selector_card_sorting_widget.h"
#include "printing_selector_host.h"
#include "printing_selector_placeholder_widget.h"
#include <QCheckBox>
#include <QDebug>
#include <QHBoxLayout>
#include <QHideEvent>
#include <QList>
@ -27,8 +26,8 @@
#include <QScrollArea>
#include <QScrollBar>
#include <QSharedPointer>
#include <QSizePolicy>
#include <QShowEvent>
#include <QSizePolicy>
#include <QString>
#include <QTimer>
#include <QVBoxLayout>
@ -46,10 +45,10 @@
* for the selected card.
*
* @param parent The parent widget for the PrintingSelector.
* @param deckEditor The TabDeckEditor instance used for managing the deck.
* @param _host The host supplying deck state and routing card activations.
*/
PrintingSelector::PrintingSelector(QWidget *parent, AbstractTabDeckEditor *_deckEditor)
: QWidget(parent), deckEditor(_deckEditor), deckStateManager(_deckEditor->deckStateManager)
PrintingSelector::PrintingSelector(QWidget *parent, PrintingSelectorHost *_host)
: QWidget(parent), host(_host), deckStateManager(_host->getDeckStateManager())
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
layout = new QVBoxLayout(this);
@ -70,14 +69,16 @@ PrintingSelector::PrintingSelector(QWidget *parent, AbstractTabDeckEditor *_deck
displayOptionsWidget = new SettingsButtonWidget(this);
displayOptionsWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
// Create the checkbox for navigation buttons visibility
navigationCheckBox = new QCheckBox(this);
navigationCheckBox->setChecked(
SettingsCache::instance().cardsDisplay().getPrintingSelectorNavigationButtonsVisible());
connect(navigationCheckBox, &QCheckBox::QT_STATE_CHANGED, this,
&PrintingSelector::toggleVisibilityNavigationButtons);
connect(navigationCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance().cardsDisplay(),
&CardsDisplaySettings::setPrintingSelectorNavigationButtonsVisible);
// Navigation only makes sense while the selector is attached to a deck.
if (deckStateManager) {
navigationCheckBox = new QCheckBox(this);
navigationCheckBox->setChecked(
SettingsCache::instance().cardsDisplay().getPrintingSelectorNavigationButtonsVisible());
connect(navigationCheckBox, &QCheckBox::QT_STATE_CHANGED, this,
&PrintingSelector::toggleVisibilityNavigationButtons);
connect(navigationCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance().cardsDisplay(),
&CardsDisplaySettings::setPrintingSelectorNavigationButtonsVisible);
}
cardSizeWidget = new CardSizeWidget(displayOptionsWidget, flowWidget,
SettingsCache::instance().cardsDisplay().getPrintingSelectorCardSize());
@ -86,7 +87,9 @@ PrintingSelector::PrintingSelector(QWidget *parent, AbstractTabDeckEditor *_deck
cardSizeWidget->enableCtrlScrollResize(flowWidget);
displayOptionsWidget->addSettingsWidget(sortToolBar);
displayOptionsWidget->addSettingsWidget(navigationCheckBox);
if (navigationCheckBox) {
displayOptionsWidget->addSettingsWidget(navigationCheckBox);
}
displayOptionsWidget->addSettingsWidget(cardSizeWidget);
sortAndOptionsContainer = new QWidget(this);
@ -107,26 +110,31 @@ PrintingSelector::PrintingSelector(QWidget *parent, AbstractTabDeckEditor *_deck
placeholderWidget->setVisible(true);
flowWidget->setVisible(false);
cardSelectionBar = new PrintingSelectorCardSelectionWidget(this, deckStateManager);
cardSelectionBar->setVisible(
SettingsCache::instance().cardsDisplay().getPrintingSelectorNavigationButtonsVisible());
layout->addWidget(cardSelectionBar);
if (deckStateManager) {
cardSelectionBar = new PrintingSelectorCardSelectionWidget(this, deckStateManager);
cardSelectionBar->setVisible(
SettingsCache::instance().cardsDisplay().getPrintingSelectorNavigationButtonsVisible());
layout->addWidget(cardSelectionBar);
// Connect deck model data change signal to update display
connect(deckStateManager, &DeckStateManager::uniqueCardsChanged, this, &PrintingSelector::printingsInDeckChanged);
connect(deckStateManager, &DeckStateManager::cardModified, this, &PrintingSelector::updateCardAmounts);
// Connect deck model data change signal to update display
connect(deckStateManager, &DeckStateManager::uniqueCardsChanged, this,
&PrintingSelector::printingsInDeckChanged);
connect(deckStateManager, &DeckStateManager::cardModified, this, &PrintingSelector::updateCardAmounts);
}
retranslateUi();
}
void PrintingSelector::retranslateUi()
{
navigationCheckBox->setText(tr("Display Navigation Buttons"));
if (navigationCheckBox) {
navigationCheckBox->setText(tr("Display Navigation Buttons"));
}
}
void PrintingSelector::printingsInDeckChanged()
{
if (SettingsCache::instance().cardsDisplay().getBumpSetsWithCardsInDeckToTop()) {
if (deckStateManager && SettingsCache::instance().cardsDisplay().getBumpSetsWithCardsInDeckToTop()) {
displayDirty = true;
if (isVisible()) {
// Coalesce deck changes while the model settles.
@ -190,7 +198,7 @@ static QMap<QString, ZoneCounts> tallyUuidCounts(const DeckListModel *model, con
void PrintingSelector::updateCardAmounts()
{
if (selectedCard.isNull()) {
if (!deckStateManager || selectedCard.isNull()) {
return;
}
@ -209,11 +217,17 @@ void PrintingSelector::updateDisplay()
widgetLoadingBufferTimer->deleteLater();
widgetLoadingBufferTimer = new QTimer(this);
flowWidget->clearLayout();
cardDisplayWidgets.clear();
if (selectedCard.isNull()) {
// Show placeholder, hide flowWidget
placeholderWidget->setVisible(true);
flowWidget->setVisible(false);
if (placeholderText.isEmpty()) {
placeholderWidget->resetText();
} else {
placeholderWidget->setText(placeholderText);
}
setWindowTitle(tr("Printing Selector"));
} else {
// Hide placeholder, show flowWidget
@ -236,6 +250,11 @@ void PrintingSelector::setCard(const CardInfoPtr &newCard)
return;
}
// A new card invalidates the previously picked printing.
if (selectedCard.isNull() || newCard.isNull() || selectedCard->getName() != newCard->getName()) {
selectedProviderId.clear();
}
widgetLoadingBufferTimer->stop();
deckChangeTimer->stop();
selectedCard = newCard;
@ -263,16 +282,16 @@ void PrintingSelector::getAllSetsForCurrentCard()
const QList<PrintingInfo> filteredPrintings =
sortToolBar->filterSets(sortedPrintings, searchBar->getSearchText().trimmed().toLower());
QList<PrintingInfo> printingsToUse;
const DeckListModel *deckModel = deckStateManager ? deckStateManager->getModel() : nullptr;
if (SettingsCache::instance().cardsDisplay().getBumpSetsWithCardsInDeckToTop()) {
printingsToUse =
sortToolBar->prependPrintingsInDeck(filteredPrintings, selectedCard, deckStateManager->getModel());
if (deckModel && SettingsCache::instance().cardsDisplay().getBumpSetsWithCardsInDeckToTop()) {
printingsToUse = sortToolBar->prependPrintingsInDeck(filteredPrintings, selectedCard, deckModel);
} else {
printingsToUse = filteredPrintings;
}
printingsToUse = sortToolBar->prependPinnedPrintings(printingsToUse, selectedCard->getName());
auto uuidToAmounts = tallyUuidCounts(deckStateManager->getModel(), selectedCard->getName());
auto uuidToAmounts = deckModel ? tallyUuidCounts(deckModel, selectedCard->getName()) : QMap<QString, ZoneCounts>();
// Defer widget creation
currentIndex = 0;
@ -280,12 +299,24 @@ void PrintingSelector::getAllSetsForCurrentCard()
connect(widgetLoadingBufferTimer, &QTimer::timeout, this, [=, this]() mutable {
for (int i = 0; i < BATCH_SIZE && currentIndex < printingsToUse.size(); ++i, ++currentIndex) {
auto card = ExactCard(selectedCard, printingsToUse[currentIndex]);
auto *cardDisplayWidget = new PrintingSelectorCardDisplayWidget(this, deckEditor, deckStateManager,
cardSizeWidget->getSlider(), card);
auto *cardDisplayWidget =
new PrintingSelectorCardDisplayWidget(this, host, cardSizeWidget->getSlider(), card);
flowWidget->addWidget(cardDisplayWidget);
cardDisplayWidgets.append(cardDisplayWidget);
cardDisplayWidget->updateCardAmounts(uuidToAmounts);
cardDisplayWidget->setSelected(card.getPrinting().getUuid() == selectedProviderId);
connect(cardDisplayWidget, &PrintingSelectorCardDisplayWidget::cardPreferenceChanged, this,
&PrintingSelector::updateDisplay);
connect(cardDisplayWidget, &PrintingSelectorCardDisplayWidget::printingSelected, this,
[this](const ExactCard &selected) {
const QString providerId = selected.getPrinting().getUuid();
if (selectedProviderId == providerId) {
return;
}
selectedProviderId = providerId;
updateSelectedCardWidgets();
emit printingSelected(selected);
});
connect(this, &PrintingSelector::cardAmountsChanged, cardDisplayWidget,
&PrintingSelectorCardDisplayWidget::updateCardAmounts);
}
@ -306,5 +337,38 @@ void PrintingSelector::getAllSetsForCurrentCard()
*/
void PrintingSelector::toggleVisibilityNavigationButtons(bool _state)
{
cardSelectionBar->setVisible(_state);
}
if (cardSelectionBar) {
cardSelectionBar->setVisible(_state);
}
}
void PrintingSelector::setSelectedProviderId(const QString &providerId)
{
if (selectedProviderId == providerId) {
return;
}
selectedProviderId = providerId;
updateSelectedCardWidgets();
}
void PrintingSelector::updateSelectedCardWidgets() const
{
for (auto *cardDisplayWidget : cardDisplayWidgets) {
cardDisplayWidget->setSelected(!selectedProviderId.isEmpty() &&
cardDisplayWidget->getProviderId() == selectedProviderId);
}
}
void PrintingSelector::setPlaceholderText(const QString &text)
{
placeholderText = text;
if (selectedCard.isNull()) {
if (placeholderText.isEmpty()) {
placeholderWidget->resetText();
} else {
placeholderWidget->setText(placeholderText);
}
placeholderWidget->setVisible(true);
flowWidget->setVisible(false);
}
}

View file

@ -7,12 +7,18 @@
#ifndef PRINTING_SELECTOR_H
#define PRINTING_SELECTOR_H
#include "printing_selector_host.h"
#include <QList>
#include <QWidget>
#include <libcockatrice/card/card_info.h>
#include <libcockatrice/card/printing/exact_card.h>
#include <qtmetamacros.h>
class CardSizeWidget;
class DeckStateManager;
class FlowWidget;
class PrintingSelectorCardDisplayWidget;
class PrintingSelectorPlaceholderWidget;
class QCheckBox;
class QHBoxLayout;
@ -33,20 +39,20 @@ struct ZoneCounts
int tokensboard = 0;
};
class DeckStateManager;
class PrintingSelectorCardSearchWidget;
class PrintingSelectorCardSelectionWidget;
class PrintingSelectorCardSortingWidget;
class AbstractTabDeckEditor;
class PrintingSelector : public QWidget
{
Q_OBJECT
public:
PrintingSelector(QWidget *parent, AbstractTabDeckEditor *deckEditor);
PrintingSelector(QWidget *parent, PrintingSelectorHost *host);
void setCard(const CardInfoPtr &newCard);
void setSelectedProviderId(const QString &providerId);
void setPlaceholderText(const QString &text);
void getAllSetsForCurrentCard();
protected:
@ -78,25 +84,36 @@ signals:
*/
void cardAmountsChanged(const QMap<QString, ZoneCounts> &uuidToAmounts);
/**
* A printing was picked in the selector.
* @param card The picked card and printing.
*/
void printingSelected(const ExactCard &card);
private:
QVBoxLayout *layout;
SettingsButtonWidget *displayOptionsWidget;
QWidget *sortAndOptionsContainer;
QHBoxLayout *sortAndOptionsLayout;
QCheckBox *navigationCheckBox;
QCheckBox *navigationCheckBox = nullptr;
PrintingSelectorCardSortingWidget *sortToolBar;
PrintingSelectorCardSearchWidget *searchBar;
PrintingSelectorPlaceholderWidget *placeholderWidget;
FlowWidget *flowWidget;
CardSizeWidget *cardSizeWidget;
PrintingSelectorCardSelectionWidget *cardSelectionBar;
AbstractTabDeckEditor *deckEditor;
PrintingSelectorCardSelectionWidget *cardSelectionBar = nullptr;
PrintingSelectorHost *host;
DeckStateManager *deckStateManager;
QList<PrintingSelectorCardDisplayWidget *> cardDisplayWidgets;
CardInfoPtr selectedCard;
QString selectedProviderId;
QString placeholderText;
QTimer *widgetLoadingBufferTimer;
QTimer *deckChangeTimer;
bool displayDirty = false;
int currentIndex = 0;
void updateSelectedCardWidgets() const;
};
#endif // PRINTING_SELECTOR_H

View file

@ -5,6 +5,7 @@
#include "libcockatrice/card/set/card_set.h"
#include "printing_selector.h"
#include "printing_selector_card_overlay_widget.h"
#include "printing_selector_host.h"
#include "set_name_and_collectors_number_display_widget.h"
#include <QMap>
@ -14,8 +15,6 @@
#include <QVBoxLayout>
#include <qnamespace.h>
class AbstractTabDeckEditor;
class DeckStateManager;
class QSlider;
/**
@ -28,18 +27,20 @@ class QSlider;
* display.
*
* @param parent The parent widget for this display.
* @param deckEditor The TabDeckEditor instance for deck management.
* @param deckStateManager The DeckStateManager instance providing deck data.
* @param host The host supplying deck state and routing card activations.
* @param cardSizeSlider The slider controlling the size of the displayed card.
* @param rootCard The root card object, representing the card to be displayed.
*/
PrintingSelectorCardDisplayWidget::PrintingSelectorCardDisplayWidget(QWidget *parent,
AbstractTabDeckEditor *deckEditor,
DeckStateManager *deckStateManager,
PrintingSelectorHost *host,
QSlider *cardSizeSlider,
const ExactCard &rootCard)
: QWidget(parent), rootCard(rootCard)
: QWidget(parent), rootCard(rootCard), selectionOnly(host->isSelectionOnly())
{
if (selectionOnly) {
// A plain QWidget only paints a stylesheet border with a styled background.
setAttribute(Qt::WA_StyledBackground, true);
}
layout = new QVBoxLayout(this);
layout->setContentsMargins(5, 5, 5, 5);
layout->setSpacing(3);
@ -47,9 +48,11 @@ PrintingSelectorCardDisplayWidget::PrintingSelectorCardDisplayWidget(QWidget *pa
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
// Create the overlay widget for the card display
overlayWidget = new PrintingSelectorCardOverlayWidget(this, deckEditor, deckStateManager, cardSizeSlider, rootCard);
overlayWidget = new PrintingSelectorCardOverlayWidget(this, host, cardSizeSlider, rootCard);
connect(overlayWidget, &PrintingSelectorCardOverlayWidget::cardPreferenceChanged, this,
[this]() { emit cardPreferenceChanged(); });
connect(overlayWidget, &PrintingSelectorCardOverlayWidget::printingSelected, this,
[this](const ExactCard &card) { emit printingSelected(card); });
CardSetPtr set = rootCard.getPrinting().getSet();
@ -89,3 +92,19 @@ void PrintingSelectorCardDisplayWidget::resizeEvent(QResizeEvent *event)
QWidget::resizeEvent(event);
clampSetNameToPicture();
}
void PrintingSelectorCardDisplayWidget::setSelected(bool selected)
{
if (!selectionOnly) {
return;
}
// Keep an invisible border in both states so selection does not reflow the grid.
const QString border = selected ? QStringLiteral("palette(highlight)") : QStringLiteral("transparent");
setStyleSheet(
QStringLiteral("PrintingSelectorCardDisplayWidget { border: 2px solid %1; border-radius: 4px; }").arg(border));
}
QString PrintingSelectorCardDisplayWidget::getProviderId() const
{
return rootCard.getPrinting().getUuid();
}

View file

@ -17,9 +17,8 @@
#include <QWidget>
#include <qtmetamacros.h>
class AbstractTabDeckEditor;
class DeckStateManager;
class PrintingSelectorCardOverlayWidget;
class PrintingSelectorHost;
class QSlider;
class QString;
class QVBoxLayout;
@ -33,11 +32,13 @@ class PrintingSelectorCardDisplayWidget : public QWidget
public:
PrintingSelectorCardDisplayWidget(QWidget *parent,
AbstractTabDeckEditor *deckEditor,
DeckStateManager *deckStateManager,
PrintingSelectorHost *host,
QSlider *cardSizeSlider,
const ExactCard &rootCard);
void setSelected(bool selected);
[[nodiscard]] QString getProviderId() const;
public slots:
void clampSetNameToPicture();
void updateCardAmounts(const QMap<QString, ZoneCounts> &uuidToAmounts);
@ -46,9 +47,11 @@ public slots:
signals:
void cardPreferenceChanged();
void printingSelected(const ExactCard &card);
private:
ExactCard rootCard;
bool selectionOnly;
QVBoxLayout *layout;
SetNameAndCollectorsNumberDisplayWidget *setNameAndCollectorsNumberDisplayWidget;
PrintingSelectorCardOverlayWidget *overlayWidget;

View file

@ -5,17 +5,15 @@
#include "../../theme_icon.h"
#include "../../theme_manager.h"
#include "../cards/card_info_picture_widget.h"
#include "../tabs/abstract_tab_deck_editor.h"
#include "all_zones_card_amount_widget.h"
#include "libcockatrice/card/card_info.h"
#include "libcockatrice/card/database/card_database_querier.h"
#include "libcockatrice/card/printing/printing_info.h"
#include "libcockatrice/card/set/card_set.h"
#include "printing_selector_host.h"
#include <QAction>
#include <QApplication>
#include <QColor>
#include <QDebug>
#include <QFileDialog>
#include <QGuiApplication>
#include <QImage>
@ -41,14 +39,12 @@
#include <QVBoxLayout>
#include <QVariant>
#include <QtGlobal>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/card/relation/card_relation.h>
#include <libcockatrice/settings/card_override_settings.h>
#include <libcockatrice/utility/card_ref.h>
#include <libcockatrice/utility/qt_utils.h>
#include <qnamespace.h>
class DeckStateManager;
namespace
{
/**
@ -100,17 +96,15 @@ previewPositionNear(const QRect &actionRect, const QSize &labelSize, const QRect
* as a widget that displays the card amounts in different zones (mainboard, sideboard, etc.).
*
* @param parent The parent widget for this overlay.
* @param _deckEditor The TabDeckEditor instance for deck management.
* @param deckStateManager The DeckStateManager instance providing deck data.
* @param _host The host supplying deck state and routing card activations.
* @param cardSizeSlider The slider controlling the size of the card.
* @param _rootCard The root card object that contains information about the card.
*/
PrintingSelectorCardOverlayWidget::PrintingSelectorCardOverlayWidget(QWidget *parent,
AbstractTabDeckEditor *_deckEditor,
DeckStateManager *deckStateManager,
PrintingSelectorHost *_host,
QSlider *cardSizeSlider,
const ExactCard &_rootCard)
: QWidget(parent), deckEditor(_deckEditor), rootCard(_rootCard)
: QWidget(parent), host(_host), rootCard(_rootCard)
{
// Set up the main layout
auto *mainLayout = QtUtils::makeLayout<QVBoxLayout>(this, 0);
@ -123,6 +117,14 @@ PrintingSelectorCardOverlayWidget::PrintingSelectorCardOverlayWidget(QWidget *pa
cardInfoPicture->setCard(_rootCard);
mainLayout->addWidget(cardInfoPicture);
// A left click reports the printing; consumers that only want a pick connect to the signal.
connect(cardInfoPicture, &CardInfoPictureWidget::cardClicked, this,
[this](QMouseEvent *event, const ExactCard &card) {
if (event->button() == Qt::LeftButton) {
emit printingSelected(card);
}
});
initializePinBadge();
auto refreshPinBadge = [this] {
initializePinBadge();
@ -163,10 +165,11 @@ PrintingSelectorCardOverlayWidget::PrintingSelectorCardOverlayWidget(QWidget *pa
// initial state
updatePinBadgeVisibility();
// Add AllZonesCardAmountWidget
allZonesCardAmountWidget = new AllZonesCardAmountWidget(this, deckStateManager, cardSizeSlider, _rootCard);
allZonesCardAmountWidget->raise(); // Ensure it's on top of the picture
// Amount controls are deck-only; a selection-only host has no deck to change.
if (DeckStateManager *deckStateManager = host->getDeckStateManager()) {
allZonesCardAmountWidget = new AllZonesCardAmountWidget(this, deckStateManager, cardSizeSlider, _rootCard);
allZonesCardAmountWidget->raise(); // Ensure it's on top of the picture
}
connect(cardSizeSlider, &QSlider::valueChanged, cardInfoPicture, &CardInfoPictureWidget::setScaleFactor);
}
@ -216,7 +219,7 @@ void PrintingSelectorCardOverlayWidget::resizeEvent(QResizeEvent *event)
void PrintingSelectorCardOverlayWidget::enterEvent(QEnterEvent *event)
{
QWidget::enterEvent(event);
deckEditor->updateCard(rootCard);
host->showCard(rootCard);
updateVisibility();
}
@ -224,6 +227,9 @@ void PrintingSelectorCardOverlayWidget::updateCardAmounts(int mainboardAmount,
int sideboardAmount,
int tokensboardAmount)
{
if (!allZonesCardAmountWidget) {
return;
}
allZonesCardAmountWidget->setAmounts(mainboardAmount, sideboardAmount, tokensboardAmount);
updateVisibility();
}
@ -233,6 +239,9 @@ void PrintingSelectorCardOverlayWidget::updateCardAmounts(int mainboardAmount,
*/
void PrintingSelectorCardOverlayWidget::updateVisibility()
{
if (!allZonesCardAmountWidget) {
return;
}
if (allZonesCardAmountWidget->isNonZero() || underMouse()) {
allZonesCardAmountWidget->setVisible(true);
} else {
@ -387,20 +396,20 @@ void PrintingSelectorCardOverlayWidget::customMenu(QPoint point)
menu.addSeparator();
// filling out the related cards submenu
auto *relatedMenu = new QMenu(tr("Show Related cards"), &menu);
menu.addMenu(relatedMenu);
auto relatedCards = rootCard.getInfo().getAllRelatedCards();
if (relatedCards.isEmpty()) {
relatedMenu->setDisabled(true);
} else {
for (const CardRelation *rel : relatedCards) {
const QString &relatedCardName = rel->getName();
QAction *relatedCard = relatedMenu->addAction(relatedCardName);
connect(relatedCard, &QAction::triggered, deckEditor, [this, relatedCardName] {
deckEditor->updateCard(CardDatabaseManager::query()->getCard({relatedCardName}));
deckEditor->showPrintingSelector();
});
// Related cards open in the deck editor, which a selection-only host does not have.
if (!host->isSelectionOnly()) {
auto *relatedMenu = new QMenu(tr("Show Related cards"), &menu);
menu.addMenu(relatedMenu);
auto relatedCards = rootCard.getInfo().getAllRelatedCards();
if (relatedCards.isEmpty()) {
relatedMenu->setDisabled(true);
} else {
for (const CardRelation *rel : relatedCards) {
const QString &relatedCardName = rel->getName();
QAction *relatedCard = relatedMenu->addAction(relatedCardName);
connect(relatedCard, &QAction::triggered, this,
[this, relatedCardName] { host->showRelatedCard(relatedCardName); });
}
}
}
// The preview anchors itself to this popup's global geometry while it is open, so the

View file

@ -14,10 +14,9 @@
class QAction;
class QMenu;
class AbstractTabDeckEditor;
class AllZonesCardAmountWidget;
class CardInfoPictureWidget;
class DeckStateManager;
class PrintingSelectorHost;
class QLabel;
class QPoint;
class QSlider;
@ -28,8 +27,7 @@ class PrintingSelectorCardOverlayWidget : public QWidget
public:
explicit PrintingSelectorCardOverlayWidget(QWidget *parent,
AbstractTabDeckEditor *_deckEditor,
DeckStateManager *_deckStateManager,
PrintingSelectorHost *_host,
QSlider *_cardSizeSlider,
const ExactCard &_rootCard);
@ -42,6 +40,7 @@ protected:
signals:
void cardPreferenceChanged();
void printingSelected(const ExactCard &card);
public slots:
void updateCardAmounts(int mainboardAmount, int sideboardAmount, int tokensboardAmount);
@ -57,9 +56,9 @@ private:
void refreshPreview();
void hidePreview();
CardInfoPictureWidget *cardInfoPicture;
AllZonesCardAmountWidget *allZonesCardAmountWidget;
AllZonesCardAmountWidget *allZonesCardAmountWidget = nullptr;
QLabel *pinBadge = nullptr;
AbstractTabDeckEditor *deckEditor;
PrintingSelectorHost *host;
ExactCard rootCard;
QLabel *cardOverridePreviewLabel = nullptr;
ExactCard hoveredOverrideCard;

View file

@ -204,7 +204,7 @@ QList<PrintingInfo> PrintingSelectorCardSortingWidget::prependPrintingsInDeck(co
const CardInfoPtr &selectedCard,
const DeckListModel *deckModel)
{
if (!selectedCard) {
if (!selectedCard || !deckModel) {
return {};
}

View file

@ -0,0 +1,39 @@
/**
* @file printing_selector_host.h
* @ingroup PrintingWidgets
* @brief Host services a PrintingSelector needs beyond its own widgets.
*/
#ifndef PRINTING_SELECTOR_HOST_H
#define PRINTING_SELECTOR_HOST_H
#include <QString>
class DeckStateManager;
class ExactCard;
/**
* @brief Dependency boundary between PrintingSelector and the surface hosting it.
*
* The deck editor supplies deck data and routes card activations back to its
* info pane. The token dialog uses the selection-only host, which has no deck.
*/
class PrintingSelectorHost
{
public:
virtual ~PrintingSelectorHost() = default;
/** @brief Deck state used for amounts and in-deck ordering, or nullptr when there is no deck. */
[[nodiscard]] virtual DeckStateManager *getDeckStateManager() const = 0;
/** @brief Shows a printing on the host's card info surface. */
virtual void showCard(const ExactCard &card) = 0;
/** @brief Opens a related card through the host. */
virtual void showRelatedCard(const QString &cardName) = 0;
/** @brief True when picking a printing only reports it instead of changing a deck. */
[[nodiscard]] virtual bool isSelectionOnly() const = 0;
};
#endif // PRINTING_SELECTOR_HOST_H

View file

@ -46,6 +46,16 @@ PrintingSelectorPlaceholderWidget::PrintingSelectorPlaceholderWidget(QWidget *pa
retranslateUi();
}
void PrintingSelectorPlaceholderWidget::setText(const QString &text)
{
textLabel->setText(text);
}
void PrintingSelectorPlaceholderWidget::resetText()
{
retranslateUi();
}
void PrintingSelectorPlaceholderWidget::retranslateUi()
{
textLabel->setText(tr("Select a card to view its available printings"));

View file

@ -13,6 +13,8 @@ class PrintingSelectorPlaceholderWidget : public QWidget
public:
explicit PrintingSelectorPlaceholderWidget(QWidget *parent = nullptr);
void setText(const QString &text);
void resetText();
private slots:
void applyInterfaceDefaultFont();

View file

@ -0,0 +1,40 @@
/**
* @file printing_selector_selection_host.h
* @ingroup PrintingWidgets
* @brief A PrintingSelectorHost for hosts that only want the picked printing.
*/
#ifndef PRINTING_SELECTOR_SELECTION_HOST_H
#define PRINTING_SELECTOR_SELECTION_HOST_H
#include "printing_selector_host.h"
/**
* @brief Host for a PrintingSelector used outside a deck editor.
*
* There is no deck: the selector hides its deck-only controls and reports a
* picked printing through its own printingSelected() signal.
*/
class PrintingSelectorSelectionHost final : public PrintingSelectorHost
{
public:
[[nodiscard]] DeckStateManager *getDeckStateManager() const override
{
return nullptr;
}
void showCard(const ExactCard & /*card*/) override
{
}
void showRelatedCard(const QString & /*cardName*/) override
{
}
[[nodiscard]] bool isSelectionOnly() const override
{
return true;
}
};
#endif // PRINTING_SELECTOR_SELECTION_HOST_H