Merge branch 'master' into edit-default-tags

This commit is contained in:
BruebachL 2025-06-13 11:57:37 +02:00 committed by GitHub
commit 297654b053
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 294 additions and 83 deletions

View file

@ -235,6 +235,7 @@ set(cockatrice_SOURCES
src/server/user/user_list_manager.cpp
src/server/user/user_list_widget.cpp
src/settings/cache_settings.cpp
src/settings/card_counter_settings.cpp
src/settings/card_database_settings.cpp
src/settings/card_override_settings.cpp
src/settings/debug_settings.cpp

View file

@ -8,6 +8,20 @@ searches are case insensitive.
<dd>[red deck wins](#red deck wins) <small>(Any deck with a display name containing the words red, deck, and wins)</small></dd>
<dd>["red deck wins"](#%22red deck wins%22) <small>(Any deck with a display name containing the exact phrase "red deck wins")</small></dd>
<dt>Deck <u>N</u>ame:</dt>
<dd>[n:aggro](#n:aggro) <small>(Any deck with a name containing the word aggro)</small></dd>
<dd>[n:red n:deck n:wins](#n:red n:deck n:wins) <small>(Any deck with a name containing the words red, deck, and wins)</small></dd>
<dd>[n:"red deck wins"](#n:%22red deck wins%22) <small>(Any deck with a name containing the exact phrase "red deck wins")</small></dd>
<dt><u>F</u>ile Name:</dt>
<dd>[f:aggro](#f:aggro) <small>(Any deck with a filename containing the word aggro)</small></dd>
<dd>[f:red f:deck f:wins](#f:red f:deck f:wins) <small>(Any deck with a filename containing the words red, deck, and wins)</small></dd>
<dd>[f:"red deck wins"](#f:%22red deck wins%22) <small>(Any deck with a filename containing the exact phrase "red deck wins")</small></dd>
<dt>Relative <u>P</u>ath (starting from the deck folder):</dt>
<dd>[p:aggro](#p:aggro) <small>(Any deck that has "aggro" somewhere in its relative path)</small></dd>
<dd>[p:edh/](#p:edh/) <small>(Any deck with "edh/" in its relative path, A.K.A. decks in the "edh" folder)</small></dd>
<dt>Deck Contents (Uses [card search expressions](#cardSearchSyntaxHelp)):</dt>
<dd><a href="#[[plains]]">[[plains]]</a> <small>(Any deck that contains at least one card with "plains" in its name)</small></dd>
<dd><a href="#[[t:legendary]]">[[t:legendary]]</a> <small>(Any deck that contains at least one legendary)</small></dd>

View file

@ -42,14 +42,6 @@ VisualDeckStorageQuickSettingsWidget::VisualDeckStorageQuickSettingsWidget(QWidg
connect(showBannerCardComboBoxCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setVisualDeckStorageShowBannerCardComboBox);
// search folder names checkbox
searchFolderNamesCheckBox = new QCheckBox(this);
searchFolderNamesCheckBox->setChecked(SettingsCache::instance().getVisualDeckStorageSearchFolderNames());
connect(searchFolderNamesCheckBox, &QCheckBox::QT_STATE_CHANGED, this,
&VisualDeckStorageQuickSettingsWidget::searchFolderNamesChanged);
connect(searchFolderNamesCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(),
&SettingsCache::setVisualDeckStorageSearchFolderNames);
// draw unused color identities checkbox
drawUnusedColorIdentitiesCheckBox = new QCheckBox(this);
drawUnusedColorIdentitiesCheckBox->setChecked(
@ -113,7 +105,6 @@ VisualDeckStorageQuickSettingsWidget::VisualDeckStorageQuickSettingsWidget(QWidg
this->addSettingsWidget(showTagFilterCheckBox);
this->addSettingsWidget(showTagsOnDeckPreviewsCheckBox);
this->addSettingsWidget(showBannerCardComboBoxCheckBox);
this->addSettingsWidget(searchFolderNamesCheckBox);
this->addSettingsWidget(drawUnusedColorIdentitiesCheckBox);
this->addSettingsWidget(unusedColorIdentityOpacityWidget);
this->addSettingsWidget(deckPreviewTooltipWidget);
@ -130,7 +121,6 @@ void VisualDeckStorageQuickSettingsWidget::retranslateUi()
showTagFilterCheckBox->setText(tr("Show Tag Filter"));
showTagsOnDeckPreviewsCheckBox->setText(tr("Show Tags On Deck Previews"));
showBannerCardComboBoxCheckBox->setText(tr("Show Banner Card Selection Option"));
searchFolderNamesCheckBox->setText(tr("Include Folder Names in Search"));
drawUnusedColorIdentitiesCheckBox->setText(tr("Draw unused Color Identities"));
unusedColorIdentitiesOpacityLabel->setText(tr("Unused Color Identities Opacity"));
unusedColorIdentitiesOpacitySpinBox->setSuffix("%");
@ -165,11 +155,6 @@ bool VisualDeckStorageQuickSettingsWidget::getShowTagsOnDeckPreviews() const
return showTagsOnDeckPreviewsCheckBox->isChecked();
}
bool VisualDeckStorageQuickSettingsWidget::getSearchFolderNames() const
{
return searchFolderNamesCheckBox->isChecked();
}
int VisualDeckStorageQuickSettingsWidget::getUnusedColorIdentitiesOpacity() const
{
return unusedColorIdentitiesOpacitySpinBox->value();

View file

@ -23,7 +23,6 @@ class VisualDeckStorageQuickSettingsWidget : public SettingsButtonWidget
QCheckBox *showBannerCardComboBoxCheckBox;
QCheckBox *showTagFilterCheckBox;
QCheckBox *showTagsOnDeckPreviewsCheckBox;
QCheckBox *searchFolderNamesCheckBox;
QLabel *unusedColorIdentitiesOpacityLabel;
QSpinBox *unusedColorIdentitiesOpacitySpinBox;
QLabel *deckPreviewTooltipLabel;
@ -50,7 +49,6 @@ public:
bool getShowBannerCardComboBox() const;
bool getShowTagFilter() const;
bool getShowTagsOnDeckPreviews() const;
bool getSearchFolderNames() const;
int getUnusedColorIdentitiesOpacity() const;
TooltipType getDeckPreviewTooltip() const;
int getCardSize() const;
@ -61,7 +59,6 @@ signals:
void showBannerCardComboBoxChanged(bool enabled);
void showTagFilterChanged(bool enabled);
void showTagsOnDeckPreviewsChanged(bool enabled);
void searchFolderNamesChanged(bool enabled);
void unusedColorIdentitiesOpacityChanged(int opacity);
void deckPreviewTooltipChanged(TooltipType tooltip);
void cardSizeChanged(int scale);

View file

@ -52,17 +52,15 @@ QString VisualDeckStorageSearchWidget::getSearchText()
}
/**
* Gets the filename used for the search.
* Converts the filepath into a relative filepath starting from the deck folder.
* If the file isn't in the deck folder, then this will just return the filename.
*
* if includeFolderName is true, then this returns the relative filepath starting from the deck folder.
* If the file isn't in the deck folder, or includeFolderName is false, then this will just return the filename.
*
* @param filePath The filePath to convert into a search name
* @param filePath The filepath to convert into a relative filepath
*/
static QString getFileSearchName(const QString &filePath, bool includeFolderName)
static QString toRelativeFilepath(const QString &filePath)
{
QString deckPath = SettingsCache::instance().getDeckPath();
if (includeFolderName && filePath.startsWith(deckPath)) {
if (filePath.startsWith(deckPath)) {
return filePath.mid(deckPath.length());
}
@ -71,14 +69,12 @@ static QString getFileSearchName(const QString &filePath, bool includeFolderName
return fileName;
}
void VisualDeckStorageSearchWidget::filterWidgets(QList<DeckPreviewWidget *> widgets,
const QString &searchText,
bool includeFolderName)
void VisualDeckStorageSearchWidget::filterWidgets(QList<DeckPreviewWidget *> widgets, const QString &searchText)
{
auto filterString = DeckFilterString(searchText);
for (auto widget : widgets) {
QString fileSearchName = getFileSearchName(widget->filePath, includeFolderName);
widget->filteredBySearch = !filterString.check(widget, {fileSearchName});
QString relativeFilePath = toRelativeFilepath(widget->filePath);
widget->filteredBySearch = !filterString.check(widget, {relativeFilePath});
}
}

View file

@ -16,7 +16,7 @@ class VisualDeckStorageSearchWidget : public QWidget
public:
explicit VisualDeckStorageSearchWidget(VisualDeckStorageWidget *parent);
QString getSearchText();
void filterWidgets(QList<DeckPreviewWidget *> widgets, const QString &searchText, bool includeFolderName);
void filterWidgets(QList<DeckPreviewWidget *> widgets, const QString &searchText);
private:
QHBoxLayout *layout;

View file

@ -46,8 +46,6 @@ VisualDeckStorageWidget::VisualDeckStorageWidget(QWidget *parent) : QWidget(pare
&VisualDeckStorageWidget::updateShowFolders);
connect(quickSettingsWidget, &VisualDeckStorageQuickSettingsWidget::showTagFilterChanged, this,
&VisualDeckStorageWidget::updateTagsVisibility);
connect(quickSettingsWidget, &VisualDeckStorageQuickSettingsWidget::searchFolderNamesChanged, this,
&VisualDeckStorageWidget::updateSearchFilter);
searchAndSortLayout->addWidget(deckPreviewColorIdentityFilterWidget);
searchAndSortLayout->addWidget(sortWidget);
@ -196,8 +194,7 @@ void VisualDeckStorageWidget::updateColorFilter()
void VisualDeckStorageWidget::updateSearchFilter()
{
if (folderWidget) {
searchWidget->filterWidgets(folderWidget->findChildren<DeckPreviewWidget *>(), searchWidget->getSearchText(),
quickSettingsWidget->getSearchFolderNames());
searchWidget->filterWidgets(folderWidget->findChildren<DeckPreviewWidget *>(), searchWidget->getSearchText());
folderWidget->updateVisibility();
}
}

View file

@ -12,13 +12,17 @@
#include "../game/cards/card_database_manager.h"
#include "../main.h"
#include "../settings/cache_settings.h"
#include "../settings/card_counter_settings.h"
#include "../settings/shortcut_treeview.h"
#include "../utility/sequence_edit.h"
#include <QAbstractButton>
#include <QAbstractListModel>
#include <QAction>
#include <QApplication>
#include <QCheckBox>
#include <QCloseEvent>
#include <QColorDialog>
#include <QComboBox>
#include <QDebug>
#include <QDesktopServices>
@ -42,7 +46,7 @@
#include <QStackedWidget>
#include <QToolBar>
#include <QTranslator>
#include <qabstractbutton.h>
#include <QVariant>
#define WIKI_CUSTOM_PIC_URL "https://github.com/Cockatrice/Cockatrice/wiki/Custom-Picture-Download-URLs"
#define WIKI_CUSTOM_SHORTCUTS "https://github.com/Cockatrice/Cockatrice/wiki/Custom-Keyboard-Shortcuts"
@ -442,6 +446,51 @@ AppearanceSettingsPage::AppearanceSettingsPage()
cardsGroupBox = new QGroupBox;
cardsGroupBox->setLayout(cardsGrid);
// Card counter colors
auto *cardCounterColorsLayout = new QGridLayout;
cardCounterColorsLayout->setColumnStretch(1, 1);
cardCounterColorsLayout->setColumnStretch(3, 1);
cardCounterColorsLayout->setColumnStretch(5, 1);
auto &cardCounterSettings = SettingsCache::instance().cardCounters();
for (int index = 0; index < 6; ++index) {
auto *pushButton = new QPushButton;
pushButton->setStyleSheet(QString("background-color: %1").arg(cardCounterSettings.color(index).name()));
connect(&SettingsCache::instance().cardCounters(), &CardCounterSettings::colorChanged, pushButton,
[index, pushButton](int changedIndex, const QColor &color) {
if (index == changedIndex) {
pushButton->setStyleSheet(QString("background-color: %1").arg(color.name()));
}
});
connect(pushButton, &QPushButton::clicked, this, [index, this]() {
auto &cardCounterSettings = SettingsCache::instance().cardCounters();
auto newColor = QColorDialog::getColor(cardCounterSettings.color(index), this);
if (!newColor.isValid())
return;
cardCounterSettings.setColor(index, newColor);
});
auto *colorName = new QLabel;
cardCounterNames.append(colorName);
int row = index / 3;
int column = 2 * (index % 3);
cardCounterColorsLayout->addWidget(pushButton, row, column);
cardCounterColorsLayout->addWidget(colorName, row, column + 1);
}
auto *cardCountersLayout = new QVBoxLayout;
cardCountersLayout->addLayout(cardCounterColorsLayout, 1);
cardCountersGroupBox = new QGroupBox;
cardCountersGroupBox->setLayout(cardCountersLayout);
// Hand layout
horizontalHandCheckBox.setChecked(settings.getHorizontalHand());
connect(&horizontalHandCheckBox, &QCheckBox::QT_STATE_CHANGED, &settings, &SettingsCache::setHorizontalHand);
@ -489,6 +538,7 @@ AppearanceSettingsPage::AppearanceSettingsPage()
mainLayout->addWidget(themeGroupBox);
mainLayout->addWidget(menuGroupBox);
mainLayout->addWidget(cardsGroupBox);
mainLayout->addWidget(cardCountersGroupBox);
mainLayout->addWidget(handGroupBox);
mainLayout->addWidget(tableGroupBox);
mainLayout->addStretch();
@ -577,6 +627,13 @@ void AppearanceSettingsPage::retranslateUi()
cardViewExpandedRowsMaxLabel.setText(tr("Maximum expanded height for card view window:"));
cardViewExpandedRowsMaxBox.setSuffix(tr(" rows"));
cardCountersGroupBox->setTitle(tr("Card counters"));
auto &cardCounterSettings = SettingsCache::instance().cardCounters();
for (int index = 0; index < cardCounterNames.size(); ++index) {
cardCounterNames[index]->setText(tr("Counter %1").arg(cardCounterSettings.displayName(index)));
}
handGroupBox->setTitle(tr("Hand layout"));
horizontalHandCheckBox.setText(tr("Display hand horizontally (wastes space)"));
leftJustifiedHandCheckBox.setText(tr("Enable left justification"));

View file

@ -125,6 +125,8 @@ private:
QGroupBox *cardsGroupBox;
QGroupBox *handGroupBox;
QGroupBox *tableGroupBox;
QGroupBox *cardCountersGroupBox;
QList<QLabel *> cardCounterNames;
QSpinBox minPlayersForMultiColumnLayoutEdit;
QSpinBox maxFontSizeForCardsEdit;

View file

@ -2,6 +2,7 @@
#include "../../client/tabs/tab_game.h"
#include "../../settings/cache_settings.h"
#include "../../settings/card_counter_settings.h"
#include "../cards/card_info.h"
#include "../game_scene.h"
#include "../player/player.h"
@ -32,6 +33,11 @@ CardItem::CardItem(Player *_owner,
ptMenu = new QMenu;
moveMenu = new QMenu;
connect(&SettingsCache::instance().cardCounters(), &CardCounterSettings::colorChanged, this, [this](int counterId) {
if (counters.contains(counterId))
update();
});
retranslateUi();
}
@ -84,6 +90,8 @@ void CardItem::retranslateUi()
void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
auto &cardCounterSettings = SettingsCache::instance().cardCounters();
painter->save();
AbstractCardItem::paint(painter, option, widget);
@ -91,8 +99,7 @@ void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QMapIterator<int, int> counterIterator(counters);
while (counterIterator.hasNext()) {
counterIterator.next();
QColor _color;
_color.setHsv(counterIterator.key() * 60, 150, 255);
QColor _color = cardCounterSettings.color(counterIterator.key());
paintNumberEllipse(counterIterator.value(), 14, _color, i, counters.size(), painter);
++i;

View file

@ -12,7 +12,7 @@ QueryPartList <- ComplexQueryPart ( ws ("AND" ws)? ComplexQueryPart)* ws*
ComplexQueryPart <- SomewhatComplexQueryPart ws "OR" ws ComplexQueryPart / SomewhatComplexQueryPart
SomewhatComplexQueryPart <- [(] QueryPartList [)] / QueryPart
QueryPart <- NotQuery / DeckContentQuery / GenericQuery
QueryPart <- NotQuery / DeckContentQuery / DeckNameQuery / FileNameQuery / PathQuery / GenericQuery
NotQuery <- ('NOT' ws/'-') SomewhatComplexQueryPart
@ -20,6 +20,10 @@ DeckContentQuery <- CardSearch NumericExpression?
CardSearch <- '[[' CardFilterString ']]'
CardFilterString <- (!']]'.)*
DeckNameQuery <- ([Dd] 'eck')? [Nn] 'ame'? [:] String
FileNameQuery <- [Ff] ('ile' 'name'?)? [:] String
PathQuery <- [Pp] 'ath'? [:] String
GenericQuery <- String
NonDoubleQuoteUnlessEscaped <- '\\\"'. / !["].
@ -129,6 +133,28 @@ static void setupParserRules()
return QString::fromStdString(std::string(sv.sv()));
};
search["DeckNameQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto name = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) {
return deck->deckLoader->getName().contains(name, Qt::CaseInsensitive);
};
};
search["FileNameQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto name = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) {
auto filename = QFileInfo(deck->filePath).fileName();
return filename.contains(name, Qt::CaseInsensitive);
};
};
search["PathQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto name = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *, const ExtraDeckSearchInfo &info) {
return info.relativeFilePath.contains(name, Qt::CaseInsensitive);
};
};
search["GenericQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto name = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) {

View file

@ -17,9 +17,9 @@ inline Q_LOGGING_CATEGORY(DeckFilterStringLog, "deck_filter_string");
struct ExtraDeckSearchInfo
{
/**
* The filename used for filtering. Varies based on settings.
* The relative filepath starting from the deck folder
*/
QString fileSearchName;
QString relativeFilePath;
};
typedef std::function<bool(const DeckPreviewWidget *, const ExtraDeckSearchInfo &)> DeckFilter;

View file

@ -8,6 +8,7 @@
#include "../../dialogs/dlg_roll_dice.h"
#include "../../main.h"
#include "../../settings/cache_settings.h"
#include "../../settings/card_counter_settings.h"
#include "../board/arrow_item.h"
#include "../board/card_item.h"
#include "../board/card_list.h"
@ -419,6 +420,8 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, T
createPredefinedTokenMenu = new QMenu(QString());
createPredefinedTokenMenu->setEnabled(false);
mCardCounters = new QMenu;
playerMenu->addSeparator();
countersMenu = playerMenu->addMenu(QString());
playerMenu->addSeparator();
@ -461,6 +464,7 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, T
mCustomZones = nullptr;
aCreateAnotherToken = nullptr;
createPredefinedTokenMenu = nullptr;
mCardCounters = nullptr;
}
aTap = new QAction(this);
@ -538,7 +542,7 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, bool _judge, T
aPlayFacedown = new QAction(this);
connect(aPlayFacedown, &QAction::triggered, this, &Player::actPlayFacedown);
for (int i = 0; i < 3; ++i) {
for (int i = 0; i < 6; ++i) {
auto *tempAddCounter = new QAction(this);
tempAddCounter->setData(9 + i * 1000);
auto *tempRemoveCounter = new QAction(this);
@ -892,19 +896,17 @@ void Player::retranslateUi()
aResetPT->setText(tr("Reset p&ower and toughness"));
aSetAnnotation->setText(tr("&Set annotation..."));
QStringList counterColors;
counterColors.append(tr("Red"));
counterColors.append(tr("Yellow"));
counterColors.append(tr("Green"));
auto &cardCounterSettings = SettingsCache::instance().cardCounters();
mCardCounters->setTitle(tr("Ca&rd counters"));
for (int i = 0; i < aAddCounter.size(); ++i) {
aAddCounter[i]->setText(tr("&Add counter (%1)").arg(counterColors[i]));
aAddCounter[i]->setText(tr("&Add counter (%1)").arg(cardCounterSettings.displayName(i)));
}
for (int i = 0; i < aRemoveCounter.size(); ++i) {
aRemoveCounter[i]->setText(tr("&Remove counter (%1)").arg(counterColors[i]));
aRemoveCounter[i]->setText(tr("&Remove counter (%1)").arg(cardCounterSettings.displayName(i)));
}
for (int i = 0; i < aSetCounter.size(); ++i) {
aSetCounter[i]->setText(tr("&Set counters (%1)...").arg(counterColors[i]));
aSetCounter[i]->setText(tr("&Set counters (%1)...").arg(cardCounterSettings.displayName(i)));
}
aMoveToTopLibrary->setText(tr("&Top of library in random order"));
@ -960,24 +962,33 @@ void Player::setShortcutsActive()
addCCShortCuts.append(shortcuts.getSingleShortcut("Player/aCCRed"));
addCCShortCuts.append(shortcuts.getSingleShortcut("Player/aCCYellow"));
addCCShortCuts.append(shortcuts.getSingleShortcut("Player/aCCGreen"));
addCCShortCuts.append(shortcuts.getSingleShortcut("Player/aCCCyan"));
addCCShortCuts.append(shortcuts.getSingleShortcut("Player/aCCPurple"));
addCCShortCuts.append(shortcuts.getSingleShortcut("Player/aCCMagenta"));
QList<QKeySequence> removeCCShortCuts;
removeCCShortCuts.append(shortcuts.getSingleShortcut("Player/aRCRed"));
removeCCShortCuts.append(shortcuts.getSingleShortcut("Player/aRCYellow"));
removeCCShortCuts.append(shortcuts.getSingleShortcut("Player/aRCGreen"));
removeCCShortCuts.append(shortcuts.getSingleShortcut("Player/aRCCyan"));
removeCCShortCuts.append(shortcuts.getSingleShortcut("Player/aRCPurple"));
removeCCShortCuts.append(shortcuts.getSingleShortcut("Player/aRCMagenta"));
QList<QKeySequence> setCCShortCuts;
setCCShortCuts.append(shortcuts.getSingleShortcut("Player/aSCRed"));
setCCShortCuts.append(shortcuts.getSingleShortcut("Player/aSCYellow"));
setCCShortCuts.append(shortcuts.getSingleShortcut("Player/aSCGreen"));
setCCShortCuts.append(shortcuts.getSingleShortcut("Player/aSCCyan"));
setCCShortCuts.append(shortcuts.getSingleShortcut("Player/aSCPurple"));
setCCShortCuts.append(shortcuts.getSingleShortcut("Player/aSCMagenta"));
for (int i = 0; i < aAddCounter.size(); ++i) {
for (int i = 0; i < addCCShortCuts.size(); ++i) {
aAddCounter[i]->setShortcut(addCCShortCuts.at(i));
}
for (int i = 0; i < aRemoveCounter.size(); ++i) {
for (int i = 0; i < removeCCShortCuts.size(); ++i) {
aRemoveCounter[i]->setShortcut(removeCCShortCuts.at(i));
}
for (int i = 0; i < aSetCounter.size(); ++i) {
for (int i = 0; i < setCCShortCuts.size(); ++i) {
aSetCounter[i]->setShortcut(setCCShortCuts.at(i));
}
@ -3944,15 +3955,18 @@ void Player::updateCardMenu(const CardItem *card)
cardMenu->addAction(aSelectAll);
cardMenu->addAction(aSelectRow);
cardMenu->addSeparator();
mCardCounters->clear();
for (int i = 0; i < aAddCounter.size(); ++i) {
cardMenu->addSeparator();
cardMenu->addAction(aAddCounter[i]);
mCardCounters->addSeparator();
mCardCounters->addAction(aAddCounter[i]);
if (card->getCounters().contains(i)) {
cardMenu->addAction(aRemoveCounter[i]);
mCardCounters->addAction(aRemoveCounter[i]);
}
cardMenu->addAction(aSetCounter[i]);
mCardCounters->addAction(aSetCounter[i]);
}
cardMenu->addSeparator();
cardMenu->addMenu(mCardCounters);
} else if (card->getZone()->getName() == "stack") {
// Card is on the stack
if (canModifyCard) {

View file

@ -253,7 +253,7 @@ public:
private:
TabGame *game;
QMenu *sbMenu, *countersMenu, *sayMenu, *createPredefinedTokenMenu, *mRevealLibrary, *mLendLibrary, *mRevealTopCard,
*mRevealHand, *mRevealRandomHandCard, *mRevealRandomGraveyardCard, *mCustomZones;
*mRevealHand, *mRevealRandomHandCard, *mRevealRandomGraveyardCard, *mCustomZones, *mCardCounters;
TearOffMenu *moveGraveMenu, *moveRfgMenu, *graveMenu, *moveHandMenu, *handMenu, *libraryMenu, *topLibraryMenu,
*bottomLibraryMenu, *rfgMenu, *playerMenu;
QList<QMenu *> playerLists;

View file

@ -6,6 +6,7 @@
#include "../game/phase.h"
#include "../game/player/player.h"
#include "../game/zones/card_zone.h"
#include "../settings/card_counter_settings.h"
#include "pb/context_move_card.pb.h"
#include "pb/context_mulligan.pb.h"
#include "pb/serverinfo_user.pb.h"
@ -613,28 +614,15 @@ void MessageLogWidget::logSetCardCounter(Player *player, QString cardName, int c
QString finalStr;
int delta = abs(oldValue - value);
if (value > oldValue) {
finalStr = tr("%1 places %2 %3 on %4 (now %5).");
finalStr = tr("%1 places %2 \"%3\" counter(s) on %4 (now %5).", "", delta);
} else {
finalStr = tr("%1 removes %2 %3 from %4 (now %5).");
}
QString colorStr;
switch (counterId) {
case 0:
colorStr = tr("red counter(s)", "", delta);
break;
case 1:
colorStr = tr("yellow counter(s)", "", delta);
break;
case 2:
colorStr = tr("green counter(s)", "", delta);
break;
default:;
finalStr = tr("%1 removes %2 \"%3\" counter(s) from %4 (now %5).", "", delta);
}
auto &cardCounterSettings = SettingsCache::instance().cardCounters();
appendHtmlServerMessage(finalStr.arg(sanitizeHtml(player->getName()))
.arg("<font class=\"blue\">" + QString::number(delta) + "</font>")
.arg(colorStr)
.arg(cardCounterSettings.displayName(counterId))
.arg(cardLink(std::move(cardName)))
.arg(value));
}

View file

@ -1,8 +1,10 @@
#include "cache_settings.h"
#include "../client/network/release_channel.h"
#include "card_counter_settings.h"
#include "card_override_settings.h"
#include <QAbstractListModel>
#include <QApplication>
#include <QDebug>
#include <QDir>
@ -179,6 +181,8 @@ SettingsCache::SettingsCache()
cardOverrideSettings = new CardOverrideSettings(settingsPath, this);
debugSettings = new DebugSettings(settingsPath, this);
cardCounterSettings = new CardCounterSettings(settingsPath, this);
if (!QFile(settingsPath + "global.ini").exists())
translateLegacySettings();
@ -1439,3 +1443,8 @@ SettingsCache &SettingsCache::instance()
{
return *settingsCache;
}
CardCounterSettings &SettingsCache::cardCounters() const
{
return *cardCounterSettings;
}

View file

@ -123,6 +123,7 @@ inline QStringList defaultTags = {
};
class QSettings;
class CardCounterSettings;
class SettingsCache : public QObject
{
@ -185,6 +186,7 @@ private:
RecentsSettings *recentsSettings;
CardOverrideSettings *cardOverrideSettings;
DebugSettings *debugSettings;
CardCounterSettings *cardCounterSettings;
QByteArray mainWindowGeometry;
QByteArray tokenDialogGeometry;
@ -870,6 +872,8 @@ public:
{
return *debugSettings;
}
CardCounterSettings &cardCounters() const;
bool getIsPortableBuild() const
{
return isPortableBuild;

View file

@ -0,0 +1,56 @@
#include "card_counter_settings.h"
#include <QColor>
#include <QSettings>
#include <QtMath>
CardCounterSettings::CardCounterSettings(const QString &settingsPath, QObject *parent)
: SettingsManager(settingsPath + "global.ini", parent)
{
}
void CardCounterSettings::setColor(int counterId, const QColor &color)
{
QString key = QString("cards/counters/%1/color").arg(counterId);
if (settings.value(key).value<QColor>() == color)
return;
settings.setValue(key, color);
emit colorChanged(counterId, color);
}
QColor CardCounterSettings::color(int counterId) const
{
QColor defaultColor;
if (counterId < 6) {
// Preserve legacy colors
defaultColor = QColor::fromHsv(counterId * 60, 150, 255);
} else {
// Future-proof support for more counters with pseudo-random colors
int h = (counterId * 37) % 360;
int s = 128 + 64 * qSin((counterId * 97) * 0.1); // 64-192
int v = 196 + 32 * qSin((counterId * 101) * 0.07); // 164-228
defaultColor = QColor::fromHsv(h, s, v);
}
return settings.value(QString("cards/counters/%1/color").arg(counterId), defaultColor).value<QColor>();
}
QString CardCounterSettings::displayName(int counterId) const
{
// Currently, card counters name are fixed to A, B, ..., Z, AA, AB, ...
auto nChars = 1 + counterId / 26;
QString str;
str.resize(nChars);
for (auto it = str.rbegin(); it != str.rend(); ++it) {
*it = QChar('A' + (counterId) % 26);
counterId /= 26;
}
return str;
}

View file

@ -0,0 +1,29 @@
#ifndef CARD_COUNTER_SETTINGS_H
#define CARD_COUNTER_SETTINGS_H
#include "settings_manager.h"
#include <QObject>
class QSettings;
class QColor;
class CardCounterSettings : public SettingsManager
{
Q_OBJECT
public:
CardCounterSettings(const QString &settingsPath, QObject *parent = nullptr);
QColor color(int counterId) const;
QString displayName(int counterId) const;
public slots:
void setColor(int counterId, const QColor &color);
signals:
void colorChanged(int counterId, const QColor &color);
};
#endif // CARD_COUNTER_SETTINGS_H

View file

@ -284,31 +284,58 @@ private:
{"DeckViewContainer/forceStartGameButton", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Force Start"),
parseSequenceString(""),
ShortcutGroup::Game_Lobby)},
{"Player/aCCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Green Counter"),
{"Player/aCCMagenta", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card Counter (F)"),
parseSequenceString(""),
ShortcutGroup::Card_Counters)},
{"Player/aRCMagenta", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Card Counter (F)"),
parseSequenceString(""),
ShortcutGroup::Card_Counters)},
{"Player/aSCMagenta", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Card Counters (F)..."),
parseSequenceString(""),
ShortcutGroup::Card_Counters)},
{"Player/aCCPurple", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card Counter (E)"),
parseSequenceString(""),
ShortcutGroup::Card_Counters)},
{"Player/aRCPurple", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Card Counter (E)"),
parseSequenceString(""),
ShortcutGroup::Card_Counters)},
{"Player/aSCPurple", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Card Counters (E)..."),
parseSequenceString(""),
ShortcutGroup::Card_Counters)},
{"Player/aCCCyan", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card Counter(D)"),
parseSequenceString(""),
ShortcutGroup::Card_Counters)},
{"Player/aRCCyan", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Card Counter (D)"),
parseSequenceString(""),
ShortcutGroup::Card_Counters)},
{"Player/aSCCyan", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Card Counters (D)..."),
parseSequenceString(""),
ShortcutGroup::Card_Counters)},
{"Player/aCCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card Counter (C)"),
parseSequenceString("Ctrl+>"),
ShortcutGroup::Card_Counters)},
{"Player/aRCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Green Counter"),
{"Player/aRCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Card Counter (C)"),
parseSequenceString("Ctrl+<"),
ShortcutGroup::Card_Counters)},
{"Player/aSCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Green Counters..."),
{"Player/aSCGreen", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Card Counters (C)..."),
parseSequenceString("Ctrl+?"),
ShortcutGroup::Card_Counters)},
{"Player/aCCYellow", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Yellow Counter"),
{"Player/aCCYellow", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card Counter (B)"),
parseSequenceString("Ctrl+."),
ShortcutGroup::Card_Counters)},
{"Player/aRCYellow", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Yellow Counter"),
{"Player/aRCYellow", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Card Counter (B)"),
parseSequenceString("Ctrl+,"),
ShortcutGroup::Card_Counters)},
{"Player/aSCYellow", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Yellow Counters..."),
{"Player/aSCYellow", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Card Counters (B)..."),
parseSequenceString("Ctrl+/"),
ShortcutGroup::Card_Counters)},
{"Player/aCCRed", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Red Counter"),
{"Player/aCCRed", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Card Counter (A)"),
parseSequenceString("Alt+."),
ShortcutGroup::Card_Counters)},
{"Player/aRCRed", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Red Counter"),
{"Player/aRCRed", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Remove Card Counter (A)"),
parseSequenceString("Alt+,"),
ShortcutGroup::Card_Counters)},
{"Player/aSCRed", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Red Counters..."),
{"Player/aSCRed", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Set Card Counters (A)..."),
parseSequenceString("Alt+/"),
ShortcutGroup::Card_Counters)},
{"Player/aInc", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Add Life Counter"),

View file

@ -56,6 +56,7 @@
<xs:element type="xs:integer" name="tablerow" minOccurs="0" maxOccurs="1" />
<xs:element type="xs:boolean" name="cipt" minOccurs="0" maxOccurs="1" />
<xs:element type="xs:boolean" name="upsidedown" minOccurs="0" maxOccurs="1" />
<xs:element type="xs:boolean" name="landscapeOrientation" minOccurs="0" maxOccurs="1" />
</xs:sequence>
</xs:complexType>
<xs:element name="cockatrice_carddatabase">

View file

@ -33,6 +33,7 @@ set(oracle_SOURCES
../cockatrice/src/game/cards/card_database_parser/cockatrice_xml_4.cpp
../cockatrice/src/settings/cache_settings.cpp
../cockatrice/src/settings/shortcuts_settings.cpp
../cockatrice/src/settings/card_counter_settings.cpp
../cockatrice/src/settings/card_database_settings.cpp
../cockatrice/src/settings/servers_settings.cpp
../cockatrice/src/settings/settings_manager.cpp