[TabDeckEditor] Create class to centralize deck state (#6459)

* create new file

* use QSharedPointer in DeckListModel

* [TabDeckEditor] Create class to centralize deck state

* delete method

* update docs
This commit is contained in:
RickyRister 2025-12-31 08:54:47 -08:00 committed by GitHub
parent 0085015ebe
commit b2dd8eed3f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 933 additions and 577 deletions

View file

@ -156,6 +156,7 @@ set(cockatrice_SOURCES
src/interface/widgets/deck_editor/deck_editor_filter_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_dock_widget.cpp
src/interface/widgets/deck_editor/deck_list_style_proxy.cpp src/interface/widgets/deck_editor/deck_list_style_proxy.cpp
src/interface/widgets/deck_editor/deck_state_manager.cpp
src/interface/widgets/general/background_sources.cpp src/interface/widgets/general/background_sources.cpp
src/interface/widgets/general/display/background_plate_widget.cpp src/interface/widgets/general/display/background_plate_widget.cpp
src/interface/widgets/general/display/banner_widget.cpp src/interface/widgets/general/display/banner_widget.cpp

View file

@ -1,8 +1,8 @@
#include "deck_editor_deck_dock_widget.h" #include "deck_editor_deck_dock_widget.h"
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../../deck_loader/deck_loader.h"
#include "deck_list_style_proxy.h" #include "deck_list_style_proxy.h"
#include "deck_state_manager.h"
#include <QComboBox> #include <QComboBox>
#include <QDockWidget> #include <QDockWidget>
@ -38,7 +38,7 @@ static int findRestoreIndex(const CardRef &wanted, const QComboBox *combo)
} }
DeckEditorDeckDockWidget::DeckEditorDeckDockWidget(AbstractTabDeckEditor *parent) DeckEditorDeckDockWidget::DeckEditorDeckDockWidget(AbstractTabDeckEditor *parent)
: QDockWidget(parent), deckEditor(parent) : QDockWidget(parent), deckEditor(parent), deckStateManager(parent->deckStateManager)
{ {
setObjectName("deckDock"); setObjectName("deckDock");
@ -52,19 +52,19 @@ DeckEditorDeckDockWidget::DeckEditorDeckDockWidget(AbstractTabDeckEditor *parent
void DeckEditorDeckDockWidget::createDeckDock() void DeckEditorDeckDockWidget::createDeckDock()
{ {
deckModel = new DeckListModel(this); connect(getModel(), &DeckListModel::deckHashChanged, this, &DeckEditorDeckDockWidget::updateHash);
deckModel->setObjectName("deckModel");
connect(deckModel, &DeckListModel::deckHashChanged, this, &DeckEditorDeckDockWidget::updateHash);
deckLoader = new DeckLoader(this);
proxy = new DeckListStyleProxy(this); proxy = new DeckListStyleProxy(this);
proxy->setSourceModel(deckModel); proxy->setSourceModel(getModel());
historyManagerWidget = new DeckListHistoryManagerWidget(deckModel, proxy, deckEditor->getHistoryManager(), this); historyManagerWidget = new DeckListHistoryManagerWidget(deckStateManager, proxy, this);
connect(historyManagerWidget, &DeckListHistoryManagerWidget::requestDisplayWidgetSync, this, connect(historyManagerWidget, &DeckListHistoryManagerWidget::requestDisplayWidgetSync, this,
&DeckEditorDeckDockWidget::syncDisplayWidgetsToModel); &DeckEditorDeckDockWidget::syncDisplayWidgetsToModel);
connect(deckStateManager, &DeckStateManager::focusIndexChanged, this, &DeckEditorDeckDockWidget::setSelectedIndex);
connect(deckStateManager, &DeckStateManager::deckReplaced, this,
&DeckEditorDeckDockWidget::syncDisplayWidgetsToModel);
deckView = new QTreeView(); deckView = new QTreeView();
deckView->setObjectName("deckView"); deckView->setObjectName("deckView");
deckView->setModel(proxy); deckView->setModel(proxy);
@ -97,7 +97,7 @@ void DeckEditorDeckDockWidget::createDeckDock()
nameDebounceTimer = new QTimer(this); nameDebounceTimer = new QTimer(this);
nameDebounceTimer->setSingleShot(true); nameDebounceTimer->setSingleShot(true);
nameDebounceTimer->setInterval(300); // debounce duration in ms nameDebounceTimer->setInterval(300); // debounce duration in ms
connect(nameDebounceTimer, &QTimer::timeout, this, [this]() { updateName(nameEdit->text()); }); connect(nameDebounceTimer, &QTimer::timeout, this, &DeckEditorDeckDockWidget::writeName);
connect(nameEdit, &LineEditUnfocusable::textChanged, this, [this]() { connect(nameEdit, &LineEditUnfocusable::textChanged, this, [this]() {
nameDebounceTimer->start(); // restart debounce timer nameDebounceTimer->start(); // restart debounce timer
@ -141,7 +141,7 @@ void DeckEditorDeckDockWidget::createDeckDock()
commentsDebounceTimer = new QTimer(this); commentsDebounceTimer = new QTimer(this);
commentsDebounceTimer->setSingleShot(true); commentsDebounceTimer->setSingleShot(true);
commentsDebounceTimer->setInterval(400); // longer debounce for multi-line commentsDebounceTimer->setInterval(400); // longer debounce for multi-line
connect(commentsDebounceTimer, &QTimer::timeout, this, [this]() { updateComments(); }); connect(commentsDebounceTimer, &QTimer::timeout, this, &DeckEditorDeckDockWidget::writeComments);
connect(commentsEdit, &QTextEdit::textChanged, this, [this]() { connect(commentsEdit, &QTextEdit::textChanged, this, [this]() {
commentsDebounceTimer->start(); // restart debounce timer commentsDebounceTimer->start(); // restart debounce timer
@ -152,21 +152,21 @@ void DeckEditorDeckDockWidget::createDeckDock()
bannerCardLabel->setText(tr("Banner Card")); bannerCardLabel->setText(tr("Banner Card"));
bannerCardLabel->setHidden(!SettingsCache::instance().getDeckEditorBannerCardComboBoxVisible()); bannerCardLabel->setHidden(!SettingsCache::instance().getDeckEditorBannerCardComboBoxVisible());
bannerCardComboBox = new QComboBox(this); bannerCardComboBox = new QComboBox(this);
connect(deckModel, &DeckListModel::dataChanged, this, [this]() { connect(getModel(), &DeckListModel::dataChanged, this, [this]() {
// Delay the update to avoid race conditions // Delay the update to avoid race conditions
QTimer::singleShot(100, this, &DeckEditorDeckDockWidget::updateBannerCardComboBox); QTimer::singleShot(100, this, &DeckEditorDeckDockWidget::updateBannerCardComboBox);
}); });
connect(deckModel, &DeckListModel::cardAddedAt, this, &DeckEditorDeckDockWidget::recursiveExpand); connect(getModel(), &DeckListModel::cardAddedAt, this, &DeckEditorDeckDockWidget::recursiveExpand);
connect(deckModel, &DeckListModel::modelReset, this, &DeckEditorDeckDockWidget::expandAll); connect(getModel(), &DeckListModel::modelReset, this, &DeckEditorDeckDockWidget::expandAll);
connect(bannerCardComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, connect(bannerCardComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&DeckEditorDeckDockWidget::setBannerCard); &DeckEditorDeckDockWidget::writeBannerCard);
bannerCardComboBox->setHidden(!SettingsCache::instance().getDeckEditorBannerCardComboBoxVisible()); bannerCardComboBox->setHidden(!SettingsCache::instance().getDeckEditorBannerCardComboBoxVisible());
deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckModel->getDeckList()->getTags()); deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, {});
deckTagsDisplayWidget->setHidden(!SettingsCache::instance().getDeckEditorTagsWidgetVisible()); deckTagsDisplayWidget->setHidden(!SettingsCache::instance().getDeckEditorTagsWidgetVisible());
connect(deckTagsDisplayWidget, &DeckPreviewDeckTagsDisplayWidget::tagsChanged, this, connect(deckTagsDisplayWidget, &DeckPreviewDeckTagsDisplayWidget::tagsChanged, deckStateManager,
&DeckEditorDeckDockWidget::setTags); &DeckStateManager::setTags);
activeGroupCriteriaLabel = new QLabel(this); activeGroupCriteriaLabel = new QLabel(this);
@ -175,9 +175,9 @@ void DeckEditorDeckDockWidget::createDeckDock()
activeGroupCriteriaComboBox->addItem(tr("Mana Cost"), DeckListModelGroupCriteria::MANA_COST); activeGroupCriteriaComboBox->addItem(tr("Mana Cost"), DeckListModelGroupCriteria::MANA_COST);
activeGroupCriteriaComboBox->addItem(tr("Colors"), DeckListModelGroupCriteria::COLOR); activeGroupCriteriaComboBox->addItem(tr("Colors"), DeckListModelGroupCriteria::COLOR);
connect(activeGroupCriteriaComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), [this]() { connect(activeGroupCriteriaComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), [this]() {
deckModel->setActiveGroupCriteria(static_cast<DeckListModelGroupCriteria::Type>( getModel()->setActiveGroupCriteria(static_cast<DeckListModelGroupCriteria::Type>(
activeGroupCriteriaComboBox->currentData(Qt::UserRole).toInt())); activeGroupCriteriaComboBox->currentData(Qt::UserRole).toInt()));
deckModel->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder()); getModel()->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder());
}); });
aIncrement = new QAction(QString(), this); aIncrement = new QAction(QString(), this);
@ -295,9 +295,10 @@ void DeckEditorDeckDockWidget::initializeFormats()
formatComboBox->addItem(formatName, formatName); // store the raw key in itemData formatComboBox->addItem(formatName, formatName); // store the raw key in itemData
} }
if (!deckModel->getDeckList()->getGameFormat().isEmpty()) { QString format = deckStateManager->getMetadata().gameFormat;
deckModel->setActiveFormat(deckModel->getDeckList()->getGameFormat()); if (!format.isEmpty()) {
formatComboBox->setCurrentIndex(formatComboBox->findData(deckModel->getDeckList()->getGameFormat())); getModel()->setActiveFormat(format);
formatComboBox->setCurrentIndex(formatComboBox->findData(format));
} else { } else {
// Ensure no selection is visible initially // Ensure no selection is visible initially
formatComboBox->setCurrentIndex(-1); formatComboBox->setCurrentIndex(-1);
@ -306,11 +307,10 @@ void DeckEditorDeckDockWidget::initializeFormats()
connect(formatComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) { connect(formatComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) {
if (index >= 0) { if (index >= 0) {
QString formatKey = formatComboBox->itemData(index).toString(); QString formatKey = formatComboBox->itemData(index).toString();
deckModel->setActiveFormat(formatKey); deckStateManager->setFormat(formatKey);
} else { } else {
deckModel->setActiveFormat(QString()); // clear format if deselected deckStateManager->setFormat(""); // clear format if deselected
} }
emit deckModified();
}); });
} }
@ -340,43 +340,37 @@ ExactCard DeckEditorDeckDockWidget::getCurrentCard()
void DeckEditorDeckDockWidget::updateCard(const QModelIndex /*&current*/, const QModelIndex & /*previous*/) void DeckEditorDeckDockWidget::updateCard(const QModelIndex /*&current*/, const QModelIndex & /*previous*/)
{ {
if (ExactCard card = getCurrentCard()) { if (ExactCard card = getCurrentCard()) {
emit cardChanged(card); emit selectedCardChanged(card);
} }
} }
void DeckEditorDeckDockWidget::updateName(const QString &name) /**
* @brief Writes the contents of the name textBox to the DeckStateManager
*/
void DeckEditorDeckDockWidget::writeName()
{ {
emit requestDeckHistorySave( QString name = nameEdit->text();
QString(tr("Rename deck to \"%1\" from \"%2\"")).arg(name).arg(deckLoader->getDeck().deckList.getName())); deckStateManager->setName(name);
deckModel->getDeckList()->setName(name);
deckEditor->setModified(name.isEmpty());
emit nameChanged();
emit deckModified();
} }
void DeckEditorDeckDockWidget::updateComments() /**
* @brief Writes the contents of the comments textBox to the DeckStateManager
*/
void DeckEditorDeckDockWidget::writeComments()
{ {
emit requestDeckHistorySave(tr("Updated comments (was %1 chars, now %2 chars)") QString comments = commentsEdit->toPlainText();
.arg(deckLoader->getDeck().deckList.getComments().size()) deckStateManager->setComments(comments);
.arg(commentsEdit->toPlainText().size()));
deckModel->getDeckList()->setComments(commentsEdit->toPlainText());
deckEditor->setModified(commentsEdit->toPlainText().isEmpty());
emit commentsChanged();
emit deckModified();
} }
void DeckEditorDeckDockWidget::updateHash() void DeckEditorDeckDockWidget::updateHash()
{ {
hashLabel->setText(deckModel->getDeckList()->getDeckHash()); hashLabel->setText(deckStateManager->getDeckHash());
emit hashChanged();
emit deckModified();
} }
void DeckEditorDeckDockWidget::updateBannerCardComboBox() void DeckEditorDeckDockWidget::updateBannerCardComboBox()
{ {
// Store current banner card identity // Store current banner card identity
CardRef wanted = deckModel->getDeckList()->getBannerCard(); CardRef wanted = deckStateManager->getMetadata().bannerCard;
// Block signals temporarily // Block signals temporarily
bool wasBlocked = bannerCardComboBox->blockSignals(true); bool wasBlocked = bannerCardComboBox->blockSignals(true);
@ -386,7 +380,7 @@ void DeckEditorDeckDockWidget::updateBannerCardComboBox()
// Collect unique (name, providerId) pairs // Collect unique (name, providerId) pairs
QSet<QPair<QString, QString>> bannerCardSet; QSet<QPair<QString, QString>> bannerCardSet;
QList<CardRef> cardsInDeck = deckModel->getCardRefs(); QList<CardRef> cardsInDeck = getModel()->getCardRefs();
for (auto cardRef : cardsInDeck) { for (auto cardRef : cardsInDeck) {
if (!CardDatabaseManager::query()->getCard(cardRef)) { if (!CardDatabaseManager::query()->getCard(cardRef)) {
@ -415,7 +409,6 @@ void DeckEditorDeckDockWidget::updateBannerCardComboBox()
// Handle results // Handle results
if (restoreIndex != -1) { if (restoreIndex != -1) {
bannerCardComboBox->setCurrentIndex(restoreIndex); bannerCardComboBox->setCurrentIndex(restoreIndex);
syncDeckListBannerCardWithComboBox();
} else { } else {
// Add a placeholder "-" and set it as the current selection // Add a placeholder "-" and set it as the current selection
bannerCardComboBox->insertItem(0, "-"); bannerCardComboBox->insertItem(0, "-");
@ -426,25 +419,14 @@ void DeckEditorDeckDockWidget::updateBannerCardComboBox()
bannerCardComboBox->blockSignals(wasBlocked); bannerCardComboBox->blockSignals(wasBlocked);
} }
void DeckEditorDeckDockWidget::setBannerCard(int /* changedIndex */) /**
* @brief Writes the selected bannerCard to the DeckStateManager
*/
void DeckEditorDeckDockWidget::writeBannerCard(int index)
{ {
emit requestDeckHistorySave(tr("Banner card changed")); auto [name, id] = bannerCardComboBox->itemData(index).value<QPair<QString, QString>>();
syncDeckListBannerCardWithComboBox(); CardRef bannerCard = {name, id};
deckEditor->setModified(true); deckStateManager->setBannerCard(bannerCard);
emit deckModified();
}
void DeckEditorDeckDockWidget::setTags(const QStringList &tags)
{
deckModel->getDeckList()->setTags(tags);
deckEditor->setModified(true);
emit deckModified();
}
void DeckEditorDeckDockWidget::syncDeckListBannerCardWithComboBox()
{
auto [name, id] = bannerCardComboBox->currentData().value<QPair<QString, QString>>();
deckModel->getDeckList()->setBannerCard({name, id});
} }
void DeckEditorDeckDockWidget::updateShowBannerCardComboBox(const bool visible) void DeckEditorDeckDockWidget::updateShowBannerCardComboBox(const bool visible)
@ -460,7 +442,7 @@ void DeckEditorDeckDockWidget::updateShowTagsWidget(const bool visible)
void DeckEditorDeckDockWidget::syncBannerCardComboBoxSelectionWithDeck() void DeckEditorDeckDockWidget::syncBannerCardComboBoxSelectionWithDeck()
{ {
if (deckModel->getDeckList()->getBannerCard().name == "") { if (deckStateManager->getMetadata().bannerCard.name == "") {
if (bannerCardComboBox->findText("-") != -1) { if (bannerCardComboBox->findText("-") != -1) {
bannerCardComboBox->setCurrentIndex(bannerCardComboBox->findText("-")); bannerCardComboBox->setCurrentIndex(bannerCardComboBox->findText("-"));
} else { } else {
@ -468,36 +450,26 @@ void DeckEditorDeckDockWidget::syncBannerCardComboBoxSelectionWithDeck()
bannerCardComboBox->setCurrentIndex(0); bannerCardComboBox->setCurrentIndex(0);
} }
} else { } else {
bannerCardComboBox->setCurrentText(deckModel->getDeckList()->getBannerCard().name); bannerCardComboBox->setCurrentText(deckStateManager->getMetadata().bannerCard.name);
} }
} }
/** void DeckEditorDeckDockWidget::setSelectedIndex(const QModelIndex &newCardIndex)
* Sets the currently active deck for this tab
* @param _deck The deck.
*/
void DeckEditorDeckDockWidget::setDeck(const LoadedDeck &_deck)
{ {
deckLoader->setDeck(_deck); deckView->clearSelection();
deckModel->setDeckList(&deckLoader->getDeck().deckList); deckView->setCurrentIndex(newCardIndex);
connect(deckLoader, &DeckLoader::deckLoaded, deckModel, &DeckListModel::rebuildTree); recursiveExpand(newCardIndex);
deckView->setFocus(Qt::FocusReason::MouseFocusReason);
emit requestDeckHistoryClear();
historyManagerWidget->setDeckListModel(deckModel);
syncDisplayWidgetsToModel();
emit deckChanged();
} }
void DeckEditorDeckDockWidget::syncDisplayWidgetsToModel() void DeckEditorDeckDockWidget::syncDisplayWidgetsToModel()
{ {
nameEdit->blockSignals(true); nameEdit->blockSignals(true);
nameEdit->setText(deckModel->getDeckList()->getName()); nameEdit->setText(deckStateManager->getMetadata().name);
nameEdit->blockSignals(false); nameEdit->blockSignals(false);
commentsEdit->blockSignals(true); commentsEdit->blockSignals(true);
commentsEdit->setText(deckModel->getDeckList()->getComments()); commentsEdit->setText(deckStateManager->getMetadata().comments);
commentsEdit->blockSignals(false); commentsEdit->blockSignals(false);
bannerCardComboBox->blockSignals(true); bannerCardComboBox->blockSignals(true);
@ -507,44 +479,22 @@ void DeckEditorDeckDockWidget::syncDisplayWidgetsToModel()
updateHash(); updateHash();
sortDeckModelToDeckView(); sortDeckModelToDeckView();
deckTagsDisplayWidget->setTags(deckModel->getDeckList()->getTags()); deckTagsDisplayWidget->setTags(deckStateManager->getMetadata().tags);
} }
void DeckEditorDeckDockWidget::sortDeckModelToDeckView() void DeckEditorDeckDockWidget::sortDeckModelToDeckView()
{ {
deckModel->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder()); getModel()->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder());
deckModel->setActiveFormat(deckModel->getDeckList()->getGameFormat()); getModel()->setActiveFormat(deckStateManager->getMetadata().gameFormat);
formatComboBox->setCurrentIndex(formatComboBox->findData(deckModel->getDeckList()->getGameFormat())); formatComboBox->setCurrentIndex(formatComboBox->findData(deckStateManager->getMetadata().gameFormat));
emit deckChanged();
}
DeckLoader *DeckEditorDeckDockWidget::getDeckLoader()
{
return deckLoader;
}
const DeckList &DeckEditorDeckDockWidget::getDeckList() const
{
return *deckModel->getDeckList();
} }
/** /**
* Resets the tab to the state for a blank new tab. * @brief Convenience method to get the underlying model instance from the DeckStateManager
*/ */
void DeckEditorDeckDockWidget::cleanDeck() DeckListModel *DeckEditorDeckDockWidget::getModel() const
{ {
deckModel->cleanList(); return deckStateManager->getModel();
nameEdit->setText(QString());
emit nameChanged();
commentsEdit->setText(QString());
emit commentsChanged();
hashLabel->setText(QString());
emit hashChanged();
emit deckModified();
emit deckChanged();
updateBannerCardComboBox();
deckTagsDisplayWidget->setTags(deckModel->getDeckList()->getTags());
} }
void DeckEditorDeckDockWidget::selectPrevCard() void DeckEditorDeckDockWidget::selectPrevCard()
@ -635,7 +585,7 @@ QModelIndexList DeckEditorDeckDockWidget::getSelectedCardNodes() const
auto selectedRows = deckView->selectionModel()->selectedRows(); auto selectedRows = deckView->selectionModel()->selectedRows();
const auto notLeafNode = [this](const QModelIndex &index) { const auto notLeafNode = [this](const QModelIndex &index) {
return deckModel->hasChildren(proxy->mapToSource(index)); return getModel()->hasChildren(proxy->mapToSource(index));
}; };
selectedRows.erase(std::remove_if(selectedRows.begin(), selectedRows.end(), notLeafNode), selectedRows.end()); selectedRows.erase(std::remove_if(selectedRows.begin(), selectedRows.end(), notLeafNode), selectedRows.end());
@ -650,21 +600,7 @@ void DeckEditorDeckDockWidget::actAddCard(const ExactCard &card, const QString &
} }
QString zoneName = card.getInfo().getIsToken() ? DECK_ZONE_TOKENS : _zoneName; QString zoneName = card.getInfo().getIsToken() ? DECK_ZONE_TOKENS : _zoneName;
deckStateManager->addCard(card, zoneName);
emit requestDeckHistorySave(tr("Added (%1): %2 (%3) %4")
.arg(zoneName, card.getName(), card.getPrinting().getSet()->getCorrectedShortName(),
card.getPrinting().getProperty("num")));
QModelIndex newCardIndex = deckModel->addCard(card, zoneName);
if (!newCardIndex.isValid()) {
return;
}
deckView->clearSelection();
deckView->setCurrentIndex(newCardIndex);
emit deckModified();
} }
void DeckEditorDeckDockWidget::actIncrementSelection() void DeckEditorDeckDockWidget::actIncrementSelection()
@ -681,12 +617,12 @@ void DeckEditorDeckDockWidget::actSwapCard(const ExactCard &card, const QString
QString providerId = card.getPrinting().getUuid(); QString providerId = card.getPrinting().getUuid();
QString collectorNumber = card.getPrinting().getProperty("num"); QString collectorNumber = card.getPrinting().getProperty("num");
QModelIndex foundCard = deckModel->findCard(card.getName(), zoneName, providerId, collectorNumber); QModelIndex foundCard = getModel()->findCard(card.getName(), zoneName, providerId, collectorNumber);
if (!foundCard.isValid()) { if (!foundCard.isValid()) {
foundCard = deckModel->findCard(card.getName(), zoneName); foundCard = getModel()->findCard(card.getName(), zoneName);
} }
swapCard(foundCard); deckStateManager->swapCardAtIndex(foundCard);
} }
void DeckEditorDeckDockWidget::actSwapSelection() void DeckEditorDeckDockWidget::actSwapSelection()
@ -699,54 +635,15 @@ void DeckEditorDeckDockWidget::actSwapSelection()
deckView->setSelectionMode(QAbstractItemView::SingleSelection); deckView->setSelectionMode(QAbstractItemView::SingleSelection);
} }
bool isModified = false;
for (const auto &currentIndex : selectedRows) { for (const auto &currentIndex : selectedRows) {
if (swapCard(currentIndex)) { deckStateManager->swapCardAtIndex(currentIndex);
isModified = true;
}
} }
deckView->setSelectionMode(QAbstractItemView::ExtendedSelection); deckView->setSelectionMode(QAbstractItemView::ExtendedSelection);
if (isModified) {
emit deckModified();
}
update(); update();
} }
/**
* Swaps the card at the index between the maindeck and sideboard
*
* @param currentIndex The index to swap.
* @return True if the swap was successful
*/
bool DeckEditorDeckDockWidget::swapCard(const QModelIndex &currentIndex)
{
if (!currentIndex.isValid())
return false;
const QString cardName = currentIndex.siblingAtColumn(DeckListModelColumns::CARD_NAME).data().toString();
const QString cardProviderID =
currentIndex.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data().toString();
const QModelIndex gparent = currentIndex.parent().parent();
if (!gparent.isValid())
return false;
const QString zoneName = gparent.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
offsetCountAtIndex(currentIndex, false);
const QString otherZoneName = zoneName == DECK_ZONE_MAIN ? DECK_ZONE_SIDE : DECK_ZONE_MAIN;
if (ExactCard card = CardDatabaseManager::query()->getCard({cardName, cardProviderID})) {
deckModel->addCard(card, otherZoneName);
} else {
// Third argument (true) says create the card no matter what, even if not in DB
deckModel->addPreferredPrintingCard(cardName, otherZoneName, true);
}
return true;
}
void DeckEditorDeckDockWidget::actDecrementCard(const ExactCard &card, QString zoneName) void DeckEditorDeckDockWidget::actDecrementCard(const ExactCard &card, QString zoneName)
{ {
if (!card) if (!card)
@ -754,17 +651,7 @@ void DeckEditorDeckDockWidget::actDecrementCard(const ExactCard &card, QString z
if (card.getInfo().getIsToken()) if (card.getInfo().getIsToken())
zoneName = DECK_ZONE_TOKENS; zoneName = DECK_ZONE_TOKENS;
QString providerId = card.getPrinting().getUuid(); deckStateManager->decrementCard(card, zoneName);
QString collectorNumber = card.getPrinting().getProperty("num");
QModelIndex idx = deckModel->findCard(card.getName(), zoneName, providerId, collectorNumber);
if (!idx.isValid()) {
return;
}
deckView->clearSelection();
deckView->setCurrentIndex(proxy->mapToSource(idx));
offsetCountAtIndex(idx, false);
} }
void DeckEditorDeckDockWidget::actDecrementSelection() void DeckEditorDeckDockWidget::actDecrementSelection()
@ -794,25 +681,11 @@ void DeckEditorDeckDockWidget::actRemoveCard()
deckView->setSelectionMode(QAbstractItemView::SingleSelection); deckView->setSelectionMode(QAbstractItemView::SingleSelection);
} }
bool isModified = false; for (const auto &row : selectedRows) {
for (const auto &index : selectedRows) { deckStateManager->removeCardAtIndex(row);
if (!index.isValid() || deckModel->hasChildren(index)) {
continue;
}
QModelIndex sourceIndex = proxy->mapToSource(index);
QString cardName = sourceIndex.siblingAtColumn(DeckListModelColumns::CARD_NAME).data().toString();
emit requestDeckHistorySave(QString(tr("Removed \"%1\" (all copies)")).arg(cardName));
deckModel->removeRow(sourceIndex.row(), sourceIndex.parent());
isModified = true;
} }
deckView->setSelectionMode(QAbstractItemView::ExtendedSelection); deckView->setSelectionMode(QAbstractItemView::ExtendedSelection);
if (isModified) {
emit deckModified();
}
} }
/** /**
@ -822,28 +695,17 @@ void DeckEditorDeckDockWidget::actRemoveCard()
*/ */
void DeckEditorDeckDockWidget::offsetCountAtIndex(const QModelIndex &idx, bool isIncrement) void DeckEditorDeckDockWidget::offsetCountAtIndex(const QModelIndex &idx, bool isIncrement)
{ {
if (!idx.isValid() || deckModel->hasChildren(idx)) { if (!idx.isValid() || getModel()->hasChildren(idx)) {
return; return;
} }
QModelIndex sourceIndex = proxy->mapToSource(idx); QModelIndex sourceIndex = proxy->mapToSource(idx);
QString cardName = sourceIndex.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString(); if (isIncrement) {
QString providerId = deckStateManager->incrementCountAtIndex(sourceIndex);
sourceIndex.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data(Qt::DisplayRole).toString(); } else {
deckStateManager->decrementCountAtIndex(sourceIndex);
const auto reason = QString(tr("%1 %2 × \"%3\" (%4)")) }
.arg(isIncrement ? tr("Added") : tr("Removed"))
.arg(1)
.arg(cardName)
.arg(providerId);
emit requestDeckHistorySave(reason);
int offset = isIncrement ? 1 : -1;
deckModel->offsetCountAtIndex(sourceIndex, offset);
emit deckModified();
} }
void DeckEditorDeckDockWidget::decklistCustomMenu(QPoint point) void DeckEditorDeckDockWidget::decklistCustomMenu(QPoint point)

