mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-04-27 07:48:01 -07:00
[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:
parent
0085015ebe
commit
b2dd8eed3f
31 changed files with 933 additions and 577 deletions
|
|
@ -1,8 +1,8 @@
|
|||
#include "deck_editor_deck_dock_widget.h"
|
||||
|
||||
#include "../../../client/settings/cache_settings.h"
|
||||
#include "../../deck_loader/deck_loader.h"
|
||||
#include "deck_list_style_proxy.h"
|
||||
#include "deck_state_manager.h"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDockWidget>
|
||||
|
|
@ -38,7 +38,7 @@ static int findRestoreIndex(const CardRef &wanted, const QComboBox *combo)
|
|||
}
|
||||
|
||||
DeckEditorDeckDockWidget::DeckEditorDeckDockWidget(AbstractTabDeckEditor *parent)
|
||||
: QDockWidget(parent), deckEditor(parent)
|
||||
: QDockWidget(parent), deckEditor(parent), deckStateManager(parent->deckStateManager)
|
||||
{
|
||||
setObjectName("deckDock");
|
||||
|
||||
|
|
@ -52,19 +52,19 @@ DeckEditorDeckDockWidget::DeckEditorDeckDockWidget(AbstractTabDeckEditor *parent
|
|||
|
||||
void DeckEditorDeckDockWidget::createDeckDock()
|
||||
{
|
||||
deckModel = new DeckListModel(this);
|
||||
deckModel->setObjectName("deckModel");
|
||||
connect(deckModel, &DeckListModel::deckHashChanged, this, &DeckEditorDeckDockWidget::updateHash);
|
||||
|
||||
deckLoader = new DeckLoader(this);
|
||||
connect(getModel(), &DeckListModel::deckHashChanged, this, &DeckEditorDeckDockWidget::updateHash);
|
||||
|
||||
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,
|
||||
&DeckEditorDeckDockWidget::syncDisplayWidgetsToModel);
|
||||
|
||||
connect(deckStateManager, &DeckStateManager::focusIndexChanged, this, &DeckEditorDeckDockWidget::setSelectedIndex);
|
||||
connect(deckStateManager, &DeckStateManager::deckReplaced, this,
|
||||
&DeckEditorDeckDockWidget::syncDisplayWidgetsToModel);
|
||||
|
||||
deckView = new QTreeView();
|
||||
deckView->setObjectName("deckView");
|
||||
deckView->setModel(proxy);
|
||||
|
|
@ -97,7 +97,7 @@ void DeckEditorDeckDockWidget::createDeckDock()
|
|||
nameDebounceTimer = new QTimer(this);
|
||||
nameDebounceTimer->setSingleShot(true);
|
||||
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]() {
|
||||
nameDebounceTimer->start(); // restart debounce timer
|
||||
|
|
@ -141,7 +141,7 @@ void DeckEditorDeckDockWidget::createDeckDock()
|
|||
commentsDebounceTimer = new QTimer(this);
|
||||
commentsDebounceTimer->setSingleShot(true);
|
||||
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]() {
|
||||
commentsDebounceTimer->start(); // restart debounce timer
|
||||
|
|
@ -152,21 +152,21 @@ void DeckEditorDeckDockWidget::createDeckDock()
|
|||
bannerCardLabel->setText(tr("Banner Card"));
|
||||
bannerCardLabel->setHidden(!SettingsCache::instance().getDeckEditorBannerCardComboBoxVisible());
|
||||
bannerCardComboBox = new QComboBox(this);
|
||||
connect(deckModel, &DeckListModel::dataChanged, this, [this]() {
|
||||
connect(getModel(), &DeckListModel::dataChanged, this, [this]() {
|
||||
// Delay the update to avoid race conditions
|
||||
QTimer::singleShot(100, this, &DeckEditorDeckDockWidget::updateBannerCardComboBox);
|
||||
});
|
||||
connect(deckModel, &DeckListModel::cardAddedAt, this, &DeckEditorDeckDockWidget::recursiveExpand);
|
||||
connect(deckModel, &DeckListModel::modelReset, this, &DeckEditorDeckDockWidget::expandAll);
|
||||
connect(getModel(), &DeckListModel::cardAddedAt, this, &DeckEditorDeckDockWidget::recursiveExpand);
|
||||
connect(getModel(), &DeckListModel::modelReset, this, &DeckEditorDeckDockWidget::expandAll);
|
||||
|
||||
connect(bannerCardComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&DeckEditorDeckDockWidget::setBannerCard);
|
||||
&DeckEditorDeckDockWidget::writeBannerCard);
|
||||
bannerCardComboBox->setHidden(!SettingsCache::instance().getDeckEditorBannerCardComboBoxVisible());
|
||||
|
||||
deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckModel->getDeckList()->getTags());
|
||||
deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, {});
|
||||
deckTagsDisplayWidget->setHidden(!SettingsCache::instance().getDeckEditorTagsWidgetVisible());
|
||||
connect(deckTagsDisplayWidget, &DeckPreviewDeckTagsDisplayWidget::tagsChanged, this,
|
||||
&DeckEditorDeckDockWidget::setTags);
|
||||
connect(deckTagsDisplayWidget, &DeckPreviewDeckTagsDisplayWidget::tagsChanged, deckStateManager,
|
||||
&DeckStateManager::setTags);
|
||||
|
||||
activeGroupCriteriaLabel = new QLabel(this);
|
||||
|
||||
|
|
@ -175,9 +175,9 @@ void DeckEditorDeckDockWidget::createDeckDock()
|
|||
activeGroupCriteriaComboBox->addItem(tr("Mana Cost"), DeckListModelGroupCriteria::MANA_COST);
|
||||
activeGroupCriteriaComboBox->addItem(tr("Colors"), DeckListModelGroupCriteria::COLOR);
|
||||
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()));
|
||||
deckModel->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder());
|
||||
getModel()->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder());
|
||||
});
|
||||
|
||||
aIncrement = new QAction(QString(), this);
|
||||
|
|
@ -295,9 +295,10 @@ void DeckEditorDeckDockWidget::initializeFormats()
|
|||
formatComboBox->addItem(formatName, formatName); // store the raw key in itemData
|
||||
}
|
||||
|
||||
if (!deckModel->getDeckList()->getGameFormat().isEmpty()) {
|
||||
deckModel->setActiveFormat(deckModel->getDeckList()->getGameFormat());
|
||||
formatComboBox->setCurrentIndex(formatComboBox->findData(deckModel->getDeckList()->getGameFormat()));
|
||||
QString format = deckStateManager->getMetadata().gameFormat;
|
||||
if (!format.isEmpty()) {
|
||||
getModel()->setActiveFormat(format);
|
||||
formatComboBox->setCurrentIndex(formatComboBox->findData(format));
|
||||
} else {
|
||||
// Ensure no selection is visible initially
|
||||
formatComboBox->setCurrentIndex(-1);
|
||||
|
|
@ -306,11 +307,10 @@ void DeckEditorDeckDockWidget::initializeFormats()
|
|||
connect(formatComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) {
|
||||
if (index >= 0) {
|
||||
QString formatKey = formatComboBox->itemData(index).toString();
|
||||
deckModel->setActiveFormat(formatKey);
|
||||
deckStateManager->setFormat(formatKey);
|
||||
} 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 /*¤t*/, const QModelIndex & /*previous*/)
|
||||
{
|
||||
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(tr("Rename deck to \"%1\" from \"%2\"")).arg(name).arg(deckLoader->getDeck().deckList.getName()));
|
||||
deckModel->getDeckList()->setName(name);
|
||||
deckEditor->setModified(name.isEmpty());
|
||||
emit nameChanged();
|
||||
emit deckModified();
|
||||
QString name = nameEdit->text();
|
||||
deckStateManager->setName(name);
|
||||
}
|
||||
|
||||
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)")
|
||||
.arg(deckLoader->getDeck().deckList.getComments().size())
|
||||
.arg(commentsEdit->toPlainText().size()));
|
||||
|
||||
deckModel->getDeckList()->setComments(commentsEdit->toPlainText());
|
||||
deckEditor->setModified(commentsEdit->toPlainText().isEmpty());
|
||||
emit commentsChanged();
|
||||
emit deckModified();
|
||||
QString comments = commentsEdit->toPlainText();
|
||||
deckStateManager->setComments(comments);
|
||||
}
|
||||
|
||||
void DeckEditorDeckDockWidget::updateHash()
|
||||
{
|
||||
hashLabel->setText(deckModel->getDeckList()->getDeckHash());
|
||||
emit hashChanged();
|
||||
emit deckModified();
|
||||
hashLabel->setText(deckStateManager->getDeckHash());
|
||||
}
|
||||
|
||||
void DeckEditorDeckDockWidget::updateBannerCardComboBox()
|
||||
{
|
||||
// Store current banner card identity
|
||||
CardRef wanted = deckModel->getDeckList()->getBannerCard();
|
||||
CardRef wanted = deckStateManager->getMetadata().bannerCard;
|
||||
|
||||
// Block signals temporarily
|
||||
bool wasBlocked = bannerCardComboBox->blockSignals(true);
|
||||
|
|
@ -386,7 +380,7 @@ void DeckEditorDeckDockWidget::updateBannerCardComboBox()
|
|||
|
||||
// Collect unique (name, providerId) pairs
|
||||
QSet<QPair<QString, QString>> bannerCardSet;
|
||||
QList<CardRef> cardsInDeck = deckModel->getCardRefs();
|
||||
QList<CardRef> cardsInDeck = getModel()->getCardRefs();
|
||||
|
||||
for (auto cardRef : cardsInDeck) {
|
||||
if (!CardDatabaseManager::query()->getCard(cardRef)) {
|
||||
|
|
@ -415,7 +409,6 @@ void DeckEditorDeckDockWidget::updateBannerCardComboBox()
|
|||
// Handle results
|
||||
if (restoreIndex != -1) {
|
||||
bannerCardComboBox->setCurrentIndex(restoreIndex);
|
||||
syncDeckListBannerCardWithComboBox();
|
||||
} else {
|
||||
// Add a placeholder "-" and set it as the current selection
|
||||
bannerCardComboBox->insertItem(0, "-");
|
||||
|
|
@ -426,25 +419,14 @@ void DeckEditorDeckDockWidget::updateBannerCardComboBox()
|
|||
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"));
|
||||
syncDeckListBannerCardWithComboBox();
|
||||
deckEditor->setModified(true);
|
||||
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});
|
||||
auto [name, id] = bannerCardComboBox->itemData(index).value<QPair<QString, QString>>();
|
||||
CardRef bannerCard = {name, id};
|
||||
deckStateManager->setBannerCard(bannerCard);
|
||||
}
|
||||
|
||||
void DeckEditorDeckDockWidget::updateShowBannerCardComboBox(const bool visible)
|
||||
|
|
@ -460,7 +442,7 @@ void DeckEditorDeckDockWidget::updateShowTagsWidget(const bool visible)
|
|||
|
||||
void DeckEditorDeckDockWidget::syncBannerCardComboBoxSelectionWithDeck()
|
||||
{
|
||||
if (deckModel->getDeckList()->getBannerCard().name == "") {
|
||||
if (deckStateManager->getMetadata().bannerCard.name == "") {
|
||||
if (bannerCardComboBox->findText("-") != -1) {
|
||||
bannerCardComboBox->setCurrentIndex(bannerCardComboBox->findText("-"));
|
||||
} else {
|
||||
|
|
@ -468,36 +450,26 @@ void DeckEditorDeckDockWidget::syncBannerCardComboBoxSelectionWithDeck()
|
|||
bannerCardComboBox->setCurrentIndex(0);
|
||||
}
|
||||
} else {
|
||||
bannerCardComboBox->setCurrentText(deckModel->getDeckList()->getBannerCard().name);
|
||||
bannerCardComboBox->setCurrentText(deckStateManager->getMetadata().bannerCard.name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the currently active deck for this tab
|
||||
* @param _deck The deck.
|
||||
*/
|
||||
void DeckEditorDeckDockWidget::setDeck(const LoadedDeck &_deck)
|
||||
void DeckEditorDeckDockWidget::setSelectedIndex(const QModelIndex &newCardIndex)
|
||||
{
|
||||
deckLoader->setDeck(_deck);
|
||||
deckModel->setDeckList(&deckLoader->getDeck().deckList);
|
||||
connect(deckLoader, &DeckLoader::deckLoaded, deckModel, &DeckListModel::rebuildTree);
|
||||
|
||||
emit requestDeckHistoryClear();
|
||||
historyManagerWidget->setDeckListModel(deckModel);
|
||||
|
||||
syncDisplayWidgetsToModel();
|
||||
|
||||
emit deckChanged();
|
||||
deckView->clearSelection();
|
||||
deckView->setCurrentIndex(newCardIndex);
|
||||
recursiveExpand(newCardIndex);
|
||||
deckView->setFocus(Qt::FocusReason::MouseFocusReason);
|
||||
}
|
||||
|
||||
void DeckEditorDeckDockWidget::syncDisplayWidgetsToModel()
|
||||
{
|
||||
nameEdit->blockSignals(true);
|
||||
nameEdit->setText(deckModel->getDeckList()->getName());
|
||||
nameEdit->setText(deckStateManager->getMetadata().name);
|
||||
nameEdit->blockSignals(false);
|
||||
|
||||
commentsEdit->blockSignals(true);
|
||||
commentsEdit->setText(deckModel->getDeckList()->getComments());
|
||||
commentsEdit->setText(deckStateManager->getMetadata().comments);
|
||||
commentsEdit->blockSignals(false);
|
||||
|
||||
bannerCardComboBox->blockSignals(true);
|
||||
|
|
@ -507,44 +479,22 @@ void DeckEditorDeckDockWidget::syncDisplayWidgetsToModel()
|
|||
updateHash();
|
||||
sortDeckModelToDeckView();
|
||||
|
||||
deckTagsDisplayWidget->setTags(deckModel->getDeckList()->getTags());
|
||||
deckTagsDisplayWidget->setTags(deckStateManager->getMetadata().tags);
|
||||
}
|
||||
|
||||
void DeckEditorDeckDockWidget::sortDeckModelToDeckView()
|
||||
{
|
||||
deckModel->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder());
|
||||
deckModel->setActiveFormat(deckModel->getDeckList()->getGameFormat());
|
||||
formatComboBox->setCurrentIndex(formatComboBox->findData(deckModel->getDeckList()->getGameFormat()));
|
||||
|
||||
emit deckChanged();
|
||||
}
|
||||
|
||||
DeckLoader *DeckEditorDeckDockWidget::getDeckLoader()
|
||||
{
|
||||
return deckLoader;
|
||||
}
|
||||
|
||||
const DeckList &DeckEditorDeckDockWidget::getDeckList() const
|
||||
{
|
||||
return *deckModel->getDeckList();
|
||||
getModel()->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder());
|
||||
getModel()->setActiveFormat(deckStateManager->getMetadata().gameFormat);
|
||||
formatComboBox->setCurrentIndex(formatComboBox->findData(deckStateManager->getMetadata().gameFormat));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
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());
|
||||
return deckStateManager->getModel();
|
||||
}
|
||||
|
||||
void DeckEditorDeckDockWidget::selectPrevCard()
|
||||
|
|
@ -635,7 +585,7 @@ QModelIndexList DeckEditorDeckDockWidget::getSelectedCardNodes() const
|
|||
auto selectedRows = deckView->selectionModel()->selectedRows();
|
||||
|
||||
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());
|
||||
|
||||
|
|
@ -650,21 +600,7 @@ void DeckEditorDeckDockWidget::actAddCard(const ExactCard &card, const QString &
|
|||
}
|
||||
|
||||
QString zoneName = card.getInfo().getIsToken() ? DECK_ZONE_TOKENS : _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();
|
||||
deckStateManager->addCard(card, zoneName);
|
||||
}
|
||||
|
||||
void DeckEditorDeckDockWidget::actIncrementSelection()
|
||||
|
|
@ -681,12 +617,12 @@ void DeckEditorDeckDockWidget::actSwapCard(const ExactCard &card, const QString
|
|||
QString providerId = card.getPrinting().getUuid();
|
||||
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()) {
|
||||
foundCard = deckModel->findCard(card.getName(), zoneName);
|
||||
foundCard = getModel()->findCard(card.getName(), zoneName);
|
||||
}
|
||||
|
||||
swapCard(foundCard);
|
||||
deckStateManager->swapCardAtIndex(foundCard);
|
||||
}
|
||||
|
||||
void DeckEditorDeckDockWidget::actSwapSelection()
|
||||
|
|
@ -699,54 +635,15 @@ void DeckEditorDeckDockWidget::actSwapSelection()
|
|||
deckView->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
}
|
||||
|
||||
bool isModified = false;
|
||||
for (const auto ¤tIndex : selectedRows) {
|
||||
if (swapCard(currentIndex)) {
|
||||
isModified = true;
|
||||
}
|
||||
deckStateManager->swapCardAtIndex(currentIndex);
|
||||
}
|
||||
|
||||
deckView->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
|
||||
if (isModified) {
|
||||
emit deckModified();
|
||||
}
|
||||
|
||||
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 ¤tIndex)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (!card)
|
||||
|
|
@ -754,17 +651,7 @@ void DeckEditorDeckDockWidget::actDecrementCard(const ExactCard &card, QString z
|
|||
if (card.getInfo().getIsToken())
|
||||
zoneName = DECK_ZONE_TOKENS;
|
||||
|
||||
QString providerId = card.getPrinting().getUuid();
|
||||
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);
|
||||
deckStateManager->decrementCard(card, zoneName);
|
||||
}
|
||||
|
||||
void DeckEditorDeckDockWidget::actDecrementSelection()
|
||||
|
|
@ -794,25 +681,11 @@ void DeckEditorDeckDockWidget::actRemoveCard()
|
|||
deckView->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
}
|
||||
|
||||
bool isModified = false;
|
||||
for (const auto &index : selectedRows) {
|
||||
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;
|
||||
for (const auto &row : selectedRows) {
|
||||
deckStateManager->removeCardAtIndex(row);
|
||||
}
|
||||
|
||||
deckView->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
|
||||
if (isModified) {
|
||||
emit deckModified();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -822,28 +695,17 @@ void DeckEditorDeckDockWidget::actRemoveCard()
|
|||
*/
|
||||
void DeckEditorDeckDockWidget::offsetCountAtIndex(const QModelIndex &idx, bool isIncrement)
|
||||
{
|
||||
if (!idx.isValid() || deckModel->hasChildren(idx)) {
|
||||
if (!idx.isValid() || getModel()->hasChildren(idx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QModelIndex sourceIndex = proxy->mapToSource(idx);
|
||||
|
||||
QString cardName = sourceIndex.siblingAtColumn(DeckListModelColumns::CARD_NAME).data(Qt::EditRole).toString();
|
||||
QString providerId =
|
||||
sourceIndex.siblingAtColumn(DeckListModelColumns::CARD_PROVIDER_ID).data(Qt::DisplayRole).toString();
|
||||
|
||||
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();
|
||||
if (isIncrement) {
|
||||
deckStateManager->incrementCountAtIndex(sourceIndex);
|
||||
} else {
|
||||
deckStateManager->decrementCountAtIndex(sourceIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void DeckEditorDeckDockWidget::decklistCustomMenu(QPoint point)
|
||||
|
|
|
|||
|
|
@ -28,22 +28,14 @@ class DeckEditorDeckDockWidget : public QDockWidget
|
|||
Q_OBJECT
|
||||
public:
|
||||
explicit DeckEditorDeckDockWidget(AbstractTabDeckEditor *parent);
|
||||
DeckLoader *deckLoader;
|
||||
|
||||
DeckListStyleProxy *proxy;
|
||||
DeckListModel *deckModel;
|
||||
QTreeView *deckView;
|
||||
QComboBox *bannerCardComboBox;
|
||||
void createDeckDock();
|
||||
ExactCard getCurrentCard();
|
||||
void retranslateUi();
|
||||
QString getDeckName()
|
||||
{
|
||||
return nameEdit->text();
|
||||
}
|
||||
QString getSimpleDeckName()
|
||||
{
|
||||
return nameEdit->text().simplified();
|
||||
}
|
||||
|
||||
QComboBox *getGroupByComboBox()
|
||||
{
|
||||
return activeGroupCriteriaComboBox;
|
||||
|
|
@ -55,15 +47,11 @@ public:
|
|||
}
|
||||
|
||||
public slots:
|
||||
void cleanDeck();
|
||||
void selectPrevCard();
|
||||
void selectNextCard();
|
||||
void updateBannerCardComboBox();
|
||||
void setDeck(const LoadedDeck &_deck);
|
||||
void syncDisplayWidgetsToModel();
|
||||
void sortDeckModelToDeckView();
|
||||
DeckLoader *getDeckLoader();
|
||||
const DeckList &getDeckList() const;
|
||||
void actAddCard(const ExactCard &card, const QString &zoneName);
|
||||
void actIncrementSelection();
|
||||
void actDecrementCard(const ExactCard &card, QString zoneName);
|
||||
|
|
@ -74,17 +62,12 @@ public slots:
|
|||
void initializeFormats();
|
||||
|
||||
signals:
|
||||
void nameChanged();
|
||||
void commentsChanged();
|
||||
void hashChanged();
|
||||
void deckChanged();
|
||||
void deckModified();
|
||||
void requestDeckHistorySave(const QString &modificationReason);
|
||||
void requestDeckHistoryClear();
|
||||
void cardChanged(const ExactCard &_card);
|
||||
void selectedCardChanged(const ExactCard &card);
|
||||
|
||||
private:
|
||||
AbstractTabDeckEditor *deckEditor;
|
||||
DeckStateManager *deckStateManager;
|
||||
|
||||
DeckListHistoryManagerWidget *historyManagerWidget;
|
||||
KeySignals deckViewKeySignals;
|
||||
QLabel *nameLabel;
|
||||
|
|
@ -107,18 +90,17 @@ private:
|
|||
|
||||
QAction *aRemoveCard, *aIncrement, *aDecrement, *aSwapCard;
|
||||
|
||||
DeckListModel *getModel() const;
|
||||
[[nodiscard]] QModelIndexList getSelectedCardNodes() const;
|
||||
void offsetCountAtIndex(const QModelIndex &idx, bool isIncrement);
|
||||
|
||||
private slots:
|
||||
void decklistCustomMenu(QPoint point);
|
||||
bool swapCard(const QModelIndex ¤tIndex);
|
||||
void updateCard(QModelIndex, const QModelIndex ¤t);
|
||||
void updateName(const QString &name);
|
||||
void updateComments();
|
||||
void setBannerCard(int);
|
||||
void setTags(const QStringList &tags);
|
||||
void syncDeckListBannerCardWithComboBox();
|
||||
void writeName();
|
||||
void writeComments();
|
||||
void writeBannerCard(int);
|
||||
void setSelectedIndex(const QModelIndex &newCardIndex);
|
||||
void updateHash();
|
||||
void refreshShortcuts();
|
||||
void updateShowBannerCardComboBox(bool visible);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
#include "deck_list_history_manager_widget.h"
|
||||
|
||||
DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckListModel *_deckListModel,
|
||||
#include "deck_state_manager.h"
|
||||
|
||||
DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckStateManager *_deckStateManager,
|
||||
DeckListStyleProxy *_styleProxy,
|
||||
DeckListHistoryManager *manager,
|
||||
QWidget *parent)
|
||||
: QWidget(parent), deckListModel(_deckListModel), styleProxy(_styleProxy), historyManager(manager)
|
||||
: QWidget(parent), deckStateManager(_deckStateManager), styleProxy(_styleProxy)
|
||||
{
|
||||
layout = new QHBoxLayout(this);
|
||||
|
||||
|
|
@ -43,8 +44,7 @@ DeckListHistoryManagerWidget::DeckListHistoryManagerWidget(DeckListModel *_deckL
|
|||
|
||||
connect(historyList, &QListWidget::itemClicked, this, &DeckListHistoryManagerWidget::onListClicked);
|
||||
|
||||
connect(historyManager, &DeckListHistoryManager::undoRedoStateChanged, this,
|
||||
&DeckListHistoryManagerWidget::refreshList);
|
||||
connect(deckStateManager, &DeckStateManager::historyChanged, this, &DeckListHistoryManagerWidget::refreshList);
|
||||
|
||||
refreshList();
|
||||
retranslateUi();
|
||||
|
|
@ -58,15 +58,12 @@ void DeckListHistoryManagerWidget::retranslateUi()
|
|||
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()
|
||||
{
|
||||
historyList->clear();
|
||||
|
||||
DeckListHistoryManager *historyManager = deckStateManager->getHistoryManager();
|
||||
|
||||
// Fill redo section first (oldest redo at top, newest redo closest to divider)
|
||||
const auto redoStack = historyManager->getRedoStack();
|
||||
for (int i = 0; i < redoStack.size(); ++i) { // iterate forward
|
||||
|
|
@ -98,36 +95,7 @@ void DeckListHistoryManagerWidget::refreshList()
|
|||
redoButton->setEnabled(historyManager->canRedo());
|
||||
}
|
||||
|
||||
void DeckListHistoryManagerWidget::doUndo()
|
||||
{
|
||||
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)
|
||||
void DeckListHistoryManagerWidget::onListClicked(const QListWidgetItem *item)
|
||||
{
|
||||
// Ignore non-selectable items (like divider)
|
||||
if (!(item->flags() & Qt::ItemIsSelectable)) {
|
||||
|
|
@ -138,23 +106,24 @@ void DeckListHistoryManagerWidget::onListClicked(QListWidgetItem *item)
|
|||
int index = item->data(Qt::UserRole + 1).toInt();
|
||||
|
||||
if (mode == "redo") {
|
||||
const auto redoStack = historyManager->getRedoStack();
|
||||
const auto redoStack = deckStateManager->getHistoryManager()->getRedoStack();
|
||||
int steps = redoStack.size() - index;
|
||||
for (int i = 0; i < steps; ++i) {
|
||||
historyManager->redo(deckListModel->getDeckList());
|
||||
}
|
||||
deckStateManager->redo(steps);
|
||||
} else if (mode == "undo") {
|
||||
const auto undoStack = historyManager->getUndoStack();
|
||||
int steps = undoStack.size() - 1 - index;
|
||||
for (int i = 0; i < steps + 1; ++i) {
|
||||
historyManager->undo(deckListModel->getDeckList());
|
||||
}
|
||||
const auto undoStack = deckStateManager->getHistoryManager()->getUndoStack();
|
||||
int steps = undoStack.size() - index;
|
||||
deckStateManager->undo(steps);
|
||||
}
|
||||
|
||||
deckListModel->rebuildTree();
|
||||
|
||||
emit deckListModel->layoutChanged();
|
||||
emit requestDisplayWidgetSync();
|
||||
|
||||
refreshList();
|
||||
}
|
||||
|
||||
void DeckListHistoryManagerWidget::doUndo()
|
||||
{
|
||||
deckStateManager->undo();
|
||||
}
|
||||
|
||||
void DeckListHistoryManagerWidget::doRedo()
|
||||
{
|
||||
deckStateManager->redo();
|
||||
}
|
||||
|
|
@ -14,6 +14,8 @@
|
|||
#include <libcockatrice/deck_list/deck_list_history_manager.h>
|
||||
#include <libcockatrice/models/deck_list/deck_list_model.h>
|
||||
|
||||
class DeckStateManager;
|
||||
|
||||
class DeckListHistoryManagerWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
@ -25,22 +27,19 @@ public slots:
|
|||
void retranslateUi();
|
||||
|
||||
public:
|
||||
explicit DeckListHistoryManagerWidget(DeckListModel *deckListModel,
|
||||
explicit DeckListHistoryManagerWidget(DeckStateManager *deckStateManager,
|
||||
DeckListStyleProxy *styleProxy,
|
||||
DeckListHistoryManager *manager,
|
||||
QWidget *parent = nullptr);
|
||||
void setDeckListModel(DeckListModel *_deckListModel);
|
||||
|
||||
private slots:
|
||||
void refreshList();
|
||||
void onListClicked(QListWidgetItem *item);
|
||||
void onListClicked(const QListWidgetItem *item);
|
||||
void doUndo();
|
||||
void doRedo();
|
||||
|
||||
private:
|
||||
DeckListModel *deckListModel;
|
||||
DeckStateManager *deckStateManager;
|
||||
DeckListStyleProxy *styleProxy;
|
||||
DeckListHistoryManager *historyManager;
|
||||
|
||||
QHBoxLayout *layout;
|
||||
QAction *aUndo;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue