Merge branch 'master' into edhrec-tab

This commit is contained in:
Zach H 2025-01-24 22:21:13 -05:00 committed by GitHub
commit 2afc4aa3a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 354 additions and 50 deletions

View file

@ -35,6 +35,7 @@ set(cockatrice_SOURCES
src/deck/deck_list_model.cpp src/deck/deck_list_model.cpp
src/deck/deck_stats_interface.cpp src/deck/deck_stats_interface.cpp
src/dialogs/dlg_connect.cpp src/dialogs/dlg_connect.cpp
src/dialogs/dlg_convert_deck_to_cod_format.cpp
src/dialogs/dlg_create_token.cpp src/dialogs/dlg_create_token.cpp
src/dialogs/dlg_create_game.cpp src/dialogs/dlg_create_game.cpp
src/dialogs/dlg_edit_avatar.cpp src/dialogs/dlg_edit_avatar.cpp

View file

@ -4,7 +4,6 @@
#include "../../server/pending_command.h" #include "../../server/pending_command.h"
#include "../../server/remote/remote_decklist_tree_widget.h" #include "../../server/remote/remote_decklist_tree_widget.h"
#include "../../settings/cache_settings.h" #include "../../settings/cache_settings.h"
#include "../game_logic/abstract_client.h"
#include "../get_text_with_max.h" #include "../get_text_with_max.h"
#include "decklist.h" #include "decklist.h"
#include "pb/command_deck_del.pb.h" #include "pb/command_deck_del.pb.h"
@ -31,7 +30,9 @@
#include <QUrl> #include <QUrl>
#include <QVBoxLayout> #include <QVBoxLayout>
TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor, AbstractClient *_client) TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor,
AbstractClient *_client,
const ServerInfo_User *currentUserInfo)
: Tab(_tabSupervisor), client(_client) : Tab(_tabSupervisor), client(_client)
{ {
localDirModel = new QFileSystemModel(this); localDirModel = new QFileSystemModel(this);
@ -151,6 +152,10 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor, AbstractClient *_c
QWidget *mainWidget = new QWidget(this); QWidget *mainWidget = new QWidget(this);
mainWidget->setLayout(hbox); mainWidget->setLayout(hbox);
setCentralWidget(mainWidget); setCentralWidget(mainWidget);
connect(client, &AbstractClient::userInfoChanged, this, &TabDeckStorage::handleConnected);
connect(client, &AbstractClient::statusChanged, this, &TabDeckStorage::handleConnectionChanged);
setRemoteEnabled(currentUserInfo && currentUserInfo->user_level() & ServerInfo_User::IsRegistered);
} }
void TabDeckStorage::retranslateUi() void TabDeckStorage::retranslateUi()
@ -187,6 +192,36 @@ QString TabDeckStorage::getTargetPath() const
} }
} }
void TabDeckStorage::handleConnected(const ServerInfo_User &userInfo)
{
setRemoteEnabled(userInfo.user_level() & ServerInfo_User::IsRegistered);
}
/**
* This is only responsible for handling the disconnect. The connect is already handled elsewhere
*/
void TabDeckStorage::handleConnectionChanged(ClientStatus status)
{
if (status == StatusDisconnected) {
setRemoteEnabled(false);
}
}
void TabDeckStorage::setRemoteEnabled(bool enabled)
{
aUpload->setEnabled(enabled);
aOpenRemoteDeck->setEnabled(enabled);
aDownload->setEnabled(enabled);
aNewFolder->setEnabled(enabled);
aDeleteRemoteDeck->setEnabled(enabled);
if (enabled) {
serverDirView->refreshTree();
} else {
serverDirView->clearTree();
}
}
void TabDeckStorage::actLocalDoubleClick(const QModelIndex &curLeft) void TabDeckStorage::actLocalDoubleClick(const QModelIndex &curLeft)
{ {
if (!localDirModel->isDir(curLeft)) { if (!localDirModel->isDir(curLeft)) {

View file

@ -2,8 +2,10 @@
#define TAB_DECK_STORAGE_H #define TAB_DECK_STORAGE_H
#include "../../server/remote/remote_decklist_tree_widget.h" #include "../../server/remote/remote_decklist_tree_widget.h"
#include "../game_logic/abstract_client.h"
#include "tab.h" #include "tab.h"
class ServerInfo_User;
class AbstractClient; class AbstractClient;
class QTreeView; class QTreeView;
class QFileSystemModel; class QFileSystemModel;
@ -31,12 +33,17 @@ private:
QAction *aOpenRemoteDeck, *aDownload, *aNewFolder, *aDeleteRemoteDeck; QAction *aOpenRemoteDeck, *aDownload, *aNewFolder, *aDeleteRemoteDeck;
QString getTargetPath() const; QString getTargetPath() const;
void setRemoteEnabled(bool enabled);
void uploadDeck(const QString &filePath, const QString &targetPath); void uploadDeck(const QString &filePath, const QString &targetPath);
void deleteRemoteDeck(const RemoteDeckList_TreeModel::Node *node); void deleteRemoteDeck(const RemoteDeckList_TreeModel::Node *node);
void downloadNodeAtIndex(const QModelIndex &curLeft, const QModelIndex &curRight); void downloadNodeAtIndex(const QModelIndex &curLeft, const QModelIndex &curRight);
private slots: private slots:
void handleConnected(const ServerInfo_User &userInfo);
void handleConnectionChanged(ClientStatus status);
void actLocalDoubleClick(const QModelIndex &curLeft); void actLocalDoubleClick(const QModelIndex &curLeft);
void actOpenLocalDeck(); void actOpenLocalDeck();
@ -63,7 +70,7 @@ private slots:
void deleteDeckFinished(const Response &response, const CommandContainer &commandContainer); void deleteDeckFinished(const Response &response, const CommandContainer &commandContainer);
public: public:
TabDeckStorage(TabSupervisor *_tabSupervisor, AbstractClient *_client); TabDeckStorage(TabSupervisor *_tabSupervisor, AbstractClient *_client, const ServerInfo_User *currentUserInfo);
void retranslateUi() override; void retranslateUi() override;
QString getTabText() const override QString getTabText() const override
{ {

View file

@ -279,6 +279,7 @@ void TabSupervisor::initStartupTabs()
addDeckEditorTab(nullptr); addDeckEditorTab(nullptr);
checkAndTrigger(aTabVisualDeckStorage, SettingsCache::instance().getTabVisualDeckStorageOpen()); checkAndTrigger(aTabVisualDeckStorage, SettingsCache::instance().getTabVisualDeckStorageOpen());
checkAndTrigger(aTabDeckStorage, SettingsCache::instance().getTabDeckStorageOpen());
} }
/** /**
@ -334,6 +335,7 @@ void TabSupervisor::resetTabsMenu()
tabsMenu->addAction(aTabDeckEditor); tabsMenu->addAction(aTabDeckEditor);
tabsMenu->addSeparator(); tabsMenu->addSeparator();
tabsMenu->addAction(aTabVisualDeckStorage); tabsMenu->addAction(aTabVisualDeckStorage);
tabsMenu->addAction(aTabDeckStorage);
} }
void TabSupervisor::start(const ServerInfo_User &_userInfo) void TabSupervisor::start(const ServerInfo_User &_userInfo)
@ -355,10 +357,8 @@ void TabSupervisor::start(const ServerInfo_User &_userInfo)
updatePingTime(0, -1); updatePingTime(0, -1);
if (userInfo->user_level() & ServerInfo_User::IsRegistered) { if (userInfo->user_level() & ServerInfo_User::IsRegistered) {
tabsMenu->addAction(aTabDeckStorage);
tabsMenu->addAction(aTabReplays); tabsMenu->addAction(aTabReplays);
checkAndTrigger(aTabDeckStorage, SettingsCache::instance().getTabDeckStorageOpen());
checkAndTrigger(aTabReplays, SettingsCache::instance().getTabReplaysOpen()); checkAndTrigger(aTabReplays, SettingsCache::instance().getTabReplaysOpen());
} }
@ -379,7 +379,6 @@ void TabSupervisor::startLocal(const QList<AbstractClient *> &_clients)
resetTabsMenu(); resetTabsMenu();
tabAccount = nullptr; tabAccount = nullptr;
tabDeckStorage = nullptr;
tabReplays = nullptr; tabReplays = nullptr;
tabAdmin = nullptr; tabAdmin = nullptr;
tabLog = nullptr; tabLog = nullptr;
@ -416,9 +415,6 @@ void TabSupervisor::stop()
if (tabServer) { if (tabServer) {
tabServer->closeRequest(true); tabServer->closeRequest(true);
} }
if (tabDeckStorage) {
tabDeckStorage->closeRequest(true);
}
if (tabReplays) { if (tabReplays) {
tabReplays->closeRequest(true); tabReplays->closeRequest(true);
} }
@ -508,7 +504,7 @@ void TabSupervisor::actTabDeckStorage(bool checked)
{ {
SettingsCache::instance().setTabDeckStorageOpen(checked); SettingsCache::instance().setTabDeckStorageOpen(checked);
if (checked && !tabDeckStorage) { if (checked && !tabDeckStorage) {
tabDeckStorage = new TabDeckStorage(this, client); tabDeckStorage = new TabDeckStorage(this, client, userInfo);
connect(tabDeckStorage, &TabDeckStorage::openDeckEditor, this, &TabSupervisor::addDeckEditorTab); connect(tabDeckStorage, &TabDeckStorage::openDeckEditor, this, &TabSupervisor::addDeckEditorTab);
myAddTab(tabDeckStorage, aTabDeckStorage); myAddTab(tabDeckStorage, aTabDeckStorage);
connect(tabDeckStorage, &Tab::closed, this, [this] { connect(tabDeckStorage, &Tab::closed, this, [this] {

View file

@ -85,15 +85,6 @@ FlowWidget::FlowWidget(QWidget *parent,
*/ */
void FlowWidget::addWidget(QWidget *widget_to_add) const void FlowWidget::addWidget(QWidget *widget_to_add) const
{ {
// Adjust size policy if scrollbars are disabled
// if (scrollArea->horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff) {
// widget_to_add->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
//}
// if (scrollArea->verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOff) {
// widget_to_add->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
//}
// Add the widget to the flow layout
flowLayout->addWidget(widget_to_add); flowLayout->addWidget(widget_to_add);
} }
@ -145,7 +136,6 @@ void FlowWidget::resizeEvent(QResizeEvent *event)
qCDebug(FlowWidgetSizeLog) << "Got a scrollarea: " << scrollArea->widget()->size(); qCDebug(FlowWidgetSizeLog) << "Got a scrollarea: " << scrollArea->widget()->size();
scrollArea->widget()->adjustSize(); scrollArea->widget()->adjustSize();
} else { } else {
qCDebug(FlowWidgetSizeLog) << "Rawdogging the container, daddy: " << container->size();
container->adjustSize(); container->adjustSize();
} }
} }

View file

@ -1,5 +1,7 @@
#include "deck_preview_tag_addition_widget.h" #include "deck_preview_tag_addition_widget.h"
#include "../../../../../dialogs/dlg_convert_deck_to_cod_format.h"
#include "../../../../../settings/cache_settings.h"
#include "deck_preview_tag_dialog.h" #include "deck_preview_tag_dialog.h"
#include <QFontMetrics> #include <QFontMetrics>
@ -40,6 +42,44 @@ void DeckPreviewTagAdditionWidget::mousePressEvent(QMouseEvent *event)
QStringList knownTags = parent->parent->parent->gatherAllTagsFromFlowWidget(); QStringList knownTags = parent->parent->parent->gatherAllTagsFromFlowWidget();
QStringList activeTags = parent->deckLoader->getTags(); QStringList activeTags = parent->deckLoader->getTags();
bool canAddTags = true;
if (parent->deckLoader->getLastFileFormat() != DeckLoader::CockatriceFormat) {
canAddTags = false;
// Retrieve saved preference if the prompt is disabled
if (!SettingsCache::instance().getVisualDeckStoragePromptForConversion()) {
if (SettingsCache::instance().getVisualDeckStorageAlwaysConvert()) {
parent->deckLoader->convertToCockatriceFormat(parent->parent->filePath);
parent->parent->filePath = parent->deckLoader->getLastFileName();
parent->parent->refreshBannerCardText();
canAddTags = true;
}
} else {
// Show the dialog to the user
DialogConvertDeckToCodFormat conversionDialog(parent);
if (conversionDialog.exec() == QDialog::Accepted) {
parent->deckLoader->convertToCockatriceFormat(parent->parent->filePath);
parent->parent->filePath = parent->deckLoader->getLastFileName();
parent->parent->refreshBannerCardText();
canAddTags = true;
if (conversionDialog.dontAskAgain()) {
SettingsCache::instance().setVisualDeckStoragePromptForConversion(Qt::CheckState::Unchecked);
SettingsCache::instance().setVisualDeckStorageAlwaysConvert(Qt::CheckState::Checked);
}
} else {
SettingsCache::instance().setVisualDeckStorageAlwaysConvert(Qt::CheckState::Unchecked);
if (conversionDialog.dontAskAgain()) {
SettingsCache::instance().setVisualDeckStoragePromptForConversion(Qt::CheckState::Unchecked);
} else {
SettingsCache::instance().setVisualDeckStoragePromptForConversion(Qt::CheckState::Checked);
}
}
}
}
if (canAddTags) {
DeckPreviewTagDialog dialog(knownTags, activeTags); DeckPreviewTagDialog dialog(knownTags, activeTags);
if (dialog.exec() == QDialog::Accepted) { if (dialog.exec() == QDialog::Accepted) {
QStringList updatedTags = dialog.getActiveTags(); QStringList updatedTags = dialog.getActiveTags();
@ -47,6 +87,7 @@ void DeckPreviewTagAdditionWidget::mousePressEvent(QMouseEvent *event)
parent->deckLoader->saveToFile(parent->parent->filePath, DeckLoader::CockatriceFormat); parent->deckLoader->saveToFile(parent->parent->filePath, DeckLoader::CockatriceFormat);
} }
} }
}
void DeckPreviewTagAdditionWidget::paintEvent(QPaintEvent *event) void DeckPreviewTagAdditionWidget::paintEvent(QPaintEvent *event)
{ {

View file

@ -17,7 +17,7 @@ DeckPreviewWidget::DeckPreviewWidget(VisualDeckStorageWidget *_parent, const QSt
deckLoader = new DeckLoader(); deckLoader = new DeckLoader();
connect(deckLoader, &DeckLoader::loadFinished, this, &DeckPreviewWidget::initializeUi); connect(deckLoader, &DeckLoader::loadFinished, this, &DeckPreviewWidget::initializeUi);
deckLoader->loadFromFileAsync(filePath, DeckLoader::CockatriceFormat, false); deckLoader->loadFromFileAsync(filePath, DeckLoader::getFormatFromName(filePath), false);
bannerCardDisplayWidget = new DeckPreviewCardPictureWidget(this); bannerCardDisplayWidget = new DeckPreviewCardPictureWidget(this);
@ -88,6 +88,12 @@ void DeckPreviewWidget::setFilePath(const QString &_filePath)
filePath = _filePath; filePath = _filePath;
} }
void DeckPreviewWidget::refreshBannerCardText()
{
bannerCardDisplayWidget->setOverlayText(
deckLoader->getName().isEmpty() ? QFileInfo(deckLoader->getLastFileName()).fileName() : deckLoader->getName());
}
void DeckPreviewWidget::imageClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance) void DeckPreviewWidget::imageClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance)
{ {
Q_UNUSED(instance); Q_UNUSED(instance);

View file

@ -33,6 +33,7 @@ signals:
public slots: public slots:
void setFilePath(const QString &filePath); void setFilePath(const QString &filePath);
void refreshBannerCardText();
void imageClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance); void imageClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance);
void imageDoubleClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance); void imageDoubleClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance);
void initializeUi(bool deckLoadSuccess); void initializeUi(bool deckLoadSuccess);

View file

@ -6,6 +6,7 @@
#include "decklist.h" #include "decklist.h"
#include <QDebug> #include <QDebug>
#include <QDir>
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QFutureWatcher> #include <QFutureWatcher>
@ -481,6 +482,53 @@ void DeckLoader::saveToStream_DeckZoneCards(QTextStream &out,
} }
} }
bool DeckLoader::convertToCockatriceFormat(QString fileName)
{
// Change the file extension to .cod
QFileInfo fileInfo(fileName);
QString newFileName = QDir::toNativeSeparators(fileInfo.path() + "/" + fileInfo.completeBaseName() + ".cod");
// Open the new file for writing
QFile file(newFileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qCWarning(DeckLoaderLog) << "Failed to open file for writing:" << newFileName;
return false;
}
bool result = false;
// Perform file modifications based on the detected format
switch (getFormatFromName(fileName)) {
case PlainTextFormat:
// Save in Cockatrice's native format
result = saveToFile_Native(&file);
break;
case CockatriceFormat:
qCInfo(DeckLoaderLog) << "File is already in Cockatrice format. No conversion needed.";
result = true;
break;
default:
qCWarning(DeckLoaderLog) << "Unsupported file format for conversion:" << fileName;
result = false;
break;
}
file.close();
// Delete the old file if conversion was successful
if (result) {
if (!QFile::remove(fileName)) {
qCWarning(DeckLoaderLog) << "Failed to delete original file:" << fileName;
} else {
qCInfo(DeckLoaderLog) << "Original file deleted successfully:" << fileName;
}
lastFileName = newFileName;
lastFileFormat = CockatriceFormat;
}
return result;
}
QString DeckLoader::getCardZoneFromName(QString cardName, QString currentZoneName) QString DeckLoader::getCardZoneFromName(QString cardName, QString currentZoneName)
{ {
CardInfoPtr card = CardDatabaseManager::getInstance()->getCard(cardName); CardInfoPtr card = CardDatabaseManager::getInstance()->getCard(cardName);

View file

@ -59,6 +59,7 @@ public:
// overload // overload
bool saveToStream_Plain(QTextStream &out, bool addComments = true, bool addSetNameAndNumber = true); bool saveToStream_Plain(QTextStream &out, bool addComments = true, bool addSetNameAndNumber = true);
bool convertToCockatriceFormat(QString fileName);
protected: protected:
void saveToStream_DeckHeader(QTextStream &out); void saveToStream_DeckHeader(QTextStream &out);

View file

@ -0,0 +1,40 @@
#include "dlg_convert_deck_to_cod_format.h"
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QLabel>
#include <QVBoxLayout>
DialogConvertDeckToCodFormat::DialogConvertDeckToCodFormat(QWidget *parent) : QDialog(parent)
{
layout = new QVBoxLayout(this);
label = new QLabel();
layout->addWidget(label);
dontAskAgainCheckbox = new QCheckBox(this);
layout->addWidget(dontAskAgainCheckbox);
buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
layout->addWidget(buttonBox);
connect(buttonBox, &QDialogButtonBox::accepted, this, [this]() { accept(); });
connect(buttonBox, &QDialogButtonBox::rejected, this, [this]() { reject(); });
setLayout(layout);
retranslateUi();
}
void DialogConvertDeckToCodFormat::retranslateUi()
{
setWindowTitle(tr("Deck Format Conversion"));
label->setText(
tr("You tried to add a tag to a .txt format deck.\n Tags can only be added to .cod format decks.\n Do "
"you want to convert the deck to the .cod format?"));
dontAskAgainCheckbox->setText(tr("Remember and automatically apply choice in the future"));
}
bool DialogConvertDeckToCodFormat::dontAskAgain() const
{
return dontAskAgainCheckbox->isChecked();
}

View file

@ -0,0 +1,29 @@
#ifndef DIALOG_CONVERT_DECK_TO_COD_FORMAT_H
#define DIALOG_CONVERT_DECK_TO_COD_FORMAT_H
#include <QCheckBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QLabel>
#include <QVBoxLayout>
class DialogConvertDeckToCodFormat : public QDialog
{
Q_OBJECT
public:
explicit DialogConvertDeckToCodFormat(QWidget *parent);
void retranslateUi();
bool dontAskAgain() const;
private:
QVBoxLayout *layout;
QLabel *label;
QCheckBox *dontAskAgainCheckbox;
QDialogButtonBox *buttonBox;
Q_DISABLE_COPY(DialogConvertDeckToCodFormat)
};
#endif // DIALOG_CONVERT_DECK_TO_COD_FORMAT_H

View file

@ -573,6 +573,15 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
connect(&useTearOffMenusCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), connect(&useTearOffMenusCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
[](const QT_STATE_CHANGED_T state) { SettingsCache::instance().setUseTearOffMenus(state == Qt::Checked); }); [](const QT_STATE_CHANGED_T state) { SettingsCache::instance().setUseTearOffMenus(state == Qt::Checked); });
visualDeckStoragePromptForConversionCheckBox.setChecked(
SettingsCache::instance().getVisualDeckStoragePromptForConversion());
connect(&visualDeckStoragePromptForConversionCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setVisualDeckStoragePromptForConversion);
visualDeckStorageAlwaysConvertCheckBox.setChecked(SettingsCache::instance().getVisualDeckStorageAlwaysConvert());
connect(&visualDeckStorageAlwaysConvertCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setVisualDeckStorageAlwaysConvert);
auto *generalGrid = new QGridLayout; auto *generalGrid = new QGridLayout;
generalGrid->addWidget(&doubleClickToPlayCheckBox, 0, 0); generalGrid->addWidget(&doubleClickToPlayCheckBox, 0, 0);
generalGrid->addWidget(&clickPlaysAllSelectedCheckBox, 1, 0); generalGrid->addWidget(&clickPlaysAllSelectedCheckBox, 1, 0);
@ -580,6 +589,8 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage()
generalGrid->addWidget(&closeEmptyCardViewCheckBox, 3, 0); generalGrid->addWidget(&closeEmptyCardViewCheckBox, 3, 0);
generalGrid->addWidget(&annotateTokensCheckBox, 4, 0); generalGrid->addWidget(&annotateTokensCheckBox, 4, 0);
generalGrid->addWidget(&useTearOffMenusCheckBox, 5, 0); generalGrid->addWidget(&useTearOffMenusCheckBox, 5, 0);
generalGrid->addWidget(&visualDeckStoragePromptForConversionCheckBox, 6, 0);
generalGrid->addWidget(&visualDeckStorageAlwaysConvertCheckBox, 7, 0);
generalGroupBox = new QGroupBox; generalGroupBox = new QGroupBox;
generalGroupBox->setLayout(generalGrid); generalGroupBox->setLayout(generalGrid);
@ -658,6 +669,8 @@ void UserInterfaceSettingsPage::retranslateUi()
closeEmptyCardViewCheckBox.setText(tr("Close card view window when last card is removed")); closeEmptyCardViewCheckBox.setText(tr("Close card view window when last card is removed"));
annotateTokensCheckBox.setText(tr("Annotate card text on tokens")); annotateTokensCheckBox.setText(tr("Annotate card text on tokens"));
useTearOffMenusCheckBox.setText(tr("Use tear-off menus, allowing right click menus to persist on screen")); useTearOffMenusCheckBox.setText(tr("Use tear-off menus, allowing right click menus to persist on screen"));
visualDeckStoragePromptForConversionCheckBox.setText(tr("Prompt before converting .txt decks to .cod format"));
visualDeckStorageAlwaysConvertCheckBox.setText(tr("Always convert if not prompted"));
notificationsGroupBox->setTitle(tr("Notifications settings")); notificationsGroupBox->setTitle(tr("Notifications settings"));
notificationsEnabledCheckBox.setText(tr("Enable notifications in taskbar")); notificationsEnabledCheckBox.setText(tr("Enable notifications in taskbar"));
specNotificationsEnabledCheckBox.setText(tr("Notify in the taskbar for game events while you are spectating")); specNotificationsEnabledCheckBox.setText(tr("Notify in the taskbar for game events while you are spectating"));

View file

@ -142,6 +142,8 @@ private:
QCheckBox closeEmptyCardViewCheckBox; QCheckBox closeEmptyCardViewCheckBox;
QCheckBox annotateTokensCheckBox; QCheckBox annotateTokensCheckBox;
QCheckBox useTearOffMenusCheckBox; QCheckBox useTearOffMenusCheckBox;
QCheckBox visualDeckStoragePromptForConversionCheckBox;
QCheckBox visualDeckStorageAlwaysConvertCheckBox;
QCheckBox tapAnimationCheckBox; QCheckBox tapAnimationCheckBox;
QCheckBox openDeckInNewTabCheckBox; QCheckBox openDeckInNewTabCheckBox;
QLabel rewindBufferingMsLabel; QLabel rewindBufferingMsLabel;

View file

@ -141,7 +141,7 @@ void CardZone::addCard(CardItem *card, const bool reorganize, const int x, const
} }
for (auto *view : views) { for (auto *view : views) {
if (view->getIsReversed() || (x <= view->getCards().size()) || (view->getNumberCards() == -1)) { if (view->prepareAddCard(x)) {
view->addCard(new CardItem(player, nullptr, card->getName(), card->getProviderId(), card->getId()), view->addCard(new CardItem(player, nullptr, card->getName(), card->getProviderId(), card->getId()),
reorganize, x, y); reorganize, x, y);
} }
@ -173,7 +173,7 @@ CardItem *CardZone::getCard(int cardId, const QString &cardName)
return c; return c;
} }
CardItem *CardZone::takeCard(int position, int cardId, bool /*canResize*/) CardItem *CardZone::takeCard(int position, int cardId, bool toNewZone)
{ {
if (position == -1) { if (position == -1) {
// position == -1 means either that the zone is indexed by card id // position == -1 means either that the zone is indexed by card id
@ -190,7 +190,7 @@ CardItem *CardZone::takeCard(int position, int cardId, bool /*canResize*/)
return nullptr; return nullptr;
for (auto *view : views) { for (auto *view : views) {
view->removeCard(position); view->removeCard(position, toNewZone);
} }
CardItem *c = cards.takeAt(position); CardItem *c = cards.takeAt(position);

View file

@ -244,10 +244,10 @@ void TableZone::toggleTapped()
player->sendGameCommand(player->prepareGameCommand(cmdList)); player->sendGameCommand(player->prepareGameCommand(cmdList));
} }
CardItem *TableZone::takeCard(int position, int cardId, bool canResize) CardItem *TableZone::takeCard(int position, int cardId, bool toNewZone)
{ {
CardItem *result = CardZone::takeCard(position, cardId); CardItem *result = CardZone::takeCard(position, cardId);
if (canResize) if (toNewZone)
resizeToContents(); resizeToContents();
return result; return result;
} }

View file

@ -147,10 +147,10 @@ public:
@param position card position @param position card position
@param cardId id of card to take @param cardId id of card to take
@param canResize defaults to true @param toNewZone Whether the destination of the card is not the same as the starting zone. Defaults to true
@return CardItem that has been removed @return CardItem that has been removed
*/ */
CardItem *takeCard(int position, int cardId, bool canResize = true) override; CardItem *takeCard(int position, int cardId, bool toNewZone = true) override;
/** /**
Resizes the TableZone in case CardItems are within or Resizes the TableZone in case CardItems are within or

View file

@ -282,6 +282,51 @@ void ZoneViewZone::setPileView(int _pileView)
reorganizeCards(); reorganizeCards();
} }
/**
* Checks if inserting a card at the given position requires an actual new card to be created and added to the view.
* Also does any cardId updates that would be required if a card is inserted in that position.
*
* Note that this method can end up modifying the cardIds despite returning false.
* (for example, if the card is inserted into a hidden portion of the deck while the view is reversed)
*
* Make sure to call this method once before calling addCard(), so that you skip creating a new CardItem and calling
* addCard() if it's not required.
*
* @param x The position to insert the card at.
* @return Whether to proceed with calling addCard.
*/
bool ZoneViewZone::prepareAddCard(int x)
{
bool doInsert = false;
if (!isReversed) {
if (x <= cards.size() || cards.size() == -1) {
doInsert = true;
}
} else {
// map x (which is in origZone indexes) to this viewZone's cardList index
int firstId = cards.isEmpty() ? origZone->getCards().size() : cards.front()->getId();
int insertionIndex = x - firstId;
if (insertionIndex >= 0) {
// card was put into a portion of the deck that's in the view
doInsert = true;
} else {
// card was put into a portion of the deck that's not in the view; update ids but don't insert card
updateCardIds(ADD_CARD);
}
}
// autoclose check is done both here and in removeCard
if (cards.isEmpty() && !doInsert && SettingsCache::instance().getCloseEmptyCardView()) {
close();
}
return doInsert;
}
/**
* Make sure prepareAddCard() was called before calling addCard().
* This method assumes we already checked that the card is being inserted into the visible portion
*/
void ZoneViewZone::addCardImpl(CardItem *card, int x, int /*y*/) void ZoneViewZone::addCardImpl(CardItem *card, int x, int /*y*/)
{ {
if (!isReversed) { if (!isReversed) {
@ -295,15 +340,8 @@ void ZoneViewZone::addCardImpl(CardItem *card, int x, int /*y*/)
// map x (which is in origZone indexes) to this viewZone's cardList index // map x (which is in origZone indexes) to this viewZone's cardList index
int firstId = cards.isEmpty() ? origZone->getCards().size() : cards.front()->getId(); int firstId = cards.isEmpty() ? origZone->getCards().size() : cards.front()->getId();
int insertionIndex = x - firstId; int insertionIndex = x - firstId;
if (insertionIndex >= 0) {
// card was put into a portion of the deck that's in the view
// qMin to prevent out-of-bounds error when bottoming a card that is already in the view // qMin to prevent out-of-bounds error when bottoming a card that is already in the view
cards.insert(qMin(insertionIndex, cards.size()), card); cards.insert(qMin(insertionIndex, cards.size()), card);
} else {
// card was put into a portion of the deck that's not in the view
updateCardIds(ADD_CARD);
return;
}
} }
card->setParentItem(this); card->setParentItem(this);
@ -332,7 +370,7 @@ void ZoneViewZone::handleDropEvent(const QList<CardDragItem *> &dragItems,
player->sendGameCommand(cmd); player->sendGameCommand(cmd);
} }
void ZoneViewZone::removeCard(int position) void ZoneViewZone::removeCard(int position, bool toNewZone)
{ {
if (isReversed) { if (isReversed) {
position -= cards.first()->getId(); position -= cards.first()->getId();
@ -349,7 +387,11 @@ void ZoneViewZone::removeCard(int position)
CardItem *card = cards.takeAt(position); CardItem *card = cards.takeAt(position);
card->deleteLater(); card->deleteLater();
if (cards.isEmpty() && SettingsCache::instance().getCloseEmptyCardView()) { // The toNewZone check is to prevent the view from auto-closing if the view contains only a single card and that
// card gets dragged within the view.
// Another autoclose check is done in prepareAddCard so that the view autocloses if the last card was moved to an
// unrevealed portion of the same zone.
if (cards.isEmpty() && SettingsCache::instance().getCloseEmptyCardView() && toNewZone) {
close(); close();
return; return;
} }

View file

@ -70,7 +70,8 @@ public:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
void reorganizeCards() override; void reorganizeCards() override;
void initializeCards(const QList<const ServerInfo_Card *> &cardList = QList<const ServerInfo_Card *>()); void initializeCards(const QList<const ServerInfo_Card *> &cardList = QList<const ServerInfo_Card *>());
void removeCard(int position); bool prepareAddCard(int x);
void removeCard(int position, bool toNewZone);
int getNumberCards() const int getNumberCards() const
{ {
return numberCards; return numberCards;

View file

@ -270,15 +270,18 @@ void RemoteDeckList_TreeModel::refreshTree()
client->sendCommand(pend); client->sendCommand(pend);
} }
void RemoteDeckList_TreeModel::clearTree()
{
beginResetModel();
root->clearTree();
endResetModel();
}
void RemoteDeckList_TreeModel::deckListFinished(const Response &r) void RemoteDeckList_TreeModel::deckListFinished(const Response &r)
{ {
const Response_DeckList &resp = r.GetExtension(Response_DeckList::ext); const Response_DeckList &resp = r.GetExtension(Response_DeckList::ext);
beginResetModel(); clearTree();
root->clearTree();
endResetModel();
ServerInfo_DeckStorage_TreeItem tempRoot; ServerInfo_DeckStorage_TreeItem tempRoot;
tempRoot.set_id(0); tempRoot.set_id(0);
@ -361,3 +364,8 @@ void RemoteDeckList_TreeWidget::refreshTree()
{ {
treeModel->refreshTree(); treeModel->refreshTree();
} }
void RemoteDeckList_TreeWidget::clearTree()
{
treeModel->clearTree();
}

View file

@ -106,6 +106,7 @@ public:
DirectoryNode *addNamedFolderToTree(const QString &name, DirectoryNode *parent); DirectoryNode *addNamedFolderToTree(const QString &name, DirectoryNode *parent);
void removeNode(Node *node); void removeNode(Node *node);
void refreshTree(); void refreshTree();
void clearTree();
}; };
class RemoteDeckList_TreeWidget : public QTreeView class RemoteDeckList_TreeWidget : public QTreeView
@ -127,6 +128,7 @@ public:
void addFolderToTree(const QString &name, RemoteDeckList_TreeModel::DirectoryNode *parent); void addFolderToTree(const QString &name, RemoteDeckList_TreeModel::DirectoryNode *parent);
void removeNode(RemoteDeckList_TreeModel::Node *node); void removeNode(RemoteDeckList_TreeModel::Node *node);
void refreshTree(); void refreshTree();
void clearTree();
}; };
#endif #endif

View file

@ -274,6 +274,9 @@ SettingsCache::SettingsCache()
settings->value("interface/visualdeckstoragedrawunusedcoloridentities", true).toBool(); settings->value("interface/visualdeckstoragedrawunusedcoloridentities", true).toBool();
visualDeckStorageUnusedColorIdentitiesOpacity = visualDeckStorageUnusedColorIdentitiesOpacity =
settings->value("interface/visualdeckstorageunusedcoloridentitiesopacity", 15).toInt(); settings->value("interface/visualdeckstorageunusedcoloridentitiesopacity", 15).toInt();
visualDeckStoragePromptForConversion =
settings->value("interface/visualdeckstoragepromptforconversion", true).toBool();
visualDeckStorageAlwaysConvert = settings->value("interface/visualdeckstoragealwaysconvert", false).toBool();
horizontalHand = settings->value("hand/horizontal", true).toBool(); horizontalHand = settings->value("hand/horizontal", true).toBool();
invertVerticalCoordinate = settings->value("table/invert_vertical", false).toBool(); invertVerticalCoordinate = settings->value("table/invert_vertical", false).toBool();
minPlayersForMultiColumnLayout = settings->value("interface/min_players_multicolumn", 4).toInt(); minPlayersForMultiColumnLayout = settings->value("interface/min_players_multicolumn", 4).toInt();
@ -699,6 +702,18 @@ void SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity(int _visual
visualDeckStorageUnusedColorIdentitiesOpacity); visualDeckStorageUnusedColorIdentitiesOpacity);
} }
void SettingsCache::setVisualDeckStoragePromptForConversion(QT_STATE_CHANGED_T _visualDeckStoragePromptForConversion)
{
visualDeckStoragePromptForConversion = _visualDeckStoragePromptForConversion;
settings->setValue("interface/visualdeckstoragepromptforconversion", visualDeckStoragePromptForConversion);
}
void SettingsCache::setVisualDeckStorageAlwaysConvert(QT_STATE_CHANGED_T _visualDeckStorageAlwaysConvert)
{
visualDeckStorageAlwaysConvert = _visualDeckStorageAlwaysConvert;
settings->setValue("interface/visualdeckstoragealwaysconvert", visualDeckStorageAlwaysConvert);
}
void SettingsCache::setHorizontalHand(QT_STATE_CHANGED_T _horizontalHand) void SettingsCache::setHorizontalHand(QT_STATE_CHANGED_T _horizontalHand)
{ {
horizontalHand = static_cast<bool>(_horizontalHand); horizontalHand = static_cast<bool>(_horizontalHand);

View file

@ -133,6 +133,8 @@ private:
int visualDeckStorageCardSize; int visualDeckStorageCardSize;
bool visualDeckStorageDrawUnusedColorIdentities; bool visualDeckStorageDrawUnusedColorIdentities;
int visualDeckStorageUnusedColorIdentitiesOpacity; int visualDeckStorageUnusedColorIdentitiesOpacity;
bool visualDeckStoragePromptForConversion;
bool visualDeckStorageAlwaysConvert;
bool horizontalHand; bool horizontalHand;
bool invertVerticalCoordinate; bool invertVerticalCoordinate;
int minPlayersForMultiColumnLayout; int minPlayersForMultiColumnLayout;
@ -424,6 +426,14 @@ public:
{ {
return visualDeckStorageUnusedColorIdentitiesOpacity; return visualDeckStorageUnusedColorIdentitiesOpacity;
} }
bool getVisualDeckStoragePromptForConversion() const
{
return visualDeckStoragePromptForConversion;
}
bool getVisualDeckStorageAlwaysConvert() const
{
return visualDeckStorageAlwaysConvert;
}
bool getHorizontalHand() const bool getHorizontalHand() const
{ {
return horizontalHand; return horizontalHand;
@ -748,6 +758,8 @@ public slots:
void setVisualDeckStorageCardSize(int _visualDeckStorageCardSize); void setVisualDeckStorageCardSize(int _visualDeckStorageCardSize);
void setVisualDeckStorageDrawUnusedColorIdentities(QT_STATE_CHANGED_T _visualDeckStorageDrawUnusedColorIdentities); void setVisualDeckStorageDrawUnusedColorIdentities(QT_STATE_CHANGED_T _visualDeckStorageDrawUnusedColorIdentities);
void setVisualDeckStorageUnusedColorIdentitiesOpacity(int _visualDeckStorageUnusedColorIdentitiesOpacity); void setVisualDeckStorageUnusedColorIdentitiesOpacity(int _visualDeckStorageUnusedColorIdentitiesOpacity);
void setVisualDeckStoragePromptForConversion(QT_STATE_CHANGED_T _visualDeckStoragePromptForConversion);
void setVisualDeckStorageAlwaysConvert(QT_STATE_CHANGED_T _visualDeckStorageAlwaysConvert);
void setHorizontalHand(QT_STATE_CHANGED_T _horizontalHand); void setHorizontalHand(QT_STATE_CHANGED_T _horizontalHand);
void setInvertVerticalCoordinate(QT_STATE_CHANGED_T _invertVerticalCoordinate); void setInvertVerticalCoordinate(QT_STATE_CHANGED_T _invertVerticalCoordinate);
void setMinPlayersForMultiColumnLayout(int _minPlayersForMultiColumnLayout); void setMinPlayersForMultiColumnLayout(int _minPlayersForMultiColumnLayout);

View file

@ -222,6 +222,13 @@ void SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity(
int /* _visualDeckStorageUnusedColorIdentitiesOpacity */) int /* _visualDeckStorageUnusedColorIdentitiesOpacity */)
{ {
} }
void SettingsCache::setVisualDeckStoragePromptForConversion(
QT_STATE_CHANGED_T /* _visualDeckStoragePromptForConversion */)
{
}
void SettingsCache::setVisualDeckStorageAlwaysConvert(QT_STATE_CHANGED_T /* _visualDeckStorageAlwaysConvert */)
{
}
void SettingsCache::setHorizontalHand(QT_STATE_CHANGED_T /* _horizontalHand */) void SettingsCache::setHorizontalHand(QT_STATE_CHANGED_T /* _horizontalHand */)
{ {
} }

View file

@ -226,6 +226,13 @@ void SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity(
int /* _visualDeckStorageUnusedColorIdentitiesOpacity */) int /* _visualDeckStorageUnusedColorIdentitiesOpacity */)
{ {
} }
void SettingsCache::setVisualDeckStoragePromptForConversion(
QT_STATE_CHANGED_T /* _visualDeckStoragePromptForConversion */)
{
}
void SettingsCache::setVisualDeckStorageAlwaysConvert(QT_STATE_CHANGED_T /* _visualDeckStorageAlwaysConvert */)
{
}
void SettingsCache::setHorizontalHand(QT_STATE_CHANGED_T /* _horizontalHand */) void SettingsCache::setHorizontalHand(QT_STATE_CHANGED_T /* _horizontalHand */)
{ {
} }