View file

@ -28,22 +28,14 @@ class DeckEditorDeckDockWidget : public QDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit DeckEditorDeckDockWidget(AbstractTabDeckEditor *parent); explicit DeckEditorDeckDockWidget(AbstractTabDeckEditor *parent);
DeckLoader *deckLoader;
DeckListStyleProxy *proxy; DeckListStyleProxy *proxy;
DeckListModel *deckModel;
QTreeView *deckView; QTreeView *deckView;
QComboBox *bannerCardComboBox; QComboBox *bannerCardComboBox;
void createDeckDock(); void createDeckDock();
ExactCard getCurrentCard(); ExactCard getCurrentCard();
void retranslateUi(); void retranslateUi();
QString getDeckName()
{
return nameEdit->text();
}
QString getSimpleDeckName()
{
return nameEdit->text().simplified();
}
QComboBox *getGroupByComboBox() QComboBox *getGroupByComboBox()
{ {
return activeGroupCriteriaComboBox; return activeGroupCriteriaComboBox;
@ -55,15 +47,11 @@ public:
} }
public slots: public slots:
void cleanDeck();
void selectPrevCard(); void selectPrevCard();
void selectNextCard(); void selectNextCard();
void updateBannerCardComboBox(); void updateBannerCardComboBox();
void setDeck(const LoadedDeck &_deck);
void syncDisplayWidgetsToModel(); void syncDisplayWidgetsToModel();
void sortDeckModelToDeckView(); void sortDeckModelToDeckView();
DeckLoader *getDeckLoader();
const DeckList &getDeckList() const;
void actAddCard(const ExactCard &card, const QString &zoneName); void actAddCard(const ExactCard &card, const QString &zoneName);
void actIncrementSelection(); void actIncrementSelection();
void actDecrementCard(const ExactCard &card, QString zoneName); void actDecrementCard(const ExactCard &card, QString zoneName);
@ -74,17 +62,12 @@ public slots:
void initializeFormats(); void initializeFormats();
signals: signals:
void nameChanged(); void selectedCardChanged(const ExactCard &card);
void commentsChanged();
void hashChanged();
void deckChanged();
void deckModified();
void requestDeckHistorySave(const QString &modificationReason);
void requestDeckHistoryClear();
void cardChanged(const ExactCard &_card);
private: private:
AbstractTabDeckEditor *deckEditor; AbstractTabDeckEditor *deckEditor;
DeckStateManager *deckStateManager;
DeckListHistoryManagerWidget *historyManagerWidget; DeckListHistoryManagerWidget *historyManagerWidget;
KeySignals deckViewKeySignals; KeySignals deckViewKeySignals;
QLabel *nameLabel; QLabel *nameLabel;
@ -107,18 +90,17 @@ private:
QAction *aRemoveCard, *aIncrement, *aDecrement, *aSwapCard; QAction *aRemoveCard, *aIncrement, *aDecrement, *aSwapCard;
DeckListModel *getModel() const;
[[nodiscard]] QModelIndexList getSelectedCardNodes() const; [[nodiscard]] QModelIndexList getSelectedCardNodes() const;
void offsetCountAtIndex(const QModelIndex &idx, bool isIncrement); void offsetCountAtIndex(const QModelIndex &idx, bool isIncrement);
private slots: private slots:
void decklistCustomMenu(QPoint point); void decklistCustomMenu(QPoint point);
bool swapCard(const QModelIndex &currentIndex);
void updateCard(QModelIndex, const QModelIndex &current); void updateCard(QModelIndex, const QModelIndex &current);
void updateName(const QString &name); void writeName();
void updateComments(); void writeComments();
void setBannerCard(int); void writeBannerCard(int);
void setTags(const QStringList &tags); void setSelectedIndex(const QModelIndex &newCardIndex);
void syncDeckListBannerCardWithComboBox();
void updateHash(); void updateHash();
void refreshShortcuts(); void refreshShortcuts();
void updateShowBannerCardComboBox(bool visible); void updateShowBannerCardComboBox(bool visible);

View file

@ -1,10 +1,11 @@
#include "deck_list_history_manager_widget.h" #include "deck_list_history_manager_widget.h"
DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckListModel *_deckListModel, #include "deck_state_manager.h"
DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckStateManager *_deckStateManager,
DeckListStyleProxy *_styleProxy, DeckListStyleProxy *_styleProxy,
DeckListHistoryManager *manager,
QWidget *parent) QWidget *parent)
: QWidget(parent), deckListModel(_deckListModel), styleProxy(_styleProxy), historyManager(manager) : QWidget(parent), deckStateManager(_deckStateManager), styleProxy(_styleProxy)
{ {
layout = new QHBoxLayout(this); layout = new QHBoxLayout(this);
@ -43,8 +44,7 @@ DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckListModel *_deckL
connect(historyList, &QListWidget::itemClicked, this, &DeckListHistoryManagerWidget::onListClicked); connect(historyList, &QListWidget::itemClicked, this, &DeckListHistoryManagerWidget::onListClicked);
connect(historyManager, &DeckListHistoryManager::undoRedoStateChanged, this, connect(deckStateManager, &DeckStateManager::historyChanged, this, &DeckListHistoryManagerWidget::refreshList);
&DeckListHistoryManagerWidget::refreshList);
refreshList(); refreshList();
retranslateUi(); retranslateUi();
@ -58,15 +58,12 @@ void DeckListHistoryManagerWidget::retranslateUi()
historyLabel->setText(tr("Click on an entry to revert to that point in the history.")); historyLabel->setText(tr("Click on an entry to revert to that point in the history."));
} }
void DeckListHistoryManagerWidget::setDeckListModel(DeckListModel *_deckListModel)
{
deckListModel = _deckListModel;
}
void DeckListHistoryManagerWidget::refreshList() void DeckListHistoryManagerWidget::refreshList()
{ {
historyList->clear(); historyList->clear();
DeckListHistoryManager *historyManager = deckStateManager->getHistoryManager();
// Fill redo section first (oldest redo at top, newest redo closest to divider) // Fill redo section first (oldest redo at top, newest redo closest to divider)
const auto redoStack = historyManager->getRedoStack(); const auto redoStack = historyManager->getRedoStack();
for (int i = 0; i < redoStack.size(); ++i) { // iterate forward for (int i = 0; i < redoStack.size(); ++i) { // iterate forward
@ -98,36 +95,7 @@ void DeckListHistoryManagerWidget::refreshList()
redoButton->setEnabled(historyManager->canRedo()); redoButton->setEnabled(historyManager->canRedo());
} }
void DeckListHistoryManagerWidget::doUndo() void DeckListHistoryManagerWidget::onListClicked(const QListWidgetItem *item)
{
if (!historyManager->canUndo()) {
return;
}
historyManager->undo(deckListModel->getDeckList());
deckListModel->rebuildTree();
emit deckListModel->layoutChanged();
emit requestDisplayWidgetSync();
refreshList();
}
void DeckListHistoryManagerWidget::doRedo()
{
if (!historyManager->canRedo()) {
return;
}
historyManager->redo(deckListModel->getDeckList());
deckListModel->rebuildTree();
emit deckListModel->layoutChanged();
emit requestDisplayWidgetSync();
refreshList();
}
void DeckListHistoryManagerWidget::onListClicked(QListWidgetItem *item)
{ {
// Ignore non-selectable items (like divider) // Ignore non-selectable items (like divider)
if (!(item->flags() & Qt::ItemIsSelectable)) { if (!(item->flags() & Qt::ItemIsSelectable)) {
@ -138,23 +106,24 @@ void DeckListHistoryManagerWidget::onListClicked(QListWidgetItem *item)
int index = item->data(Qt::UserRole + 1).toInt(); int index = item->data(Qt::UserRole + 1).toInt();
if (mode == "redo") { if (mode == "redo") {
const auto redoStack = historyManager->getRedoStack(); const auto redoStack = deckStateManager->getHistoryManager()->getRedoStack();
int steps = redoStack.size() - index; int steps = redoStack.size() - index;
for (int i = 0; i < steps; ++i) { deckStateManager->redo(steps);
historyManager->redo(deckListModel->getDeckList());
}
} else if (mode == "undo") { } else if (mode == "undo") {
const auto undoStack = historyManager->getUndoStack(); const auto undoStack = deckStateManager->getHistoryManager()->getUndoStack();
int steps = undoStack.size() - 1 - index; int steps = undoStack.size() - index;
for (int i = 0; i < steps + 1; ++i) { deckStateManager->undo(steps);
historyManager->undo(deckListModel->getDeckList());
}
} }
deckListModel->rebuildTree();
emit deckListModel->layoutChanged();
emit requestDisplayWidgetSync();
refreshList(); refreshList();
}
void DeckListHistoryManagerWidget::doUndo()
{
deckStateManager->undo();
}
void DeckListHistoryManagerWidget::doRedo()
{
deckStateManager->redo();
} }

View file

@ -14,6 +14,8 @@
#include <libcockatrice/deck_list/deck_list_history_manager.h> #include <libcockatrice/deck_list/deck_list_history_manager.h>
#include <libcockatrice/models/deck_list/deck_list_model.h> #include <libcockatrice/models/deck_list/deck_list_model.h>
class DeckStateManager;
class DeckListHistoryManagerWidget : public QWidget class DeckListHistoryManagerWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
@ -25,22 +27,19 @@ public slots:
void retranslateUi(); void retranslateUi();
public: public:
explicit DeckListHistoryManagerWidget(DeckListModel *deckListModel, explicit DeckListHistoryManagerWidget(DeckStateManager *deckStateManager,
DeckListStyleProxy *styleProxy, DeckListStyleProxy *styleProxy,
DeckListHistoryManager *manager,
QWidget *parent = nullptr); QWidget *parent = nullptr);
void setDeckListModel(DeckListModel *_deckListModel);
private slots: private slots:
void refreshList(); void refreshList();
void onListClicked(QListWidgetItem *item); void onListClicked(const QListWidgetItem *item);
void doUndo(); void doUndo();
void doRedo(); void doRedo();
private: private:
DeckListModel *deckListModel; DeckStateManager *deckStateManager;
DeckListStyleProxy *styleProxy; DeckListStyleProxy *styleProxy;
DeckListHistoryManager *historyManager;
QHBoxLayout *layout; QHBoxLayout *layout;
QAction *aUndo; QAction *aUndo;

View file

@ -0,0 +1,361 @@
#include "deck_state_manager.h"
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/deck_list/deck_list_history_manager.h>
DeckStateManager::DeckStateManager(QObject *parent)
: QObject(parent), deckList(QSharedPointer<DeckList>(new DeckList)),
deckListModel(new DeckListModel(this, deckList)), historyManager(new DeckListHistoryManager(this))
{
connect(historyManager, &DeckListHistoryManager::undoRedoStateChanged, this, [this] {
setModified(true);
emit historyChanged();
});
connect(deckListModel, &DeckListModel::rowsInserted, this, &DeckStateManager::uniqueCardsChanged);
connect(deckListModel, &DeckListModel::rowsRemoved, this, &DeckStateManager::uniqueCardsChanged);
}
const DeckList &DeckStateManager::getDeckList() const
{
return *deckList.get();
}
LoadedDeck DeckStateManager::toLoadedDeck() const
{
return {getDeckList(), lastLoadInfo};
}
DeckList::Metadata const &DeckStateManager::getMetadata() const
{
return deckList->getMetadata();
}
QString DeckStateManager::getSimpleDeckName() const
{
return deckList->getMetadata().name.simplified();
}
QString DeckStateManager::getDeckHash() const
{
return deckList->getDeckHash();
}
bool DeckStateManager::isModified() const
{
return modified;
}
void DeckStateManager::setModified(bool state)
{
if (state == modified) {
return;
}
modified = state;
emit isModifiedChanged(modified);
}
bool DeckStateManager::isBlankNewDeck() const
{
return !isModified() && deckList->isBlankDeck();
}
void DeckStateManager::replaceDeck(const LoadedDeck &deck)
{
lastLoadInfo = deck.lastLoadInfo;
deckList = QSharedPointer<DeckList>(new DeckList(deck.deckList));
deckListModel->setDeckList(deckList);
historyManager->clear();
setModified(false);
emit deckReplaced();
}
void DeckStateManager::clearDeck()
{
replaceDeck(LoadedDeck());
}
bool DeckStateManager::modifyDeck(const QString &reason, const std::function<bool(DeckListModel *)> &operation)
{
DeckListMemento memento = deckList->createMemento(reason);
bool success = operation(deckListModel);
if (success) {
historyManager->save(memento);
doCardModified();
}
return success;
}
QModelIndex DeckStateManager::modifyDeck(const QString &reason,
const std::function<QModelIndex(DeckListModel *)> &operation)
{
DeckListMemento memento = deckList->createMemento(reason);
QModelIndex idx = operation(deckListModel);
if (idx.isValid()) {
historyManager->save(memento);
doCardModified();
}
return idx;
}
void DeckStateManager::setName(const QString &name)
{
QString previous = deckList->getName();
if (previous == name) {
return;
}
requestHistorySave(tr("Rename deck to \"%1\" from \"%2\"").arg(name).arg(previous));
deckList->setName(name);
doMetadataModified();
}
void DeckStateManager::setComments(const QString &comments)
{
QString previous = deckList->getComments();
if (previous == comments) {
return;
}
requestHistorySave(tr("Updated comments (was %1 chars, now %2 chars)").arg(previous.size()).arg(comments.size()));
deckList->setComments(comments);
doMetadataModified();
}
void DeckStateManager::setBannerCard(const CardRef &bannerCard)
{
CardRef previous = deckList->getBannerCard();
if (previous == bannerCard) {
return;
}
requestHistorySave(tr("Set banner card to %1 (%2)").arg(bannerCard.name).arg(bannerCard.providerId));
deckList->setBannerCard(bannerCard);
doMetadataModified();
}
void DeckStateManager::setTags(const QStringList &tags)
{
QStringList previous = deckList->getTags();
if (previous == tags) {
return;
}
requestHistorySave(tr("Tags changed"));
deckList->setTags(tags);
doMetadataModified();
}
void DeckStateManager::setFormat(const QString &format)
{
if (deckList->getMetadata().gameFormat == format) {
return;
}
requestHistorySave(tr("Set format to %1").arg(format));
deckListModel->setActiveFormat(format);
doMetadataModified();
}
QModelIndex DeckStateManager::addCard(const ExactCard &card, const QString &zoneName)
{
if (!card) {
return {};
}
QString reason = tr("Added (%1): %2 (%3) %4")
.arg(zoneName, card.getName(), card.getPrinting().getSet()->getCorrectedShortName(),
card.getPrinting().getProperty("num"));
QModelIndex idx = modifyDeck(reason, [&card, &zoneName](auto model) { return model->addCard(card, zoneName); });
if (idx.isValid()) {
emit focusIndexChanged(idx);
}
return idx;
}
QModelIndex DeckStateManager::decrementCard(const ExactCard &card, const QString &zoneName)
{
if (!card)
return {};
QString providerId = card.getPrinting().getUuid();
QString collectorNumber = card.getPrinting().getProperty("num");
QModelIndex idx = deckListModel->findCard(card.getName(), zoneName, providerId, collectorNumber);
if (!idx.isValid()) {
return {};
}
bool success = offsetCountAtIndex(idx, false);
if (!success) {
return {};
}
if (idx.isValid()) {
emit focusIndexChanged(idx);
}
return idx;
}
static bool doSwapCard(DeckListModel *model,
const QModelIndex &idx,
const QString &cardName,
const QString &providerId,
const QString &otherZone)
{
bool success = model->offsetCountAtIndex(idx, -1);
if (!success) {
return false;
}
if (ExactCard card = CardDatabaseManager::query()->getCard({cardName, providerId})) {
model->addCard(card, otherZone);
} else {
// Third argument (true) says create the card no matter what, even if not in DB
model->addPreferredPrintingCard(cardName, otherZone, true);
}
return true;
}
bool DeckStateManager::swapCardAtIndex(const QModelIndex &idx)
{
if (!idx.isValid())
return false;
QString cardName = idx.siblingAtColumn(DeckListModelColumns::CARD_NAME).data().toString();
QString providerId = idx.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data().toString();
QModelIndex gparent = idx.parent().parent();
if (!gparent.isValid())
return false;
QString zoneName = gparent.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
QString otherZoneName = zoneName == DECK_ZONE_MAIN ? DECK_ZONE_SIDE : DECK_ZONE_MAIN;
QString reason = tr("Moved to %1 1 × \"%2\" (%3)") //
.arg(otherZoneName)
.arg(cardName)
.arg(providerId);
return modifyDeck(reason, [&idx, &cardName, &providerId, &otherZoneName](auto model) {
return doSwapCard(model, idx, cardName, providerId, otherZoneName);
});
}
bool DeckStateManager::removeCardAtIndex(const QModelIndex &idx)
{
if (!idx.isValid() || deckListModel->hasChildren(idx)) {
return false;
}
QString cardName = idx.siblingAtColumn(DeckListModelColumns::CARD_NAME).data().toString();
QString reason = tr("Removed \"%1\" (all copies)").arg(cardName);
return modifyDeck(reason, [&idx](auto model) { return model->removeRow(idx.row(), idx.parent()); });
}
bool DeckStateManager::incrementCountAtIndex(const QModelIndex &idx)
{
return offsetCountAtIndex(idx, 1);
}
bool DeckStateManager::decrementCountAtIndex(const QModelIndex &idx)
{
return offsetCountAtIndex(idx, -1);
}
bool DeckStateManager::offsetCountAtIndex(const QModelIndex &idx, int offset)
{
if (!idx.isValid()) {
return false;
}
QString cardName = idx.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
QString providerId = idx.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data(Qt::DisplayRole).toString();
QString reason = tr("%1 1 × \"%2\" (%3)") //
.arg(offset > 0 ? tr("Added") : tr("Removed"))
.arg(cardName)
.arg(providerId);
return modifyDeck(reason, [&idx, &offset](auto model) { return model->offsetCountAtIndex(idx, offset); });
}
void DeckStateManager::undo(int steps)
{
if (!historyManager->canUndo()) {
return;
}
for (int i = 0; i < steps; i++) {
if (!historyManager->canUndo()) {
continue;
}
historyManager->undo(deckList.get());
}
deckListModel->rebuildTree();
emit deckListModel->layoutChanged();
}
void DeckStateManager::redo(int steps)
{
if (!historyManager->canRedo()) {
return;
}
for (int i = 0; i < steps; i++) {
if (!historyManager->canRedo()) {
continue;
}
historyManager->redo(deckList.get());
}
deckListModel->rebuildTree();
emit deckListModel->layoutChanged();
}
void DeckStateManager::requestHistorySave(const QString &reason)
{
historyManager->save(deckList->createMemento(reason));
}
/**
* @brief Handles updating state and emitting signals whenever the cards are modified
*/
void DeckStateManager::doCardModified()
{
setModified(true);
emit cardModified();
emit deckModified();
}
/**
* @brief Handles updating state and emitting signals whenever the metadata is modified
*/
void DeckStateManager::doMetadataModified()
{
setModified(true);
emit metadataModified();
emit deckModified();
}

View file

@ -0,0 +1,297 @@
#ifndef COCKATRICE_DECK_STATE_MANAGER_H
#define COCKATRICE_DECK_STATE_MANAGER_H
#include "../../deck_loader/loaded_deck.h"
#include "deck_list_model.h"
#include <QSharedPointer>
#include <libcockatrice/deck_list/deck_list.h>
class DeckListHistoryManager;
/**
* @brief This class centralizes the management of the state of the deck in the deck editor tab.
* It is responsible for owning and managing the DeckListModel, underlying DeckList, load info, and edit history.
*
* Although this class provides getters for the underlying DeckListModel, you should generally refrain from directly
* modifying the returned model. Outside modifications to the deck state should be done through @link
* DeckStateManager::modifyDeck and the metadata setters.
* Those methods ensure that the history is recorded and correct signals are emitted.
*/
class DeckStateManager : public QObject
{
Q_OBJECT
LoadedDeck::LoadInfo lastLoadInfo;
QSharedPointer<DeckList> deckList;
DeckListModel *deckListModel;
DeckListHistoryManager *historyManager;
bool modified = false;
public:
explicit DeckStateManager(QObject *parent = nullptr);
/**
* Gets the underlying HistoryManager.
* @return The DeckListHistoryManager instance
*/
DeckListHistoryManager *getHistoryManager() const
{
return historyManager;
}
/**
* @brief Gets the underlying DeckListModel.
* You should generally refrain modifying the returned model directly.
* However, it's fine (and intended) to perform queries on the returned model.
* @return The DeckListModel instance
*/
DeckListModel *getModel() const
{
return deckListModel;
}
/**
* @brief Gets a view of the current deck.
*/
const DeckList &getDeckList() const;
/**
* @brief Creates a LoadedDeck containing the contents of the current deck and the current LoadInfo.
*
* @return A new LoadedDeck instance.
*/
LoadedDeck toLoadedDeck() const;
/**
* @brief Gets a view of the metadata in the DeckList
*/
DeckList::Metadata const &getMetadata() const;
/**
* @brief Gets the deck's simplified name.
*/
QString getSimpleDeckName() const;
/**
* @brief Gets the deck hash.
*/
QString getDeckHash() const;
/**
* @brief Checks if the deck has been modified since it was last saved
*/
bool isModified() const;
/**
* @brief Sets the new isModified state, emitting a signal if the state changed.
* This class will automatically update its isModified state, but you may need to set it manually to handle, for
* example, saving.
* @param state The state
*/
void setModified(bool state);
/**
* @brief Checks if the deck state is as if it was a new deck
*/
bool isBlankNewDeck() const;
/**
* @brief Overwrites the current deck with a new deck, resetting all history
* @param deck The new deck.
*/
void replaceDeck(const LoadedDeck &deck);
/**
* @brief Resets the deck to a blank new deck, resetting all history.
*/
void clearDeck();
/**
* @brief Sets the lastLoadInfo.
* @param loadInfo The lastLoadInfo
*/
void setLastLoadInfo(const LoadedDeck::LoadInfo &loadInfo)
{
lastLoadInfo = loadInfo;
}
/**
* @brief Modifies the cards in the deck, in a wrapped operation that is saved to the history.
*
* The operation is a function that accepts a DeckListModel that it operates upon, and returns a bool.
*
* This method will pass the underlying DeckListModel into the operation function. The function can call methods on
* the model to modify the deck.
* The function should return a bool to indicate success/failure.
*
* If the operation returns true, the state of the deck before the operation is ran is saved to the history, and the
* isModified state is updated.
* If the operation returns false, the history and isModified state is not updated.
*
* Note that even if the operation fails, any modifications to the model will already have been made.
* It's recommended for the operation to always return true if any modification has already been made to the model,
* as not doing that may cause the state to become desynced.
*
* @param reason The reason to display in the history
* @param operation The modification operation.
* @return The bool returned from the operation
*/
bool modifyDeck(const QString &reason, const std::function<bool(DeckListModel *)> &operation);
/**
* @brief Modifies the cards in the deck, in a wrapped operation that is saved to the history.
*
* The operation is a function that accepts a DeckListModel that it operates upon, and returns a QModelIndex.
* If the index is invalid, then the operation is considered to be a failure.
*
* See the other @link DeckStateManager::modifyDeck for more info about the behavior of this method.
*
* @param reason The reason to display in the history
* @param operation The modification operation.
* @return The QModelIndex returned from the operation
*/
QModelIndex modifyDeck(const QString &reason, const std::function<QModelIndex(DeckListModel *)> &operation);
/// @name Metadata setters
/// @brief These methods set the metadata. Will no-op if the new value is the same as the current value.
/// Saves the operation to history if successful.
///@{
void setName(const QString &name);
void setComments(const QString &comments);
void setBannerCard(const CardRef &bannerCard);
void setTags(const QStringList &tags);
void setFormat(const QString &format);
///@}
/**
* @brief Adds the given card to the given zone.
* Saves the operation to history if successful.
*
* @param card The card to add
* @param zoneName The zone to add the card to
* @return The index of the added card
*/
QModelIndex addCard(const ExactCard &card, const QString &zoneName);
/**
* @brief Removes 1 copy of the given card from the given zone.
* Saves the operation to history if successful.
*
* @param card The card to remove
* @param zoneName The zone to remove the card from
* @return The index of the removed card. Will be invalid if the last copy was removed.
*/
QModelIndex decrementCard(const ExactCard &card, const QString &zoneName);
/**
* @brief Swaps one copy of the card at the given index between the maindeck and sideboard.
* No-ops if index is invalid or not a card node.
* Saves the operation to history if successful.
*
* @param idx The model index
* @return Whether the operation was successfully performed
*/
bool swapCardAtIndex(const QModelIndex &idx);
/**
* @brief Removes all copies of the card at the given index.
* No-ops if index is invalid or not a card node.
* Saves the operation to history if successful.
*
* @param idx The model index
* @return Whether the operation was successfully performed
*/
bool removeCardAtIndex(const QModelIndex &idx);
/**
* @brief Increments the number of copies of the card at the given index by 1.
* No-ops if index is invalid or not a card node.
* Saves the operation to history if successful.
*
* @param idx The model index
* @return Whether the operation was successfully performed
*/
bool incrementCountAtIndex(const QModelIndex &idx);
/**
* @brief Decrements the number of copies of the card at the given index by 1.
* No-ops if index is invalid or not a card node.
* Saves the operation to history if successful.
*
* @param idx The model index
* @return Whether the operation was successfully performed
*/
bool decrementCountAtIndex(const QModelIndex &idx);
/**
* Undoes n steps of the history, setting the decklist state and updating the current step in the historyManager.
* @param steps Number of steps to undo.
*/
void undo(int steps = 1);
/**
* Redoes n steps of the history, setting the decklist state and updating the current step in the historyManager.
* @param steps Number of steps to redo.
*/
void redo(int steps = 1);
public slots:
/**
* Saves the current decklist state to history.
* @param reason The reason that is shown in the history.
*/
void requestHistorySave(const QString &reason);
private:
bool offsetCountAtIndex(const QModelIndex &idx, int offset);
void doCardModified();
void doMetadataModified();
signals:
/**
* A modification has been made to the cards in the deck
*/
void cardModified();
/**
* A card that wasn't previously in the deck was added to the deck, or the last copy of a card was removed from the
* deck.
*/
void uniqueCardsChanged();
/**
* A modification has been made to the metadata in the deck
*/
void metadataModified();
/**
* A modification has been made to the cards or metadata in the deck
*/
void deckModified();
/**
* The history has been greatly changed and needs to be reloaded.
*/
void historyChanged();
/**
* The deck has been completely changed.
*/
void deckReplaced();
/**
* The isModified state of the deck has changed
* @param isModified the new state
*/
void isModifiedChanged(bool isModified);
/**
* The selected card on any views connected to this deck should be changed to this index.
* @param index The model index
*/
void focusIndexChanged(QModelIndex index);
};
#endif // COCKATRICE_DECK_STATE_MANAGER_H

View file

@ -2,6 +2,7 @@
#include "../../deck_loader/card_node_function.h" #include "../../deck_loader/card_node_function.h"
#include "../../deck_loader/deck_loader.h" #include "../../deck_loader/deck_loader.h"
#include "../deck_editor/deck_state_manager.h"
#include "../interface/widgets/cards/card_info_picture_widget.h" #include "../interface/widgets/cards/card_info_picture_widget.h"
#include "../interface/widgets/general/layout_containers/flow_widget.h" #include "../interface/widgets/general/layout_containers/flow_widget.h"
@ -21,7 +22,8 @@
#include <qdrag.h> #include <qdrag.h>
#include <qevent.h> #include <qevent.h>
DlgSelectSetForCards::DlgSelectSetForCards(QWidget *parent, DeckListModel *_model) : QDialog(parent), model(_model) DlgSelectSetForCards::DlgSelectSetForCards(QWidget *parent, DeckStateManager *deckStateManger)
: QDialog(parent), deckStateManager(deckStateManger)
{ {
setMinimumSize(500, 500); setMinimumSize(500, 500);
setAcceptDrops(true); setAcceptDrops(true);
@ -165,36 +167,39 @@ void DlgSelectSetForCards::actOK()
if (modifiedSetsAndCardsMap.isEmpty()) { if (modifiedSetsAndCardsMap.isEmpty()) {
accept(); // Nothing to do accept(); // Nothing to do
} else { return;
emit deckAboutToBeModified(tr("Bulk modified printings."));
} }
for (QString modifiedSet : modifiedSetsAndCardsMap.keys()) { auto bulkModify = [&modifiedSetsAndCardsMap](DeckListModel *model) {
for (QString card : modifiedSetsAndCardsMap.value(modifiedSet)) { for (QString modifiedSet : modifiedSetsAndCardsMap.keys()) {
swapPrinting(model, modifiedSet, card); for (QString card : modifiedSetsAndCardsMap.value(modifiedSet)) {
swapPrinting(model, modifiedSet, card);
}
} }
} return true;
};
deckStateManager->modifyDeck(tr("Bulk modified printings."), bulkModify);
if (!modifiedSetsAndCardsMap.isEmpty()) {
emit deckModified();
}
accept(); accept();
} }
void DlgSelectSetForCards::actClear() void DlgSelectSetForCards::actClear()
{ {
emit deckAboutToBeModified(tr("Cleared all printing information.")); deckStateManager->modifyDeck(tr("Cleared all printing information."), [](auto model) {
model->forEachCard(CardNodeFunction::ClearPrintingData()); model->forEachCard(CardNodeFunction::ClearPrintingData());
emit deckModified(); return true;
});
accept(); accept();
} }
void DlgSelectSetForCards::actSetAllToPreferred() void DlgSelectSetForCards::actSetAllToPreferred()
{ {
emit deckAboutToBeModified(tr("Set all printings to preferred.")); deckStateManager->modifyDeck(tr("Set all printings to preferred."), [](auto model) {
model->forEachCard(CardNodeFunction::ClearPrintingData()); model->forEachCard(CardNodeFunction::ClearPrintingData());
model->forEachCard(CardNodeFunction::SetProviderIdToPreferred()); model->forEachCard(CardNodeFunction::SetProviderIdToPreferred());
emit deckModified(); return true;
});
accept(); accept();
} }
@ -227,10 +232,8 @@ void DlgSelectSetForCards::sortSetsByCount()
QMap<QString, int> DlgSelectSetForCards::getSetsForCards() QMap<QString, int> DlgSelectSetForCards::getSetsForCards()
{ {
QMap<QString, int> setCounts; QMap<QString, int> setCounts;
if (!model)
return setCounts;
QList<QString> cardNames = model->getCardNames(); QList<QString> cardNames = deckStateManager->getModel()->getCardNames();
for (auto cardName : cardNames) { for (auto cardName : cardNames) {
CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(cardName); CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(cardName);
@ -269,7 +272,7 @@ void DlgSelectSetForCards::updateCardLists()
} }
} }
QList<QString> cardNames = model->getCardNames(); QList<QString> cardNames = deckStateManager->getModel()->getCardNames();
for (auto cardName : cardNames) { for (auto cardName : cardNames) {
bool found = false; bool found = false;
@ -351,10 +354,8 @@ void DlgSelectSetForCards::dropEvent(QDropEvent *event)
QMap<QString, QStringList> DlgSelectSetForCards::getCardsForSets() QMap<QString, QStringList> DlgSelectSetForCards::getCardsForSets()
{ {
QMap<QString, QStringList> setCards; QMap<QString, QStringList> setCards;
if (!model)
return setCards;
QList<QString> cardNames = model->getCardNames(); QList<QString> cardNames = deckStateManager->getModel()->getCardNames();
for (auto cardName : cardNames) { for (auto cardName : cardNames) {
CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(cardName); CardInfoPtr infoPtr = CardDatabaseManager::query()->getCardInfo(cardName);

View file

@ -18,6 +18,7 @@
#include <QVBoxLayout> #include <QVBoxLayout>
#include <libcockatrice/models/deck_list/deck_list_model.h> #include <libcockatrice/models/deck_list/deck_list_model.h>
class DeckStateManager;
class SetEntryWidget; // Forward declaration class SetEntryWidget; // Forward declaration
class DlgSelectSetForCards : public QDialog class DlgSelectSetForCards : public QDialog
@ -25,7 +26,7 @@ class DlgSelectSetForCards : public QDialog
Q_OBJECT Q_OBJECT
public: public:
explicit DlgSelectSetForCards(QWidget *parent, DeckListModel *_model); explicit DlgSelectSetForCards(QWidget *parent, DeckStateManager *deckStateManager);
void retranslateUi(); void retranslateUi();
void sortSetsByCount(); void sortSetsByCount();
QMap<QString, QStringList> getCardsForSets(); QMap<QString, QStringList> getCardsForSets();
@ -37,7 +38,6 @@ public:
signals: signals:
void widgetOrderChanged(); void widgetOrderChanged();
void orderChanged(); void orderChanged();
void deckAboutToBeModified(const QString &reason);
void deckModified(); void deckModified();
public slots: public slots:
@ -61,7 +61,7 @@ private:
QLabel *modifiedCardsLabel; QLabel *modifiedCardsLabel;
QWidget *listContainer; QWidget *listContainer;
QListWidget *listWidget; QListWidget *listWidget;
DeckListModel *model; DeckStateManager *deckStateManager;
QMap<QString, SetEntryWidget *> setEntries; QMap<QString, SetEntryWidget *> setEntries;
QPushButton *clearButton; QPushButton *clearButton;
QPushButton *setAllToPreferredButton; QPushButton *setAllToPreferredButton;

View file

@ -11,16 +11,12 @@
* UI elements for managing card counts in both the mainboard and sideboard zones. * UI elements for managing card counts in both the mainboard and sideboard zones.
* *
* @param parent The parent widget. * @param parent The parent widget.
* @param deckEditor Pointer to the TabDeckEditor. * @param deckStateManager Pointer to the DeckStateManager
* @param deckModel Pointer to the DeckListModel.
* @param deckView Pointer to the QTreeView for the deck display.
* @param cardSizeSlider Pointer to the QSlider used for dynamic font resizing. * @param cardSizeSlider Pointer to the QSlider used for dynamic font resizing.
* @param rootCard The root card for the widget. * @param rootCard The root card for the widget.
*/ */
AllZonesCardAmountWidget::AllZonesCardAmountWidget(QWidget *parent, AllZonesCardAmountWidget::AllZonesCardAmountWidget(QWidget *parent,
AbstractTabDeckEditor *deckEditor, DeckStateManager *deckStateManager,
DeckListModel *deckModel,
QTreeView *deckView,
QSlider *cardSizeSlider, QSlider *cardSizeSlider,
const ExactCard &rootCard) const ExactCard &rootCard)
: QWidget(parent), cardSizeSlider(cardSizeSlider) : QWidget(parent), cardSizeSlider(cardSizeSlider)
@ -32,11 +28,9 @@ AllZonesCardAmountWidget::AllZonesCardAmountWidget(QWidget *parent,
setContentsMargins(5, 5, 5, 5); // Padding around the text setContentsMargins(5, 5, 5, 5); // Padding around the text
zoneLabelMainboard = new ShadowBackgroundLabel(this, tr("Mainboard")); zoneLabelMainboard = new ShadowBackgroundLabel(this, tr("Mainboard"));
buttonBoxMainboard = buttonBoxMainboard = new CardAmountWidget(this, deckStateManager, cardSizeSlider, rootCard, DECK_ZONE_MAIN);
new CardAmountWidget(this, deckEditor, deckModel, deckView, cardSizeSlider, rootCard, DECK_ZONE_MAIN);
zoneLabelSideboard = new ShadowBackgroundLabel(this, tr("Sideboard")); zoneLabelSideboard = new ShadowBackgroundLabel(this, tr("Sideboard"));
buttonBoxSideboard = buttonBoxSideboard = new CardAmountWidget(this, deckStateManager, cardSizeSlider, rootCard, DECK_ZONE_SIDE);
new CardAmountWidget(this, deckEditor, deckModel, deckView, cardSizeSlider, rootCard, DECK_ZONE_SIDE);
layout->addWidget(zoneLabelMainboard, 0, Qt::AlignHCenter | Qt::AlignBottom); layout->addWidget(zoneLabelMainboard, 0, Qt::AlignHCenter | Qt::AlignBottom);
layout->addWidget(buttonBoxMainboard, 0, Qt::AlignHCenter | Qt::AlignTop); layout->addWidget(buttonBoxMainboard, 0, Qt::AlignHCenter | Qt::AlignTop);

View file

@ -18,9 +18,7 @@ class AllZonesCardAmountWidget : public QWidget
Q_OBJECT Q_OBJECT
public: public:
explicit AllZonesCardAmountWidget(QWidget *parent, explicit AllZonesCardAmountWidget(QWidget *parent,
AbstractTabDeckEditor *deckEditor, DeckStateManager *deckStateManager,
DeckListModel *deckModel,
QTreeView *deckView,
QSlider *cardSizeSlider, QSlider *cardSizeSlider,
const ExactCard &rootCard); const ExactCard &rootCard);
int getMainboardAmount(); int getMainboardAmount();

View file

@ -1,5 +1,7 @@
#include "card_amount_widget.h" #include "card_amount_widget.h"
#include "../deck_editor/deck_state_manager.h"
#include <QPainter> #include <QPainter>
#include <QTimer> #include <QTimer>
@ -7,22 +9,17 @@
* @brief Constructs a widget for displaying and controlling the card count in a specific zone. * @brief Constructs a widget for displaying and controlling the card count in a specific zone.
* *
* @param parent The parent widget. * @param parent The parent widget.
* @param deckEditor Pointer to the TabDeckEditor instance.
* @param deckModel Pointer to the DeckListModel instance.
* @param deckView Pointer to the QTreeView displaying the deck.
* @param cardSizeSlider Pointer to the QSlider for adjusting font size. * @param cardSizeSlider Pointer to the QSlider for adjusting font size.
* @param rootCard The root card to manage within the widget. * @param rootCard The root card to manage within the widget.
* @param zoneName The zone name (e.g., DECK_ZONE_MAIN or DECK_ZONE_SIDE). * @param zoneName The zone name (e.g., DECK_ZONE_MAIN or DECK_ZONE_SIDE).
*/ */
CardAmountWidget::CardAmountWidget(QWidget *parent, CardAmountWidget::CardAmountWidget(QWidget *parent,
AbstractTabDeckEditor *deckEditor, DeckStateManager *deckStateManager,
DeckListModel *deckModel,
QTreeView *deckView,
QSlider *cardSizeSlider, QSlider *cardSizeSlider,
const ExactCard &rootCard, const ExactCard &rootCard,
const QString &zoneName) const QString &zoneName)
: QWidget(parent), deckEditor(deckEditor), deckModel(deckModel), deckView(deckView), cardSizeSlider(cardSizeSlider), : QWidget(parent), deckStateManager(deckStateManager), cardSizeSlider(cardSizeSlider), rootCard(rootCard),
rootCard(rootCard), zoneName(zoneName), hovered(false) zoneName(zoneName), hovered(false)
{ {
layout = new QHBoxLayout(this); layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
@ -56,15 +53,10 @@ CardAmountWidget::CardAmountWidget(QWidget *parent,
layout->addWidget(incrementButton); layout->addWidget(incrementButton);
// React to model changes // React to model changes
connect(deckModel, &DeckListModel::dataChanged, this, &CardAmountWidget::updateCardCount); connect(deckStateManager, &DeckStateManager::cardModified, this, &CardAmountWidget::updateCardCount);
connect(deckModel, &QAbstractItemModel::rowsRemoved, this, &CardAmountWidget::updateCardCount);
// Connect slider for dynamic font size adjustment // Connect slider for dynamic font size adjustment
connect(cardSizeSlider, &QSlider::valueChanged, this, &CardAmountWidget::adjustFontSize); connect(cardSizeSlider, &QSlider::valueChanged, this, &CardAmountWidget::adjustFontSize);
if (deckEditor) {
connect(this, &CardAmountWidget::deckModified, deckEditor, &AbstractTabDeckEditor::onDeckHistorySaveRequested);
}
} }
/** /**
@ -168,7 +160,7 @@ static QModelIndex addAndReplacePrintings(DeckListModel *model,
void CardAmountWidget::addPrinting(const QString &zone) void CardAmountWidget::addPrinting(const QString &zone)
{ {
// Check if we will need to add extra copies due to replacing copies without providerIds // Check if we will need to add extra copies due to replacing copies without providerIds
QModelIndex existing = deckModel->findCard(rootCard.getName(), zone); QModelIndex existing = deckStateManager->getModel()->findCard(rootCard.getName(), zone);
int extraCopies = 0; int extraCopies = 0;
bool replacingProviderless = false; bool replacingProviderless = false;
@ -192,15 +184,13 @@ void CardAmountWidget::addPrinting(const QString &zone)
.arg(rootCard.getPrinting().getUuid()) .arg(rootCard.getPrinting().getUuid())
.arg(replacingProviderless ? " (replaced providerless printings)" : ""); .arg(replacingProviderless ? " (replaced providerless printings)" : "");
emit deckModified(reason);
// Add the card and expand the list UI // Add the card and expand the list UI
auto newCardIndex = addAndReplacePrintings(deckModel, existing, rootCard, zone, extraCopies, replacingProviderless); QModelIndex newCardIndex = deckStateManager->modifyDeck(reason, [&](auto model) {
return addAndReplacePrintings(model, existing, rootCard, zone, extraCopies, replacingProviderless);
});
if (newCardIndex.isValid()) { if (newCardIndex.isValid()) {
deckView->setCurrentIndex(newCardIndex); emit deckStateManager->focusIndexChanged(newCardIndex);
deckView->setFocus(Qt::FocusReason::MouseFocusReason);
deckEditor->setModified(true);
} }
} }
@ -250,13 +240,11 @@ void CardAmountWidget::decrementCardHelper(const QString &zone)
.arg(zone == DECK_ZONE_MAIN ? "mainboard" : "sideboard") .arg(zone == DECK_ZONE_MAIN ? "mainboard" : "sideboard")
.arg(rootCard.getPrinting().getUuid()); .arg(rootCard.getPrinting().getUuid());
emit deckModified(reason); deckStateManager->modifyDeck(reason, [this, &zone](auto model) {
QModelIndex idx = model->findCard(rootCard.getName(), zone, rootCard.getPrinting().getUuid(),
QModelIndex idx = deckModel->findCard(rootCard.getName(), zone, rootCard.getPrinting().getUuid(),
rootCard.getPrinting().getProperty("num")); rootCard.getPrinting().getProperty("num"));
return model->offsetCountAtIndex(idx, -1);
deckModel->offsetCountAtIndex(idx, -1); });
deckEditor->setModified(true);
} }
/** /**
@ -273,7 +261,7 @@ int CardAmountWidget::countCardsInZone(const QString &deckZone)
return 0; // Cards without uuids/providerIds CANNOT match another card, they are undefined for us. return 0; // Cards without uuids/providerIds CANNOT match another card, they are undefined for us.
} }
QList<ExactCard> cards = deckModel->getCardsForZone(deckZone); QList<ExactCard> cards = deckStateManager->getModel()->getCardsForZone(deckZone);
return std::count_if(cards.cbegin(), cards.cend(), return std::count_if(cards.cbegin(), cards.cend(),
[&uuid](const ExactCard &card) { return card.getPrinting().getUuid() == uuid; }); [&uuid](const ExactCard &card) { return card.getPrinting().getUuid() == uuid; });

View file

@ -27,9 +27,7 @@ signals:
public: public:
explicit CardAmountWidget(QWidget *parent, explicit CardAmountWidget(QWidget *parent,
AbstractTabDeckEditor *deckEditor, DeckStateManager *deckStateManager,
DeckListModel *deckModel,
QTreeView *deckView,
QSlider *cardSizeSlider, QSlider *cardSizeSlider,
const ExactCard &rootCard, const ExactCard &rootCard,
const QString &zoneName); const QString &zoneName);
@ -44,9 +42,7 @@ protected:
void showEvent(QShowEvent *event) override; void showEvent(QShowEvent *event) override;
private: private:
AbstractTabDeckEditor *deckEditor; DeckStateManager *deckStateManager;
DeckListModel *deckModel;
QTreeView *deckView;
QSlider *cardSizeSlider; QSlider *cardSizeSlider;
ExactCard rootCard; ExactCard rootCard;
QString zoneName; QString zoneName;

View file

@ -3,6 +3,7 @@
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../../../interface/card_picture_loader/card_picture_loader.h" #include "../../../interface/card_picture_loader/card_picture_loader.h"
#include "../../../interface/widgets/dialogs/dlg_select_set_for_cards.h" #include "../../../interface/widgets/dialogs/dlg_select_set_for_cards.h"
#include "../deck_editor/deck_state_manager.h"
#include "printing_selector_card_display_widget.h" #include "printing_selector_card_display_widget.h"
#include "printing_selector_card_search_widget.h" #include "printing_selector_card_search_widget.h"
#include "printing_selector_card_selection_widget.h" #include "printing_selector_card_selection_widget.h"
@ -21,12 +22,9 @@
* *
* @param parent The parent widget for the PrintingSelector. * @param parent The parent widget for the PrintingSelector.
* @param deckEditor The TabDeckEditor instance used for managing the deck. * @param deckEditor The TabDeckEditor instance used for managing the deck.
* @param deckModel The DeckListModel instance that provides data for the deck's contents.
* @param deckView The QTreeView instance used to display the deck and its contents.
*/ */
PrintingSelector::PrintingSelector(QWidget *parent, AbstractTabDeckEditor *_deckEditor) PrintingSelector::PrintingSelector(QWidget *parent, AbstractTabDeckEditor *_deckEditor)
: QWidget(parent), deckEditor(_deckEditor), deckModel(deckEditor->deckDockWidget->deckModel), : QWidget(parent), deckEditor(_deckEditor), deckStateManager(_deckEditor->deckStateManager)
deckView(deckEditor->deckDockWidget->deckView)
{ {
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
layout = new QVBoxLayout(this); layout = new QVBoxLayout(this);
@ -74,13 +72,12 @@ PrintingSelector::PrintingSelector(QWidget *parent, AbstractTabDeckEditor *_deck
layout->addWidget(flowWidget); layout->addWidget(flowWidget);
cardSelectionBar = new PrintingSelectorCardSelectionWidget(this); cardSelectionBar = new PrintingSelectorCardSelectionWidget(this, deckStateManager);
cardSelectionBar->setVisible(SettingsCache::instance().getPrintingSelectorNavigationButtonsVisible()); cardSelectionBar->setVisible(SettingsCache::instance().getPrintingSelectorNavigationButtonsVisible());
layout->addWidget(cardSelectionBar); layout->addWidget(cardSelectionBar);
// Connect deck model data change signal to update display // Connect deck model data change signal to update display
connect(deckModel, &DeckListModel::rowsInserted, this, &PrintingSelector::printingsInDeckChanged); connect(deckStateManager, &DeckStateManager::uniqueCardsChanged, this, &PrintingSelector::printingsInDeckChanged);
connect(deckModel, &DeckListModel::rowsRemoved, this, &PrintingSelector::printingsInDeckChanged);
retranslateUi(); retranslateUi();
} }
@ -152,7 +149,8 @@ void PrintingSelector::getAllSetsForCurrentCard()
QList<PrintingInfo> printingsToUse; QList<PrintingInfo> printingsToUse;
if (SettingsCache::instance().getBumpSetsWithCardsInDeckToTop()) { if (SettingsCache::instance().getBumpSetsWithCardsInDeckToTop()) {
printingsToUse = sortToolBar->prependPrintingsInDeck(filteredPrintings, selectedCard, deckModel); printingsToUse =
sortToolBar->prependPrintingsInDeck(filteredPrintings, selectedCard, deckStateManager->getModel());
} else { } else {
printingsToUse = filteredPrintings; printingsToUse = filteredPrintings;
} }
@ -164,7 +162,7 @@ void PrintingSelector::getAllSetsForCurrentCard()
connect(widgetLoadingBufferTimer, &QTimer::timeout, this, [=, this]() mutable { connect(widgetLoadingBufferTimer, &QTimer::timeout, this, [=, this]() mutable {
for (int i = 0; i < BATCH_SIZE && currentIndex < printingsToUse.size(); ++i, ++currentIndex) { for (int i = 0; i < BATCH_SIZE && currentIndex < printingsToUse.size(); ++i, ++currentIndex) {
auto card = ExactCard(selectedCard, printingsToUse[currentIndex]); auto card = ExactCard(selectedCard, printingsToUse[currentIndex]);
auto *cardDisplayWidget = new PrintingSelectorCardDisplayWidget(this, deckEditor, deckModel, deckView, auto *cardDisplayWidget = new PrintingSelectorCardDisplayWidget(this, deckEditor, deckStateManager,
cardSizeWidget->getSlider(), card); cardSizeWidget->getSlider(), card);
flowWidget->addWidget(cardDisplayWidget); flowWidget->addWidget(cardDisplayWidget);
cardDisplayWidget->clampSetNameToPicture(); cardDisplayWidget->clampSetNameToPicture();

View file

@ -21,6 +21,7 @@
#define BATCH_SIZE 10 #define BATCH_SIZE 10
class DeckStateManager;
class PrintingSelectorCardSearchWidget; class PrintingSelectorCardSearchWidget;
class PrintingSelectorCardSelectionWidget; class PrintingSelectorCardSelectionWidget;
class PrintingSelectorCardSortingWidget; class PrintingSelectorCardSortingWidget;
@ -35,15 +36,6 @@ public:
void setCard(const CardInfoPtr &newCard); void setCard(const CardInfoPtr &newCard);
void getAllSetsForCurrentCard(); void getAllSetsForCurrentCard();
[[nodiscard]] DeckListModel *getDeckModel() const
{
return deckModel;
}
[[nodiscard]] AbstractTabDeckEditor *getDeckEditor() const
{
return deckEditor;
}
public slots: public slots:
void retranslateUi(); void retranslateUi();
@ -75,8 +67,7 @@ private:
CardSizeWidget *cardSizeWidget; CardSizeWidget *cardSizeWidget;
PrintingSelectorCardSelectionWidget *cardSelectionBar; PrintingSelectorCardSelectionWidget *cardSelectionBar;
AbstractTabDeckEditor *deckEditor; AbstractTabDeckEditor *deckEditor;
DeckListModel *deckModel; DeckStateManager *deckStateManager;
QTreeView *deckView;
CardInfoPtr selectedCard; CardInfoPtr selectedCard;
QTimer *widgetLoadingBufferTimer; QTimer *widgetLoadingBufferTimer;
int currentIndex = 0; int currentIndex = 0;

View file

@ -18,15 +18,13 @@
* *
* @param parent The parent widget for this display. * @param parent The parent widget for this display.
* @param deckEditor The TabDeckEditor instance for deck management. * @param deckEditor The TabDeckEditor instance for deck management.
* @param deckModel The DeckListModel instance providing deck data. * @param deckStateManager The DeckStateManager instance providing deck data.
* @param deckView The QTreeView instance displaying the deck.
* @param cardSizeSlider The slider controlling the size of the displayed card. * @param cardSizeSlider The slider controlling the size of the displayed card.
* @param rootCard The root card object, representing the card to be displayed. * @param rootCard The root card object, representing the card to be displayed.
*/ */
PrintingSelectorCardDisplayWidget::PrintingSelectorCardDisplayWidget(QWidget *parent, PrintingSelectorCardDisplayWidget::PrintingSelectorCardDisplayWidget(QWidget *parent,
AbstractTabDeckEditor *deckEditor, AbstractTabDeckEditor *deckEditor,
DeckListModel *deckModel, DeckStateManager *deckStateManager,
QTreeView *deckView,
QSlider *cardSizeSlider, QSlider *cardSizeSlider,
const ExactCard &rootCard) const ExactCard &rootCard)
: QWidget(parent) : QWidget(parent)
@ -36,8 +34,7 @@ PrintingSelectorCardDisplayWidget::PrintingSelectorCardDisplayWidget(QWidget *pa
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
// Create the overlay widget for the card display // Create the overlay widget for the card display
overlayWidget = overlayWidget = new PrintingSelectorCardOverlayWidget(this, deckEditor, deckStateManager, cardSizeSlider, rootCard);
new PrintingSelectorCardOverlayWidget(this, deckEditor, deckModel, deckView, cardSizeSlider, rootCard);
connect(overlayWidget, &PrintingSelectorCardOverlayWidget::cardPreferenceChanged, this, connect(overlayWidget, &PrintingSelectorCardOverlayWidget::cardPreferenceChanged, this,
[this]() { emit cardPreferenceChanged(); }); [this]() { emit cardPreferenceChanged(); });

View file

@ -21,8 +21,7 @@ class PrintingSelectorCardDisplayWidget : public QWidget
public: public:
PrintingSelectorCardDisplayWidget(QWidget *parent, PrintingSelectorCardDisplayWidget(QWidget *parent,
AbstractTabDeckEditor *deckEditor, AbstractTabDeckEditor *deckEditor,
DeckListModel *deckModel, DeckStateManager *deckStateManager,
QTreeView *deckView,
QSlider *cardSizeSlider, QSlider *cardSizeSlider,
const ExactCard &rootCard); const ExactCard &rootCard);

View file

@ -22,15 +22,13 @@
* *
* @param parent The parent widget for this overlay. * @param parent The parent widget for this overlay.
* @param _deckEditor The TabDeckEditor instance for deck management. * @param _deckEditor The TabDeckEditor instance for deck management.
* @param deckModel The DeckListModel instance providing deck data. * @param deckStateManager The DeckStateManager instance providing deck data.
* @param deckView The QTreeView instance displaying the deck.
* @param cardSizeSlider The slider controlling the size of the card. * @param cardSizeSlider The slider controlling the size of the card.
* @param _rootCard The root card object that contains information about the card. * @param _rootCard The root card object that contains information about the card.
*/ */
PrintingSelectorCardOverlayWidget::PrintingSelectorCardOverlayWidget(QWidget *parent, PrintingSelectorCardOverlayWidget::PrintingSelectorCardOverlayWidget(QWidget *parent,
AbstractTabDeckEditor *_deckEditor, AbstractTabDeckEditor *_deckEditor,
DeckListModel *deckModel, DeckStateManager *deckStateManager,
QTreeView *deckView,
QSlider *cardSizeSlider, QSlider *cardSizeSlider,
const ExactCard &_rootCard) const ExactCard &_rootCard)
: QWidget(parent), deckEditor(_deckEditor), rootCard(_rootCard) : QWidget(parent), deckEditor(_deckEditor), rootCard(_rootCard)
@ -58,8 +56,7 @@ PrintingSelectorCardOverlayWidget::PrintingSelectorCardOverlayWidget(QWidget *pa
updatePinBadgeVisibility(); updatePinBadgeVisibility();
// Add AllZonesCardAmountWidget // Add AllZonesCardAmountWidget
allZonesCardAmountWidget = allZonesCardAmountWidget = new AllZonesCardAmountWidget(this, deckStateManager, cardSizeSlider, _rootCard);
new AllZonesCardAmountWidget(this, deckEditor, deckModel, deckView, cardSizeSlider, _rootCard);
allZonesCardAmountWidget->raise(); // Ensure it's on top of the picture allZonesCardAmountWidget->raise(); // Ensure it's on top of the picture
// Set initial visibility based on amounts // Set initial visibility based on amounts

View file

@ -20,8 +20,7 @@ class PrintingSelectorCardOverlayWidget : public QWidget
public: public:
explicit PrintingSelectorCardOverlayWidget(QWidget *parent, explicit PrintingSelectorCardOverlayWidget(QWidget *parent,
AbstractTabDeckEditor *_deckEditor, AbstractTabDeckEditor *_deckEditor,
DeckListModel *_deckModel, DeckStateManager *_deckStateManager,
QTreeView *_deckView,
QSlider *_cardSizeSlider, QSlider *_cardSizeSlider,
const ExactCard &_rootCard); const ExactCard &_rootCard);

View file

@ -11,7 +11,9 @@
* *
* @param parent The parent PrintingSelector widget responsible for managing card selection. * @param parent The parent PrintingSelector widget responsible for managing card selection.
*/ */
PrintingSelectorCardSelectionWidget::PrintingSelectorCardSelectionWidget(PrintingSelector *parent) : parent(parent) PrintingSelectorCardSelectionWidget::PrintingSelectorCardSelectionWidget(PrintingSelector *parent,
DeckStateManager *deckStateManager)
: parent(parent), deckStateManager(deckStateManager)
{ {
cardSelectionBarLayout = new QHBoxLayout(this); cardSelectionBarLayout = new QHBoxLayout(this);
cardSelectionBarLayout->setContentsMargins(9, 0, 9, 0); cardSelectionBarLayout->setContentsMargins(9, 0, 9, 0);
@ -48,12 +50,6 @@ void PrintingSelectorCardSelectionWidget::connectSignals()
void PrintingSelectorCardSelectionWidget::selectSetForCards() void PrintingSelectorCardSelectionWidget::selectSetForCards()
{ {
auto *setSelectionDialog = new DlgSelectSetForCards(nullptr, parent->getDeckModel()); auto *setSelectionDialog = new DlgSelectSetForCards(nullptr, deckStateManager);
connect(setSelectionDialog, &DlgSelectSetForCards::deckAboutToBeModified, parent->getDeckEditor(), setSelectionDialog->exec();
&AbstractTabDeckEditor::onDeckHistorySaveRequested);
connect(setSelectionDialog, &DlgSelectSetForCards::deckModified, parent->getDeckEditor(),
&AbstractTabDeckEditor::onDeckModified);
if (!setSelectionDialog->exec()) {
return;
}
} }

View file

@ -18,7 +18,7 @@ class PrintingSelectorCardSelectionWidget : public QWidget
Q_OBJECT Q_OBJECT
public: public:
explicit PrintingSelectorCardSelectionWidget(PrintingSelector *parent); explicit PrintingSelectorCardSelectionWidget(PrintingSelector *parent, DeckStateManager *deckStateManager);
void connectSignals(); void connectSignals();
@ -27,6 +27,7 @@ public slots:
private: private:
PrintingSelector *parent; PrintingSelector *parent;
DeckStateManager *deckStateManager;
QHBoxLayout *cardSelectionBarLayout; QHBoxLayout *cardSelectionBarLayout;
QPushButton *previousCardButton; QPushButton *previousCardButton;
QPushButton *selectSetForCardsButton; QPushButton *selectSetForCardsButton;

View file

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

View file

@ -23,7 +23,7 @@ public:
QList<PrintingInfo> prependPinnedPrintings(const QList<PrintingInfo> &printings, const QString &cardName); QList<PrintingInfo> prependPinnedPrintings(const QList<PrintingInfo> &printings, const QString &cardName);
QList<PrintingInfo> prependPrintingsInDeck(const QList<PrintingInfo> &printings, QList<PrintingInfo> prependPrintingsInDeck(const QList<PrintingInfo> &printings,
const CardInfoPtr &selectedCard, const CardInfoPtr &selectedCard,
DeckListModel *deckModel); const DeckListModel *deckModel);
public slots: public slots:
void updateSortOrder(); void updateSortOrder();

View file

@ -12,6 +12,7 @@
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../client/network/interfaces/deck_stats_interface.h" #include "../client/network/interfaces/deck_stats_interface.h"
#include "../client/network/interfaces/tapped_out_interface.h" #include "../client/network/interfaces/tapped_out_interface.h"
#include "../deck_editor/deck_state_manager.h"
#include "../interface/card_picture_loader/card_picture_loader.h" #include "../interface/card_picture_loader/card_picture_loader.h"
#include "../interface/pixel_map_generator.h" #include "../interface/pixel_map_generator.h"
#include "../interface/widgets/dialogs/dlg_load_deck.h" #include "../interface/widgets/dialogs/dlg_load_deck.h"
@ -52,7 +53,7 @@ AbstractTabDeckEditor::AbstractTabDeckEditor(TabSupervisor *_tabSupervisor) : Ta
{ {
setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks); setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks);
historyManager = new DeckListHistoryManager(this); deckStateManager = new DeckStateManager(this);
databaseDisplayDockWidget = new DeckEditorDatabaseDisplayWidget(this); databaseDisplayDockWidget = new DeckEditorDatabaseDisplayWidget(this);
deckDockWidget = new DeckEditorDeckDockWidget(this); deckDockWidget = new DeckEditorDeckDockWidget(this);
@ -64,14 +65,8 @@ AbstractTabDeckEditor::AbstractTabDeckEditor(TabSupervisor *_tabSupervisor) : Ta
}); });
// Connect deck signals to this tab // Connect deck signals to this tab
connect(deckDockWidget, &DeckEditorDeckDockWidget::deckChanged, this, &AbstractTabDeckEditor::onDeckChanged); connect(deckStateManager, &DeckStateManager::isModifiedChanged, this, &AbstractTabDeckEditor::onDeckModified);
connect(deckDockWidget, &DeckEditorDeckDockWidget::deckModified, this, &AbstractTabDeckEditor::onDeckModified); connect(deckDockWidget, &DeckEditorDeckDockWidget::selectedCardChanged, this, &AbstractTabDeckEditor::updateCard);
connect(deckDockWidget, &DeckEditorDeckDockWidget::requestDeckHistorySave, this,
&AbstractTabDeckEditor::onDeckHistorySaveRequested);
connect(deckDockWidget, &DeckEditorDeckDockWidget::requestDeckHistoryClear, this,
&AbstractTabDeckEditor::onDeckHistoryClearRequested);
connect(deckDockWidget, &DeckEditorDeckDockWidget::cardChanged, this, &AbstractTabDeckEditor::updateCard);
connect(this, &AbstractTabDeckEditor::decrementCard, deckDockWidget, &DeckEditorDeckDockWidget::actDecrementCard);
// Connect database display signals to this tab // Connect database display signals to this tab
connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::cardChanged, this, connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::cardChanged, this,
@ -107,7 +102,6 @@ void AbstractTabDeckEditor::updateCard(const ExactCard &card)
/** @brief Placeholder: called when the deck changes. */ /** @brief Placeholder: called when the deck changes. */
void AbstractTabDeckEditor::onDeckChanged() void AbstractTabDeckEditor::onDeckChanged()
{ {
historyManager->clear();
} }
/** /**
@ -115,24 +109,8 @@ void AbstractTabDeckEditor::onDeckChanged()
*/ */
void AbstractTabDeckEditor::onDeckModified() void AbstractTabDeckEditor::onDeckModified()
{ {
setModified(!isBlankNewDeck()); deckMenu->setSaveStatus(!deckStateManager->isBlankNewDeck());
deckMenu->setSaveStatus(!isBlankNewDeck()); emit tabTextChanged(this, getTabText());
}
/**
* @brief Marks the tab as modified and updates the save menu status.
*/
void AbstractTabDeckEditor::onDeckHistorySaveRequested(const QString &modificationReason)
{
historyManager->save(deckDockWidget->getDeckList().createMemento(modificationReason));
}
/**
* @brief Marks the tab as modified and updates the save menu status.
*/
void AbstractTabDeckEditor::onDeckHistoryClearRequested()
{
historyManager->clear();
} }
/** /**
@ -142,7 +120,7 @@ void AbstractTabDeckEditor::onDeckHistoryClearRequested()
*/ */
void AbstractTabDeckEditor::addCardHelper(const ExactCard &card, const QString &zoneName) void AbstractTabDeckEditor::addCardHelper(const ExactCard &card, const QString &zoneName)
{ {
deckDockWidget->actAddCard(card, zoneName); deckStateManager->addCard(card, zoneName);
databaseDisplayDockWidget->searchEdit->setSelection(0, databaseDisplayDockWidget->searchEdit->text().length()); databaseDisplayDockWidget->searchEdit->setSelection(0, databaseDisplayDockWidget->searchEdit->text().length());
} }
@ -170,13 +148,13 @@ void AbstractTabDeckEditor::actAddCardToSideboard(const ExactCard &card)
/** @brief Decrements a card from the main deck. */ /** @brief Decrements a card from the main deck. */
void AbstractTabDeckEditor::actDecrementCard(const ExactCard &card) void AbstractTabDeckEditor::actDecrementCard(const ExactCard &card)
{ {
emit decrementCard(card, DECK_ZONE_MAIN); deckStateManager->decrementCard(card, DECK_ZONE_MAIN);
} }
/** @brief Decrements a card from the sideboard. */ /** @brief Decrements a card from the sideboard. */
void AbstractTabDeckEditor::actDecrementCardFromSideboard(const ExactCard &card) void AbstractTabDeckEditor::actDecrementCardFromSideboard(const ExactCard &card)
{ {
emit decrementCard(card, DECK_ZONE_SIDE); deckStateManager->decrementCard(card, DECK_ZONE_SIDE);
} }
/** /**
@ -198,45 +176,13 @@ void AbstractTabDeckEditor::openDeck(const LoadedDeck &deck)
*/ */
void AbstractTabDeckEditor::setDeck(const LoadedDeck &_deck) void AbstractTabDeckEditor::setDeck(const LoadedDeck &_deck)
{ {
deckDockWidget->setDeck(_deck); deckStateManager->replaceDeck(_deck);
CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(getDeckList().getCardRefList())); CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(_deck.deckList.getCardRefList()));
setModified(false);
aDeckDockVisible->setChecked(true); aDeckDockVisible->setChecked(true);
deckDockWidget->setVisible(aDeckDockVisible->isChecked()); deckDockWidget->setVisible(aDeckDockVisible->isChecked());
} }
/** @brief Returns the currently loaded deck. */
DeckLoader *AbstractTabDeckEditor::getDeckLoader() const
{
return deckDockWidget->getDeckLoader();
}
/** @brief Returns the currently loaded deck list. */
const DeckList &AbstractTabDeckEditor::getDeckList() const
{
return deckDockWidget->getDeckList();
}
/**
* @brief Sets the modified state of the tab.
* @param _modified True if tab is modified, false otherwise.
*/
void AbstractTabDeckEditor::setModified(bool _modified)
{
modified = _modified;
emit tabTextChanged(this, getTabText());
}
/**
* @brief Returns true if the tab is a blank newly created deck.
*/
bool AbstractTabDeckEditor::isBlankNewDeck() const
{
const LoadedDeck &loadedDeck = deckDockWidget->getDeckLoader()->getDeck();
return !modified && loadedDeck.isEmpty();
}
/** @brief Creates a new deck. Handles opening in new tab if needed. */ /** @brief Creates a new deck. Handles opening in new tab if needed. */
void AbstractTabDeckEditor::actNewDeck() void AbstractTabDeckEditor::actNewDeck()
{ {
@ -255,9 +201,8 @@ void AbstractTabDeckEditor::actNewDeck()
/** @brief Clears the current deck and resets modified flag. */ /** @brief Clears the current deck and resets modified flag. */
void AbstractTabDeckEditor::cleanDeckAndResetModified() void AbstractTabDeckEditor::cleanDeckAndResetModified()
{ {
deckStateManager->clearDeck();
deckMenu->setSaveStatus(false); deckMenu->setSaveStatus(false);
deckDockWidget->cleanDeck();
setModified(false);
} }
/** /**
@ -268,13 +213,13 @@ void AbstractTabDeckEditor::cleanDeckAndResetModified()
AbstractTabDeckEditor::DeckOpenLocation AbstractTabDeckEditor::confirmOpen(const bool openInSameTabIfBlank) AbstractTabDeckEditor::DeckOpenLocation AbstractTabDeckEditor::confirmOpen(const bool openInSameTabIfBlank)
{ {
if (SettingsCache::instance().getOpenDeckInNewTab()) { if (SettingsCache::instance().getOpenDeckInNewTab()) {
if (openInSameTabIfBlank && isBlankNewDeck()) if (openInSameTabIfBlank && deckStateManager->isBlankNewDeck())
return SAME_TAB; return SAME_TAB;
else else
return NEW_TAB; return NEW_TAB;
} }
if (!modified) if (!deckStateManager->isModified())
return SAME_TAB; return SAME_TAB;
tabSupervisor->setCurrentWidget(this); tabSupervisor->setCurrentWidget(this);
@ -325,7 +270,6 @@ void AbstractTabDeckEditor::actLoadDeck()
QString fileName = dialog.selectedFiles().at(0); QString fileName = dialog.selectedFiles().at(0);
openDeckFromFile(fileName, deckOpenLocation); openDeckFromFile(fileName, deckOpenLocation);
deckDockWidget->updateBannerCardComboBox();
} }
/** /**
@ -371,7 +315,7 @@ void AbstractTabDeckEditor::openDeckFromFile(const QString &fileName, DeckOpenLo
*/ */
bool AbstractTabDeckEditor::actSaveDeck() bool AbstractTabDeckEditor::actSaveDeck()
{ {
const LoadedDeck &loadedDeck = getDeckLoader()->getDeck(); const auto loadedDeck = deckStateManager->toLoadedDeck();
if (loadedDeck.lastLoadInfo.remoteDeckId != LoadedDeck::LoadInfo::NON_REMOTE_ID) { if (loadedDeck.lastLoadInfo.remoteDeckId != LoadedDeck::LoadInfo::NON_REMOTE_ID) {
QString deckString = loadedDeck.deckList.writeToString_Native(); QString deckString = loadedDeck.deckList.writeToString_Native();
if (deckString.length() > MAX_FILE_LENGTH) { if (deckString.length() > MAX_FILE_LENGTH) {
@ -392,8 +336,10 @@ bool AbstractTabDeckEditor::actSaveDeck()
if (loadedDeck.lastLoadInfo.fileName.isEmpty()) if (loadedDeck.lastLoadInfo.fileName.isEmpty())
return actSaveDeckAs(); return actSaveDeckAs();
if (getDeckLoader()->saveToFile(loadedDeck.lastLoadInfo.fileName, loadedDeck.lastLoadInfo.fileFormat)) { auto deckLoader = DeckLoader(this);
setModified(false); deckLoader.setDeck(loadedDeck);
if (deckLoader.saveToFile(loadedDeck.lastLoadInfo.fileName, loadedDeck.lastLoadInfo.fileFormat)) {
deckStateManager->setModified(false);
return true; return true;
} }
@ -409,12 +355,14 @@ bool AbstractTabDeckEditor::actSaveDeck()
*/ */
bool AbstractTabDeckEditor::actSaveDeckAs() bool AbstractTabDeckEditor::actSaveDeckAs()
{ {
LoadedDeck loadedDeck = deckStateManager->toLoadedDeck();
QFileDialog dialog(this, tr("Save deck")); QFileDialog dialog(this, tr("Save deck"));
dialog.setDirectory(SettingsCache::instance().getDeckPath()); dialog.setDirectory(SettingsCache::instance().getDeckPath());
dialog.setAcceptMode(QFileDialog::AcceptSave); dialog.setAcceptMode(QFileDialog::AcceptSave);
dialog.setDefaultSuffix("cod"); dialog.setDefaultSuffix("cod");
dialog.setNameFilters(DeckLoader::FILE_NAME_FILTERS); dialog.setNameFilters(DeckLoader::FILE_NAME_FILTERS);
dialog.selectFile(getDeckList().getName().trimmed()); dialog.selectFile(loadedDeck.deckList.getName().trimmed());
if (!dialog.exec()) if (!dialog.exec())
return false; return false;
@ -422,14 +370,18 @@ bool AbstractTabDeckEditor::actSaveDeckAs()
QString fileName = dialog.selectedFiles().at(0); QString fileName = dialog.selectedFiles().at(0);
DeckFileFormat::Format fmt = DeckFileFormat::getFormatFromName(fileName); DeckFileFormat::Format fmt = DeckFileFormat::getFormatFromName(fileName);
if (!getDeckLoader()->saveToFile(fileName, fmt)) { DeckLoader deckLoader = DeckLoader(this);
deckLoader.setDeck(loadedDeck);
if (!deckLoader.saveToFile(fileName, fmt)) {
QMessageBox::critical( QMessageBox::critical(
this, tr("Error"), this, tr("Error"),
tr("The deck could not be saved.\nPlease check that the directory is writable and try again.")); tr("The deck could not be saved.\nPlease check that the directory is writable and try again."));
return false; return false;
} }
setModified(false); deckStateManager->setLastLoadInfo({.fileName = fileName, .fileFormat = fmt});
deckStateManager->setModified(false);
SettingsCache::instance().recents().updateRecentlyOpenedDeckPaths(fileName); SettingsCache::instance().recents().updateRecentlyOpenedDeckPaths(fileName);
return true; return true;
} }
@ -443,7 +395,7 @@ void AbstractTabDeckEditor::saveDeckRemoteFinished(const Response &response)
if (response.response_code() != Response::RespOk) if (response.response_code() != Response::RespOk)
QMessageBox::critical(this, tr("Error"), tr("The deck could not be saved.")); QMessageBox::critical(this, tr("Error"), tr("The deck could not be saved."));
else else
setModified(false); deckStateManager->setModified(false);
} }
/** /**
@ -464,7 +416,7 @@ void AbstractTabDeckEditor::actLoadDeckFromClipboard()
emit openDeckEditor({.deckList = dlg.getDeckList()}); emit openDeckEditor({.deckList = dlg.getDeckList()});
} else { } else {
setDeck({.deckList = dlg.getDeckList()}); setDeck({.deckList = dlg.getDeckList()});
setModified(true); deckStateManager->setModified(true);
} }
deckMenu->setSaveStatus(true); deckMenu->setSaveStatus(true);
@ -476,12 +428,13 @@ void AbstractTabDeckEditor::actLoadDeckFromClipboard()
*/ */
void AbstractTabDeckEditor::editDeckInClipboard(bool annotated) void AbstractTabDeckEditor::editDeckInClipboard(bool annotated)
{ {
DlgEditDeckInClipboard dlg(getDeckLoader()->getDeck().deckList, annotated, this); LoadedDeck loadedDeck = deckStateManager->toLoadedDeck();
DlgEditDeckInClipboard dlg(loadedDeck.deckList, annotated, this);
if (!dlg.exec()) if (!dlg.exec())
return; return;
setDeck({dlg.getDeckList(), getDeckLoader()->getDeck().lastLoadInfo}); setDeck({dlg.getDeckList(), loadedDeck.lastLoadInfo});
setModified(true); deckStateManager->setModified(true);
deckMenu->setSaveStatus(true); deckMenu->setSaveStatus(true);
} }
@ -500,25 +453,25 @@ void AbstractTabDeckEditor::actEditDeckInClipboardRaw()
/** @brief Saves deck to clipboard with set info and annotation. */ /** @brief Saves deck to clipboard with set info and annotation. */
void AbstractTabDeckEditor::actSaveDeckToClipboard() void AbstractTabDeckEditor::actSaveDeckToClipboard()
{ {
DeckLoader::saveToClipboard(getDeckList(), true, true); DeckLoader::saveToClipboard(deckStateManager->getDeckList(), true, true);
} }
/** @brief Saves deck to clipboard with annotation, without set info. */ /** @brief Saves deck to clipboard with annotation, without set info. */
void AbstractTabDeckEditor::actSaveDeckToClipboardNoSetInfo() void AbstractTabDeckEditor::actSaveDeckToClipboardNoSetInfo()
{ {
DeckLoader::saveToClipboard(getDeckList(), true, false); DeckLoader::saveToClipboard(deckStateManager->getDeckList(), true, false);
} }
/** @brief Saves deck to clipboard without annotations, with set info. */ /** @brief Saves deck to clipboard without annotations, with set info. */
void AbstractTabDeckEditor::actSaveDeckToClipboardRaw() void AbstractTabDeckEditor::actSaveDeckToClipboardRaw()
{ {
DeckLoader::saveToClipboard(getDeckList(), false, true); DeckLoader::saveToClipboard(deckStateManager->getDeckList(), false, true);
} }
/** @brief Saves deck to clipboard without annotations or set info. */ /** @brief Saves deck to clipboard without annotations or set info. */
void AbstractTabDeckEditor::actSaveDeckToClipboardRawNoSetInfo() void AbstractTabDeckEditor::actSaveDeckToClipboardRawNoSetInfo()
{ {
DeckLoader::saveToClipboard(getDeckList(), false, false); DeckLoader::saveToClipboard(deckStateManager->getDeckList(), false, false);
} }
/** @brief Prints the deck using a QPrintPreviewDialog. */ /** @brief Prints the deck using a QPrintPreviewDialog. */
@ -526,7 +479,7 @@ void AbstractTabDeckEditor::actPrintDeck()
{ {
auto *dlg = new QPrintPreviewDialog(this); auto *dlg = new QPrintPreviewDialog(this);
connect(dlg, &QPrintPreviewDialog::paintRequested, this, connect(dlg, &QPrintPreviewDialog::paintRequested, this,
[this](QPrinter *printer) { DeckLoader::printDeckList(printer, getDeckList()); }); [this](QPrinter *printer) { DeckLoader::printDeckList(printer, deckStateManager->getDeckList()); });
dlg->exec(); dlg->exec();
} }
@ -547,7 +500,7 @@ void AbstractTabDeckEditor::actLoadDeckFromWebsite()
emit openDeckEditor({.deckList = dlg.getDeck()}); emit openDeckEditor({.deckList = dlg.getDeck()});
} else { } else {
setDeck({.deckList = dlg.getDeck()}); setDeck({.deckList = dlg.getDeck()});
setModified(true); deckStateManager->setModified(true);
} }
deckMenu->setSaveStatus(true); deckMenu->setSaveStatus(true);
@ -559,7 +512,7 @@ void AbstractTabDeckEditor::actLoadDeckFromWebsite()
*/ */
void AbstractTabDeckEditor::exportToDecklistWebsite(DeckLoader::DecklistWebsite website) void AbstractTabDeckEditor::exportToDecklistWebsite(DeckLoader::DecklistWebsite website)
{ {
QString decklistUrlString = DeckLoader::exportDeckToDecklist(getDeckList(), website); QString decklistUrlString = DeckLoader::exportDeckToDecklist(deckStateManager->getDeckList(), website);
// Check to make sure the string isn't empty. // Check to make sure the string isn't empty.
if (decklistUrlString.isEmpty()) { if (decklistUrlString.isEmpty()) {
// Show an error if the deck is empty, and return. // Show an error if the deck is empty, and return.
@ -592,14 +545,14 @@ void AbstractTabDeckEditor::actExportDeckDecklistXyz()
void AbstractTabDeckEditor::actAnalyzeDeckDeckstats() void AbstractTabDeckEditor::actAnalyzeDeckDeckstats()
{ {
auto *interface = new DeckStatsInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(), this); auto *interface = new DeckStatsInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(), this);
interface->analyzeDeck(getDeckList()); interface->analyzeDeck(deckStateManager->getDeckList());
} }
/** @brief Analyzes the deck using TappedOut. */ /** @brief Analyzes the deck using TappedOut. */
void AbstractTabDeckEditor::actAnalyzeDeckTappedout() void AbstractTabDeckEditor::actAnalyzeDeckTappedout()
{ {
auto *interface = new TappedOutInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(), this); auto *interface = new TappedOutInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(), this);
interface->analyzeDeck(getDeckList()); interface->analyzeDeck(deckStateManager->getDeckList());
} }
/** @brief Applies a new filter tree to the database display. */ /** @brief Applies a new filter tree to the database display. */
@ -658,7 +611,7 @@ bool AbstractTabDeckEditor::eventFilter(QObject *o, QEvent *e)
/** @brief Shows a confirmation dialog before closing. */ /** @brief Shows a confirmation dialog before closing. */
bool AbstractTabDeckEditor::confirmClose() bool AbstractTabDeckEditor::confirmClose()
{ {
if (modified) { if (deckStateManager->isModified()) {
tabSupervisor->setCurrentWidget(this); tabSupervisor->setCurrentWidget(this);
int ret = createSaveConfirmationWindow()->exec(); int ret = createSaveConfirmationWindow()->exec();
if (ret == QMessageBox::Save) if (ret == QMessageBox::Save)

View file

@ -19,6 +19,7 @@
#include <libcockatrice/deck_list/deck_list_history_manager.h> #include <libcockatrice/deck_list/deck_list_history_manager.h>
class DeckStateManager;
class CardDatabaseModel; class CardDatabaseModel;
class CardDatabaseDisplayModel; class CardDatabaseDisplayModel;
@ -117,30 +118,13 @@ public:
*/ */
void openDeck(const LoadedDeck &deck); void openDeck(const LoadedDeck &deck);
/** @brief Returns the currently active deck loader. */
DeckLoader *getDeckLoader() const;
/** @brief Returns the currently active deck list. */
const DeckList &getDeckList() const;
/** @brief Sets the modified state of the tab.
* @param _windowModified Whether the tab is modified.
*/
void setModified(bool _windowModified);
DeckEditorDeckDockWidget *getDeckDockWidget() const DeckEditorDeckDockWidget *getDeckDockWidget() const
{ {
return deckDockWidget; return deckDockWidget;
} }
DeckListHistoryManager *getHistoryManager() const
{
return historyManager;
}
DeckListHistoryManager *historyManager;
// UI Elements // UI Elements
DeckStateManager *deckStateManager;
DeckEditorMenu *deckMenu; ///< Menu for deck operations DeckEditorMenu *deckMenu; ///< Menu for deck operations
DeckEditorDatabaseDisplayWidget *databaseDisplayDockWidget; ///< Database dock DeckEditorDatabaseDisplayWidget *databaseDisplayDockWidget; ///< Database dock
DeckEditorCardInfoDockWidget *cardInfoDockWidget; ///< Card info dock DeckEditorCardInfoDockWidget *cardInfoDockWidget; ///< Card info dock
@ -155,14 +139,6 @@ public slots:
/** @brief Called when the deck is modified. */ /** @brief Called when the deck is modified. */
virtual void onDeckModified(); virtual void onDeckModified();
/** @brief Called when a widget is about to modify the state of the DeckList.
* @param modificationReason The reason for the state modification
*/
virtual void onDeckHistorySaveRequested(const QString &modificationReason);
/** @brief Called when a widget would like to clear the history. */
virtual void onDeckHistoryClearRequested();
/** @brief Updates the card info panel. /** @brief Updates the card info panel.
* @param card The card to display. * @param card The card to display.
*/ */
@ -202,9 +178,6 @@ signals:
/** @brief Emitted before the tab is closed. */ /** @brief Emitted before the tab is closed. */
void deckEditorClosing(AbstractTabDeckEditor *tab); void deckEditorClosing(AbstractTabDeckEditor *tab);
/** @brief Emitted when a card should be decremented. */
void decrementCard(const ExactCard &card, QString zoneName);
protected slots: protected slots:
/** @brief Starts a new deck in this tab. */ /** @brief Starts a new deck in this tab. */
virtual void actNewDeck(); virtual void actNewDeck();
@ -315,9 +288,6 @@ protected:
*/ */
QMessageBox *createSaveConfirmationWindow(); QMessageBox *createSaveConfirmationWindow();
/** @brief Returns true if the tab is a blank newly created deck. */
bool isBlankNewDeck() const;
/** @brief Helper function to add a card to a specific deck zone. */ /** @brief Helper function to add a card to a specific deck zone. */
void addCardHelper(const ExactCard &card, const QString &zoneName); void addCardHelper(const ExactCard &card, const QString &zoneName);
@ -330,8 +300,6 @@ protected:
QAction *aResetLayout; QAction *aResetLayout;
QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aDeckDockVisible, *aDeckDockFloating; QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aDeckDockVisible, *aDeckDockFloating;
QAction *aFilterDockVisible, *aFilterDockFloating, *aPrintingSelectorDockVisible, *aPrintingSelectorDockFloating; QAction *aFilterDockVisible, *aFilterDockFloating, *aPrintingSelectorDockVisible, *aPrintingSelectorDockFloating;
bool modified = false; ///< Whether the deck/tab has unsaved changes
}; };
#endif // TAB_GENERIC_DECK_EDITOR_H #endif // TAB_GENERIC_DECK_EDITOR_H

View file

@ -68,7 +68,10 @@ ArchidektApiResponseDeckDisplayWidget::ArchidektApiResponseDeckDisplayWidget(QWi
model = new DeckListModel(this); model = new DeckListModel(this);
connect(model, &DeckListModel::modelReset, this, &ArchidektApiResponseDeckDisplayWidget::decklistModelReset); connect(model, &DeckListModel::modelReset, this, &ArchidektApiResponseDeckDisplayWidget::decklistModelReset);
model->getDeckList()->loadFromStream_Plain(deckStream, false);
auto decklist = QSharedPointer<DeckList>(new DeckList);
decklist->loadFromStream_Plain(deckStream, false);
model->setDeckList(decklist);
model->forEachCard(CardNodeFunction::ResolveProviderId()); model->forEachCard(CardNodeFunction::ResolveProviderId());

View file

@ -1,6 +1,7 @@
#include "tab_deck_editor.h" #include "tab_deck_editor.h"
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
#include "../deck_editor/deck_state_manager.h"
#include "../filters/filter_builder.h" #include "../filters/filter_builder.h"
#include "../interface/pixel_map_generator.h" #include "../interface/pixel_map_generator.h"
#include "../interface/widgets/cards/card_info_frame_widget.h" #include "../interface/widgets/cards/card_info_frame_widget.h"
@ -114,8 +115,8 @@ void TabDeckEditor::createMenus()
*/ */
QString TabDeckEditor::getTabText() const QString TabDeckEditor::getTabText() const
{ {
QString result = tr("Deck: %1").arg(deckDockWidget->getSimpleDeckName()); QString result = tr("Deck: %1").arg(deckStateManager->getSimpleDeckName());
if (modified) if (deckStateManager->isModified())
result.prepend("* "); result.prepend("* ");
return result; return result;
} }

View file

@ -1,6 +1,7 @@
#include "tab_deck_editor_visual.h" #include "tab_deck_editor_visual.h"
#include "../../../../client/settings/cache_settings.h" #include "../../../../client/settings/cache_settings.h"
#include "../../deck_editor/deck_state_manager.h"
#include "../../filters/filter_builder.h" #include "../../filters/filter_builder.h"
#include "../../interface/pixel_map_generator.h" #include "../../interface/pixel_map_generator.h"
#include "../../interface/widgets/cards/card_info_frame_widget.h" #include "../../interface/widgets/cards/card_info_frame_widget.h"
@ -61,7 +62,7 @@ void TabDeckEditorVisual::createCentralFrame()
centralFrame = new QVBoxLayout; centralFrame = new QVBoxLayout;
centralWidget->setLayout(centralFrame); centralWidget->setLayout(centralFrame);
tabContainer = new TabDeckEditorVisualTabWidget(centralWidget, this, deckDockWidget->deckModel, tabContainer = new TabDeckEditorVisualTabWidget(centralWidget, this, deckStateManager->getModel(),
databaseDisplayDockWidget->databaseModel, databaseDisplayDockWidget->databaseModel,
databaseDisplayDockWidget->databaseDisplayModel); databaseDisplayDockWidget->databaseDisplayModel);
@ -85,7 +86,7 @@ void TabDeckEditorVisual::onDeckChanged()
AbstractTabDeckEditor::onDeckModified(); AbstractTabDeckEditor::onDeckModified();
tabContainer->visualDeckView->constructZoneWidgetsFromDeckListModel(); tabContainer->visualDeckView->constructZoneWidgetsFromDeckListModel();
tabContainer->deckAnalytics->refreshDisplays(); tabContainer->deckAnalytics->refreshDisplays();
tabContainer->sampleHandWidget->setDeckModel(deckDockWidget->deckModel); tabContainer->sampleHandWidget->setDeckModel(deckStateManager->getModel());
} }
/** @brief Creates menus for deck editing and view options, including dock actions. */ /** @brief Creates menus for deck editing and view options, including dock actions. */
@ -149,8 +150,8 @@ void TabDeckEditorVisual::createMenus()
/** @brief Returns the tab text, prepending a mark if the deck has unsaved changes. */ /** @brief Returns the tab text, prepending a mark if the deck has unsaved changes. */
QString TabDeckEditorVisual::getTabText() const QString TabDeckEditorVisual::getTabText() const
{ {
QString result = tr("Visual Deck: %1").arg(deckDockWidget->getSimpleDeckName()); QString result = tr("Visual Deck: %1").arg(deckStateManager->getSimpleDeckName());
if (modified) if (deckStateManager->isModified())
result.prepend("* "); result.prepend("* ");
return result; return result;
} }
@ -166,9 +167,9 @@ void TabDeckEditorVisual::changeModelIndexAndCardInfo(const ExactCard &activeCar
void TabDeckEditorVisual::changeModelIndexToCard(const ExactCard &activeCard) void TabDeckEditorVisual::changeModelIndexToCard(const ExactCard &activeCard)
{ {
QString cardName = activeCard.getName(); QString cardName = activeCard.getName();
QModelIndex index = deckDockWidget->deckModel->findCard(cardName, DECK_ZONE_MAIN); QModelIndex index = deckStateManager->getModel()->findCard(cardName, DECK_ZONE_MAIN);
if (!index.isValid()) { if (!index.isValid()) {
index = deckDockWidget->deckModel->findCard(cardName, DECK_ZONE_SIDE); index = deckStateManager->getModel()->findCard(cardName, DECK_ZONE_SIDE);
} }
if (!deckDockWidget->getSelectionModel()->hasSelection()) { if (!deckDockWidget->getSelectionModel()->hasSelection()) {
deckDockWidget->getSelectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); deckDockWidget->getSelectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
@ -182,7 +183,7 @@ void TabDeckEditorVisual::processMainboardCardClick(QMouseEvent *event,
auto card = instance->getCard(); auto card = instance->getCard();
// Get the model index for the card // Get the model index for the card
QModelIndex idx = deckDockWidget->deckModel->findCard(card.getName(), zoneName); QModelIndex idx = deckStateManager->getModel()->findCard(card.getName(), zoneName);
if (!idx.isValid()) { if (!idx.isValid()) {
return; return;
} }
@ -191,8 +192,8 @@ void TabDeckEditorVisual::processMainboardCardClick(QMouseEvent *event,
// Double click = swap // Double click = swap
if (event->type() == QEvent::MouseButtonDblClick && event->button() == Qt::LeftButton) { if (event->type() == QEvent::MouseButtonDblClick && event->button() == Qt::LeftButton) {
deckDockWidget->actSwapCard(card, zoneName); deckStateManager->swapCardAtIndex(idx);
idx = deckDockWidget->deckModel->findCard(card.getName(), zoneName); idx = deckStateManager->getModel()->findCard(card.getName(), zoneName);
sel->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect); sel->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
return; return;
} }

View file

@ -2,6 +2,7 @@
#include "../../../interface/widgets/dialogs/dlg_load_deck_from_clipboard.h" #include "../../../interface/widgets/dialogs/dlg_load_deck_from_clipboard.h"
#include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h" #include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h"
#include "../deck_editor/deck_state_manager.h"
#include <QHBoxLayout> #include <QHBoxLayout>
@ -60,7 +61,7 @@ void VisualDatabaseDisplayNameFilterWidget::retranslateUi()
void VisualDatabaseDisplayNameFilterWidget::actLoadFromDeck() void VisualDatabaseDisplayNameFilterWidget::actLoadFromDeck()
{ {
DeckListModel *deckListModel = deckEditor->deckDockWidget->deckModel; DeckListModel *deckListModel = deckEditor->deckStateManager->getModel();
if (!deckListModel) if (!deckListModel)
return; return;

View file

@ -5,14 +5,15 @@
DeckListModel::DeckListModel(QObject *parent) DeckListModel::DeckListModel(QObject *parent)
: QAbstractItemModel(parent), lastKnownColumn(1), lastKnownOrder(Qt::AscendingOrder) : QAbstractItemModel(parent), lastKnownColumn(1), lastKnownOrder(Qt::AscendingOrder)
{ {
// This class will leak the decklist object. We cannot safely delete it in the dtor because the deckList field is a deckList = QSharedPointer<DeckList>(new DeckList());
// non-owning pointer and another deckList might have been assigned to it.
// `DeckListModel::cleanList` also leaks for the same reason.
// TODO: fix the leak
deckList = new DeckList;
root = new InnerDecklistNode; root = new InnerDecklistNode;
} }
DeckListModel::DeckListModel(QObject *parent, const QSharedPointer<DeckList> &deckList) : DeckListModel(parent)
{
setDeckList(deckList);
}
DeckListModel::~DeckListModel() DeckListModel::~DeckListModel()
{ {
delete root; delete root;
@ -586,13 +587,13 @@ void DeckListModel::setActiveFormat(const QString &_format)
void DeckListModel::cleanList() void DeckListModel::cleanList()
{ {
setDeckList(new DeckList); setDeckList(QSharedPointer<DeckList>(new DeckList()));
} }
/** /**
* @param _deck The deck. * @param _deck The deck.
*/ */
void DeckListModel::setDeckList(DeckList *_deck) void DeckListModel::setDeckList(const QSharedPointer<DeckList> &_deck)
{ {
if (deckList != _deck) { if (deckList != _deck) {
deckList = _deck; deckList = _deck;

View file

@ -245,6 +245,7 @@ signals:
public: public:
explicit DeckListModel(QObject *parent = nullptr); explicit DeckListModel(QObject *parent = nullptr);
explicit DeckListModel(QObject *parent, const QSharedPointer<DeckList> &deckList);
~DeckListModel() override; ~DeckListModel() override;
/** /**
@ -314,11 +315,12 @@ public:
* @brief Removes all cards and resets the model. * @brief Removes all cards and resets the model.
*/ */
void cleanList(); void cleanList();
[[nodiscard]] DeckList *getDeckList() const
[[nodiscard]] QSharedPointer<DeckList> getDeckList() const
{ {
return deckList; return deckList;
} }
void setDeckList(DeckList *_deck); void setDeckList(const QSharedPointer<DeckList> &_deck);
/** /**
* @brief Apply a function to every card in the deck tree. * @brief Apply a function to every card in the deck tree.
@ -351,8 +353,8 @@ public:
[[nodiscard]] QList<QString> getZones() const; [[nodiscard]] QList<QString> getZones() const;
private: private:
DeckList *deckList; /**< Pointer to the deck loader providing the underlying data. */ QSharedPointer<DeckList> deckList; /**< Pointer to the decklist providing the underlying data. */
InnerDecklistNode *root; /**< Root node of the model tree. */ InnerDecklistNode *root; /**< Root node of the model tree. */
DeckListModelGroupCriteria::Type activeGroupCriteria = DeckListModelGroupCriteria::MAIN_TYPE; DeckListModelGroupCriteria::Type activeGroupCriteria = DeckListModelGroupCriteria::MAIN_TYPE;
int lastKnownColumn; /**< Last column used for sorting. */ int lastKnownColumn; /**< Last column used for sorting. */
Qt::SortOrder lastKnownOrder; /**< Last known sort order. */ Qt::SortOrder lastKnownOrder; /**< Last known sort order. */