mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-13 21:40:00 -08:00
Merge pull request #8 from Continuum-Universes-Studio/feature/continuumatrice-product-migration
Feature/continuumatrice product migration
This commit is contained in:
commit
533d2f04c1
150 changed files with 5268 additions and 2563 deletions
55
cockatrice/src/interface/essence_chart_colors.h
Normal file
55
cockatrice/src/interface/essence_chart_colors.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#ifndef CONTINUUMATRICE_ESSENCE_CHART_COLORS_H
|
||||
#define CONTINUUMATRICE_ESSENCE_CHART_COLORS_H
|
||||
|
||||
#include <QColor>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QPair>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <libcockatrice/card/essence_types.h>
|
||||
|
||||
// CHART-ONLY presentation palette, NOT canon Essence colors or affiliations.
|
||||
// Each type must remain distinguishable without inventing lore. N is colorless.
|
||||
namespace EssenceCharts
|
||||
{
|
||||
inline QColor colorFor(const QString &code)
|
||||
{
|
||||
if (code == QStringLiteral("N")) {
|
||||
return QColor(145, 149, 156);
|
||||
}
|
||||
const int index = Essence::symbols().indexOf(code);
|
||||
if (index < 0) {
|
||||
// These charts are also reused for card types/categories. Retain
|
||||
// distinct readable visualization colors without assigning Essence lore.
|
||||
const QColor recognizedColor(code);
|
||||
if (recognizedColor.isValid()) {
|
||||
return recognizedColor;
|
||||
}
|
||||
return QColor::fromHsv(static_cast<int>(qHash(code) % 360u), 110, 195);
|
||||
}
|
||||
return QColor::fromHsv((index * 137) % 360, 125, 210);
|
||||
}
|
||||
|
||||
inline QList<QPair<QString, int>> ordered(const QMap<QString, int> &values)
|
||||
{
|
||||
QList<QPair<QString, int>> result;
|
||||
QSet<QString> used;
|
||||
for (const QChar code : Essence::symbols()) {
|
||||
const QString key(code);
|
||||
auto it = values.constFind(key);
|
||||
if (it != values.cend()) {
|
||||
result.append({it.key(), it.value()});
|
||||
used.insert(key);
|
||||
}
|
||||
}
|
||||
for (auto it = values.cbegin(); it != values.cend(); ++it) {
|
||||
if (!used.contains(it.key())) {
|
||||
result.append({it.key(), it.value()});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace EssenceCharts
|
||||
|
||||
#endif // CONTINUUMATRICE_ESSENCE_CHART_COLORS_H
|
||||
|
|
@ -44,7 +44,7 @@ void IntentUrlParser::handle(const QString &urlStr)
|
|||
{
|
||||
QUrl url(urlStr);
|
||||
|
||||
if (url.scheme() != "cockatrice") {
|
||||
if (url.scheme() != "continuumatrice") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ struct PendingIntentChain
|
|||
};
|
||||
|
||||
/**
|
||||
* @brief Parses cockatrice:// links and runs them as serialized intent chains.
|
||||
* @brief Parses continuumatrice:// links and runs them as serialized intent chains.
|
||||
*
|
||||
* Links are parsed by action (joingame/opendeck) and translated into an intent
|
||||
* chain. Chains are queued and run one at a time: a document can hand multiple
|
||||
|
|
|
|||
|
|
@ -488,7 +488,10 @@ const QPixmap &ManaSymbolPixmapGenerator::masterIcon(const QString &symbol)
|
|||
return it.value();
|
||||
}
|
||||
|
||||
QImageReader reader("theme:icons/mana/" + symbol);
|
||||
// Essence uses its own artwork namespace, including codes shared with MTG.
|
||||
// Theme overrides are supported through icons/essence/<symbol>.svg/png.
|
||||
const QString asset = themeManager->assetPath(QStringLiteral("icons/essence/") + symbol);
|
||||
QImageReader reader(QStringLiteral("theme:") + asset);
|
||||
QSize sourceSize = reader.size();
|
||||
if (!sourceSize.isEmpty()) {
|
||||
sourceSize.scale(QSize(MASTER_ICON_SIZE, MASTER_ICON_SIZE), Qt::KeepAspectRatio);
|
||||
|
|
|
|||
|
|
@ -294,7 +294,7 @@ QString ThemeManager::assetPath(QStringView prefix) const
|
|||
// on Windows where UAC VirtualStore can make a system dir appear writable).
|
||||
bool ThemeManager::isDirReallyWritable(const QString &dirPath)
|
||||
{
|
||||
const QString probe = QDir(dirPath).absoluteFilePath(".cockatrice_write_test");
|
||||
const QString probe = QDir(dirPath).absoluteFilePath(".continuumatrice_write_test");
|
||||
QFile f(probe);
|
||||
if (!f.open(QIODevice::WriteOnly)) {
|
||||
return false;
|
||||
|
|
@ -326,7 +326,7 @@ static QString systemThemesBasePath()
|
|||
#elif defined(Q_OS_WIN)
|
||||
base += "/themes";
|
||||
#else // linux
|
||||
base += "/../share/cockatrice/themes";
|
||||
base += "/../share/continuumatrice/themes";
|
||||
#endif
|
||||
return base;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "../../../../client/settings/cache_settings.h"
|
||||
#include "libcockatrice/card/card_info.h"
|
||||
#include <libcockatrice/card/essence_types.h>
|
||||
#include "mana_symbol_widget.h"
|
||||
|
||||
#include <QChar>
|
||||
|
|
@ -41,9 +42,9 @@ ColorIdentityWidget::ColorIdentityWidget(QWidget *parent, const CardInfoPtr &car
|
|||
|
||||
void ColorIdentityWidget::populateManaSymbolWidgets()
|
||||
{
|
||||
// Define the full WUBRG set (White, Blue, Black, Red, Green)
|
||||
QString fullColorIdentity = "WUBRG";
|
||||
QStringList symbols = parseColorIdentity(colorIdentity); // Parse mana cost string
|
||||
// Continuum Universes supports fourteen named Essence codes plus N (colorless).
|
||||
const QString &allEssences = Essence::symbols();
|
||||
QStringList symbols = parseColorIdentity(colorIdentity);
|
||||
|
||||
// clear old layout
|
||||
QtUtils::clearLayoutRec(layout);
|
||||
|
|
@ -55,7 +56,8 @@ void ColorIdentityWidget::populateManaSymbolWidgets()
|
|||
|
||||
// populate mana symbols
|
||||
if (SettingsCache::instance().visualDeckStorage().getVisualDeckStorageDrawUnusedColorIdentities()) {
|
||||
for (const QString symbol : fullColorIdentity) {
|
||||
for (const QChar code : allEssences) {
|
||||
const QString symbol(code);
|
||||
auto *manaSymbol = new ManaSymbolWidget(this, symbol, symbols.contains(symbol));
|
||||
layout->addWidget(manaSymbol);
|
||||
}
|
||||
|
|
@ -120,28 +122,11 @@ void ColorIdentityWidget::resizeEvent(QResizeEvent *event)
|
|||
}
|
||||
}
|
||||
|
||||
QStringList ColorIdentityWidget::parseColorIdentity(const QString &manaString)
|
||||
QStringList ColorIdentityWidget::parseColorIdentity(const QString &essenceIdentity)
|
||||
{
|
||||
QStringList symbols;
|
||||
|
||||
// Handle split costs (e.g., "3U // 4UU")
|
||||
QStringList splitCosts = manaString.split(" // ");
|
||||
for (const QString &part : splitCosts) {
|
||||
QRegularExpression regex(R"(\{([^}]+)\}|(\d+)|([WUBRGCSPX]))");
|
||||
QRegularExpressionMatchIterator matches = regex.globalMatch(part);
|
||||
while (matches.hasNext()) {
|
||||
QRegularExpressionMatch match = matches.next();
|
||||
if (match.captured(1).isEmpty()) { // If no `{}` group was captured, check other groups
|
||||
if (!match.captured(2).isEmpty()) {
|
||||
symbols.append(match.captured(2)); // Number match
|
||||
} else {
|
||||
symbols.append(match.captured(3)); // Single mana letter match
|
||||
}
|
||||
} else {
|
||||
symbols.append(match.captured(1)); // `{}` enclosed match
|
||||
}
|
||||
}
|
||||
for (const QChar code : Essence::normalizeIdentity(essenceIdentity)) {
|
||||
symbols.append(QString(code));
|
||||
}
|
||||
|
||||
return symbols;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "deck_color_identity.h"
|
||||
|
||||
#include "libcockatrice/card/card_info.h"
|
||||
#include <libcockatrice/card/essence_types.h>
|
||||
|
||||
#include <QChar>
|
||||
#include <QList>
|
||||
|
|
@ -17,7 +18,7 @@ QString getDeckColorIdentity(const DeckList &deck, const CardDatabaseQuerier *db
|
|||
return {};
|
||||
}
|
||||
|
||||
QSet<QChar> colorSet; // A set to collect unique color symbols (e.g., W, U, B, R, G)
|
||||
QSet<QChar> colorSet; // Unique Essence symbols (including N for colorless).
|
||||
|
||||
for (const QString &cardName : cardList) {
|
||||
CardInfoPtr currentCard = db->getCardInfo(cardName);
|
||||
|
|
@ -29,10 +30,9 @@ QString getDeckColorIdentity(const DeckList &deck, const CardDatabaseQuerier *db
|
|||
}
|
||||
}
|
||||
|
||||
// Ensure the color identity is in WUBRG order
|
||||
// Canonical Essence order is shared by the database and all widgets.
|
||||
QString colorIdentity;
|
||||
const QString wubrgOrder = "WUBRG";
|
||||
for (const QChar &color : wubrgOrder) {
|
||||
for (const QChar &color : Essence::symbols()) {
|
||||
if (colorSet.contains(color)) {
|
||||
colorIdentity.append(color);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@ class CardDatabaseQuerier;
|
|||
class DeckList;
|
||||
|
||||
/**
|
||||
* @brief Computes the color identity of a deck (e.g. "WUBRG") from the color
|
||||
* symbols of all cards in the main deck and sideboard, ordered WUBRG.
|
||||
* @brief Computes the Essence identity of a deck from the defined Essence
|
||||
* symbols (M,S,O,G,E,A,Q,C,J,B,R,L,V,T,P,N) of cards in main/sideboard.
|
||||
* N denotes colorless.
|
||||
*
|
||||
* Shared as a free function so the deck storage previews and the deck share
|
||||
* dialog compute identities identically.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,157 @@
|
|||
#include "essence_filter_picker.h"
|
||||
|
||||
#include "../../../pixel_map_generator.h"
|
||||
|
||||
#include <QFontMetrics>
|
||||
#include <QGridLayout>
|
||||
#include <QIcon>
|
||||
#include <QLabel>
|
||||
#include <QMenu>
|
||||
#include <QPushButton>
|
||||
#include <QSignalBlocker>
|
||||
#include <QSize>
|
||||
#include <QSizePolicy>
|
||||
#include <QStringList>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <QWidgetAction>
|
||||
#include <libcockatrice/card/essence_types.h>
|
||||
|
||||
EssenceFilterPicker::EssenceFilterPicker(QWidget *parent) : QToolButton(parent)
|
||||
{
|
||||
setPopupMode(QToolButton::InstantPopup);
|
||||
setToolButtonStyle(Qt::ToolButtonTextOnly); // Keep the summary visible even without a button icon.
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
// Force a stable toolbar footprint independent of popup and font sizing.
|
||||
// Modes and all 16 Essence icons are contained inside the popup.
|
||||
setFixedWidth(qMax(156, fontMetrics().horizontalAdvance(tr("Essence: Any")) + 42));
|
||||
|
||||
popupMenu = new QMenu(this);
|
||||
auto *panel = new QWidget(popupMenu);
|
||||
panelLayout = new QVBoxLayout(panel);
|
||||
panelLayout->setContentsMargins(10, 10, 10, 10);
|
||||
panelLayout->setSpacing(7);
|
||||
|
||||
auto *heading = new QLabel(tr("Filter by Essence"), panel);
|
||||
panelLayout->addWidget(heading);
|
||||
|
||||
auto *grid = new QGridLayout();
|
||||
grid->setSpacing(5);
|
||||
|
||||
int index = 0;
|
||||
for (const QChar essence : Essence::symbols()) {
|
||||
const QString symbol(essence);
|
||||
auto *button = new QToolButton(panel);
|
||||
button->setCheckable(true);
|
||||
button->setAutoRaise(false);
|
||||
button->setFixedSize(42, 42);
|
||||
button->setIcon(QIcon(ManaSymbolPixmapGenerator::generatePixmap(symbol, QSize(32, 32))));
|
||||
button->setIconSize(QSize(30, 30));
|
||||
if (essence == QChar('N')) {
|
||||
button->setToolTip(tr("N — Colorless Essence"));
|
||||
button->setAccessibleName(tr("Colorless Essence N"));
|
||||
} else if (essence == QChar('J')) {
|
||||
button->setToolTip(tr("J — Jasper Essence"));
|
||||
button->setAccessibleName(tr("Jasper Essence J"));
|
||||
} else {
|
||||
button->setToolTip(tr("Essence %1").arg(symbol));
|
||||
button->setAccessibleName(tr("Essence %1").arg(symbol));
|
||||
}
|
||||
button->setProperty("essenceCode", symbol);
|
||||
connect(button, &QToolButton::toggled, this, [this, essence](bool checked) {
|
||||
updateSummary();
|
||||
emit essenceToggled(essence, checked);
|
||||
});
|
||||
|
||||
// Sixteen Essences form a compact 4x4 grid.
|
||||
grid->addWidget(button, index / 4, index % 4);
|
||||
buttons.insert(essence, button);
|
||||
++index;
|
||||
}
|
||||
panelLayout->addLayout(grid);
|
||||
|
||||
auto *clearButton = new QPushButton(tr("Clear Essence selection"), panel);
|
||||
connect(clearButton, &QPushButton::clicked, this, [this]() {
|
||||
// Clear as ONE model operation, so Exact Match and Include/Exclude
|
||||
// don't retain stale "not this Essence" predicates.
|
||||
for (const QChar essence : Essence::symbols()) {
|
||||
if (auto *button = buttons.value(essence); button && button->isChecked()) {
|
||||
const QSignalBlocker blocker(button);
|
||||
button->setChecked(false);
|
||||
}
|
||||
}
|
||||
updateSummary();
|
||||
emit selectionCleared();
|
||||
});
|
||||
panelLayout->addWidget(clearButton);
|
||||
|
||||
auto *panelAction = new QWidgetAction(popupMenu);
|
||||
panelAction->setDefaultWidget(panel);
|
||||
popupMenu->addAction(panelAction);
|
||||
setMenu(popupMenu);
|
||||
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void EssenceFilterPicker::addPopupControl(QWidget *control)
|
||||
{
|
||||
if (!control || !panelLayout) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert above the Clear button, keeping the entire configuration in one
|
||||
// popup rather than competing for horizontal space in the main toolbar.
|
||||
panelLayout->insertWidget(panelLayout->count() - 1, control);
|
||||
}
|
||||
|
||||
QSet<QChar> EssenceFilterPicker::activeEssences() const
|
||||
{
|
||||
QSet<QChar> active;
|
||||
for (auto it = buttons.cbegin(); it != buttons.cend(); ++it) {
|
||||
if (it.value()->isChecked()) {
|
||||
active.insert(it.key());
|
||||
}
|
||||
}
|
||||
return active;
|
||||
}
|
||||
|
||||
void EssenceFilterPicker::setEssenceActive(QChar essence, bool active)
|
||||
{
|
||||
auto *button = buttons.value(essence);
|
||||
if (!button || button->isChecked() == active) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Restoring external filter state must not rewrite the filter model.
|
||||
const QSignalBlocker blocker(button);
|
||||
button->setChecked(active);
|
||||
updateSummary();
|
||||
}
|
||||
|
||||
void EssenceFilterPicker::retranslateUi()
|
||||
{
|
||||
updateSummary();
|
||||
}
|
||||
|
||||
void EssenceFilterPicker::updateSummary()
|
||||
{
|
||||
QStringList selected;
|
||||
const QSet<QChar> active = activeEssences();
|
||||
for (const QChar essence : Essence::symbols()) {
|
||||
if (active.contains(essence)) {
|
||||
selected.append(QString(essence));
|
||||
}
|
||||
}
|
||||
|
||||
if (selected.isEmpty()) {
|
||||
setText(tr("Essence: Any"));
|
||||
setToolTip(tr("Filter by Essence (none selected)"));
|
||||
} else if (selected.size() == 1) {
|
||||
setText(tr("Essence: %1").arg(selected.first()));
|
||||
setToolTip(tr("Selected Essence: %1").arg(selected.first()));
|
||||
} else {
|
||||
// Keep the toolbar label short; the tooltip lists the exact types.
|
||||
setText(tr("Essence (%1)").arg(selected.size()));
|
||||
setToolTip(tr("Selected Essence: %1").arg(selected.join(QStringLiteral(", "))));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
#ifndef CONTINUUMATRICE_ESSENCE_FILTER_PICKER_H
|
||||
#define CONTINUUMATRICE_ESSENCE_FILTER_PICKER_H
|
||||
|
||||
#include <QChar>
|
||||
#include <QHash>
|
||||
#include <QSet>
|
||||
#include <QToolButton>
|
||||
|
||||
class QMenu;
|
||||
class QVBoxLayout;
|
||||
class QWidget;
|
||||
|
||||
/**
|
||||
* Compact, reusable multi-select Essence picker for the deck and card database
|
||||
* toolbars. The menu contains all fifteen Essence symbols in a 5x3 icon grid.
|
||||
* Clicking a symbol does not close the popup; programmatic synchronization
|
||||
* does not emit a user-selection signal.
|
||||
*/
|
||||
class EssenceFilterPicker final : public QToolButton
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit EssenceFilterPicker(QWidget *parent = nullptr);
|
||||
|
||||
[[nodiscard]] QSet<QChar> activeEssences() const;
|
||||
void setEssenceActive(QChar essence, bool active);
|
||||
// Put the host's filtering mode selector inside this popup too:
|
||||
// neither toolbar should require a separate cramped control.
|
||||
void addPopupControl(QWidget *control);
|
||||
void retranslateUi();
|
||||
|
||||
signals:
|
||||
void essenceToggled(QChar essence, bool active);
|
||||
// A clear request removes ALL Essence filters, including exclusions,
|
||||
// rather than creating fifteen negative filters in Exact/Exclude mode.
|
||||
void selectionCleared();
|
||||
|
||||
private:
|
||||
void updateSummary();
|
||||
|
||||
QHash<QChar, QToolButton *> buttons;
|
||||
QMenu *popupMenu = nullptr;
|
||||
QVBoxLayout *panelLayout = nullptr;
|
||||
};
|
||||
|
||||
#endif // CONTINUUMATRICE_ESSENCE_FILTER_PICKER_H
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
#include "mana_cost_widget.h"
|
||||
|
||||
#include "libcockatrice/card/card_info.h"
|
||||
#include <libcockatrice/card/essence_types.h>
|
||||
#include "mana_symbol_widget.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
|
|
@ -51,28 +52,7 @@ void ManaCostWidget::resizeEvent(QResizeEvent *event)
|
|||
}
|
||||
}
|
||||
|
||||
QStringList ManaCostWidget::parseManaCost(const QString &cmc)
|
||||
QStringList ManaCostWidget::parseManaCost(const QString &essenceCost)
|
||||
{
|
||||
QStringList symbols;
|
||||
|
||||
// Handle split costs (e.g., "3U // 4UU")
|
||||
QStringList splitCosts = cmc.split(" // ");
|
||||
for (const QString &part : splitCosts) {
|
||||
QRegularExpression regex(R"(\{([^}]+)\}|(\d+)|([WUBRGCSPX]))");
|
||||
QRegularExpressionMatchIterator matches = regex.globalMatch(part);
|
||||
while (matches.hasNext()) {
|
||||
QRegularExpressionMatch match = matches.next();
|
||||
if (match.captured(1).isEmpty()) { // If no `{}` group was captured, check other groups
|
||||
if (!match.captured(2).isEmpty()) {
|
||||
symbols.append(match.captured(2)); // Number match
|
||||
} else {
|
||||
symbols.append(match.captured(3)); // Single mana letter match
|
||||
}
|
||||
} else {
|
||||
symbols.append(match.captured(1)); // `{}` enclosed match
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return symbols;
|
||||
return Essence::parseCost(essenceCost);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,14 +57,14 @@ ManaBaseConfigDialog::ManaBaseConfigDialog(DeckListStatisticsAnalyzer *analyzer,
|
|||
|
||||
void ManaBaseConfigDialog::retranslateUi()
|
||||
{
|
||||
setWindowTitle(tr("Mana Base Configuration"));
|
||||
setWindowTitle(tr("Essence Production Configuration"));
|
||||
|
||||
displayTypeLabel->setText(tr("Display type:"));
|
||||
|
||||
displayType->clear();
|
||||
displayType->addItems({tr("pie"), tr("bar"), tr("combinedBar")});
|
||||
|
||||
filterLabel->setText(tr("Filter Colors (optional):"));
|
||||
filterLabel->setText(tr("Filter Essence types (optional):"));
|
||||
|
||||
buttons->button(QDialogButtonBox::Ok)->setText(tr("OK"));
|
||||
buttons->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "mana_base_widget.h"
|
||||
|
||||
#include "../../../../essence_chart_colors.h"
|
||||
#include "../../../general/display/charts/bars/bar_widget.h"
|
||||
#include "../../../general/display/charts/bars/color_bar.h"
|
||||
#include "../../../general/display/charts/pies/color_pie.h"
|
||||
|
|
@ -30,7 +31,7 @@ namespace
|
|||
{
|
||||
|
||||
AnalyticsPanelWidgetRegistrar registerManaBase{
|
||||
"manaBase", ManaBaseWidget::tr("Mana Base"),
|
||||
"manaBase", ManaBaseWidget::tr("Essence Production"),
|
||||
[](QWidget *parent, DeckListStatisticsAnalyzer *analyzer) { return new ManaBaseWidget(parent, analyzer); }};
|
||||
|
||||
} // anonymous namespace
|
||||
|
|
@ -83,16 +84,12 @@ void ManaBaseWidget::updateDisplay()
|
|||
|
||||
// Choose display mode
|
||||
if (config.displayType == "bar") {
|
||||
const QList<QPair<QString, int>> sortedColors = GameSpecificColors::MTG::sortManaMapWUBRGCFirst(mapSorted);
|
||||
static const QHash<QString, QColor> colorMap = {
|
||||
{"W", QColor(248, 231, 185)}, {"U", QColor(14, 104, 171)}, {"B", QColor(21, 11, 0)},
|
||||
{"R", QColor(211, 32, 42)}, {"G", QColor(0, 115, 62)}, {"C", QColor(150, 150, 150)},
|
||||
};
|
||||
const QList<QPair<QString, int>> sortedColors = EssenceCharts::ordered(mapSorted);
|
||||
|
||||
for (const auto &[color, count] : sortedColors) {
|
||||
QString label = QString("%1 %2 (%3)").arg(color).arg(count).arg(cardCount.value(color));
|
||||
|
||||
BarWidget *bar = new BarWidget(label, count, highest, colorMap.value(color, Qt::gray), this);
|
||||
BarWidget *bar = new BarWidget(label, count, highest, EssenceCharts::colorFor(color), this);
|
||||
|
||||
barLayout->addWidget(bar);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include "mana_curve_category_widget.h"
|
||||
|
||||
#include "../../../general/display/charts/bars/bar_chart_widget.h"
|
||||
#include "libcockatrice/utility/color.h"
|
||||
#include "../../../../essence_chart_colors.h"
|
||||
#include "libcockatrice/utility/qt_utils.h"
|
||||
#include "mana_curve_config.h"
|
||||
|
||||
|
|
@ -110,7 +110,7 @@ void ManaCurveCategoryWidget::updateDisplay(int minCmc,
|
|||
|
||||
QVector<BarSegment> segments;
|
||||
if (val > 0) {
|
||||
segments.push_back({cat, val, cards, GameSpecificColors::MTG::colorHelper(cat)});
|
||||
segments.push_back({cat, val, cards, EssenceCharts::colorFor(cat)});
|
||||
}
|
||||
|
||||
catBars.push_back({QString::number(cmc), segments});
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ void ManaCurveConfigDialog::retranslateUi()
|
|||
{
|
||||
labelGroupBy->setText(tr("Group By:"));
|
||||
groupBy->setItemText(0, tr("type"));
|
||||
groupBy->setItemText(1, tr("color"));
|
||||
groupBy->setItemText(1, tr("Essence"));
|
||||
groupBy->setItemText(2, tr("subtype"));
|
||||
groupBy->setItemText(3, tr("power"));
|
||||
groupBy->setItemText(4, tr("toughness"));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include "mana_curve_total_widget.h"
|
||||
|
||||
#include "../../../general/display/charts/bars/bar_chart_widget.h"
|
||||
#include "libcockatrice/utility/color.h"
|
||||
#include "../../../../essence_chart_colors.h"
|
||||
#include "mana_curve_config.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
|
|
@ -71,7 +71,7 @@ void ManaCurveTotalWidget::updateDisplay(const QString &categoryName,
|
|||
cards = catIt->value(cmc);
|
||||
}
|
||||
|
||||
segments.push_back({category, value, cards, GameSpecificColors::MTG::colorHelper(category)});
|
||||
segments.push_back({category, value, cards, EssenceCharts::colorFor(category)});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ namespace
|
|||
{
|
||||
|
||||
AnalyticsPanelWidgetRegistrar registerManaCurve{
|
||||
"manaCurve", ManaCurveWidget::tr("Mana Curve"),
|
||||
"manaCurve", ManaCurveWidget::tr("Essence Curve"),
|
||||
[](QWidget *parent, DeckListStatisticsAnalyzer *analyzer) { return new ManaCurveWidget(parent, analyzer); }};
|
||||
|
||||
} // anonymous namespace
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ void ManaDevotionConfigDialog::retranslateUi()
|
|||
displayType->setItemText(1, tr("bar"));
|
||||
displayType->setItemText(2, tr("combinedBar"));
|
||||
|
||||
labelFilters->setText(tr("Filter Colors (optional):"));
|
||||
labelFilters->setText(tr("Filter Essence types (optional):"));
|
||||
}
|
||||
|
||||
void ManaDevotionConfigDialog::accept()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "mana_devotion_widget.h"
|
||||
|
||||
#include "../../../../essence_chart_colors.h"
|
||||
#include "../../../general/display/charts/bars/bar_widget.h"
|
||||
#include "../../../general/display/charts/bars/color_bar.h"
|
||||
#include "../../../general/display/charts/pies/color_pie.h"
|
||||
|
|
@ -29,7 +30,7 @@ namespace
|
|||
{
|
||||
|
||||
AnalyticsPanelWidgetRegistrar registerManaDevotion{
|
||||
"manaDevotion", ManaDevotionWidget::tr("Mana Devotion"),
|
||||
"manaDevotion", ManaDevotionWidget::tr("Essence Cost Symbols"),
|
||||
[](QWidget *parent, DeckListStatisticsAnalyzer *analyzer) { return new ManaDevotionWidget(parent, analyzer); }};
|
||||
|
||||
} // anonymous namespace
|
||||
|
|
@ -82,18 +83,13 @@ void ManaDevotionWidget::updateDisplay()
|
|||
mapSorted.insert(QString(it.key()), it.value());
|
||||
}
|
||||
|
||||
// Color map
|
||||
QHash<QChar, QColor> colors = {{'W', QColor(248, 231, 185)}, {'U', QColor(14, 104, 171)},
|
||||
{'B', QColor(21, 11, 0)}, {'R', QColor(211, 32, 42)},
|
||||
{'G', QColor(0, 115, 62)}, {'C', QColor(150, 150, 150)}};
|
||||
|
||||
// Choose display mode
|
||||
if (config.displayType == "bar") {
|
||||
// One BarWidget per devotion color
|
||||
for (auto c : devoMap.keys()) {
|
||||
QString label = QString("%1 %2 (%3)").arg(c).arg(devoMap[c]).arg(cardCount.value(QString(c)));
|
||||
|
||||
BarWidget *bar = new BarWidget(label, devoMap[c], highest, colors.value(c, Qt::gray), this);
|
||||
BarWidget *bar = new BarWidget(label, devoMap[c], highest, EssenceCharts::colorFor(QString(c)), this);
|
||||
|
||||
barLayout->addWidget(bar);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,18 @@
|
|||
#include <QListWidgetItem>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <libcockatrice/card/essence_types.h>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
class QWidget;
|
||||
|
||||
static const QStringList kColors = {"W", "U", "B", "R", "G", "C"};
|
||||
static const QStringList kEssences = [] {
|
||||
QStringList codes;
|
||||
for (const QChar symbol : Essence::symbols()) {
|
||||
codes.append(QString(symbol));
|
||||
}
|
||||
return codes;
|
||||
}();
|
||||
|
||||
ManaDistributionConfigDialog::ManaDistributionConfigDialog(DeckListStatisticsAnalyzer *analyzer, QWidget *parent)
|
||||
: QDialog(parent), analyzer(analyzer)
|
||||
|
|
@ -33,7 +40,7 @@ ManaDistributionConfigDialog::ManaDistributionConfigDialog(DeckListStatisticsAna
|
|||
|
||||
filterList = new QListWidget(this);
|
||||
filterList->setSelectionMode(QAbstractItemView::MultiSelection);
|
||||
filterList->addItems(kColors); // dynamic/fixed, no translation needed
|
||||
filterList->addItems(kEssences); // dynamic/fixed, no translation needed
|
||||
lay->addWidget(filterList);
|
||||
|
||||
showColorRows = new QCheckBox(this);
|
||||
|
|
@ -56,9 +63,9 @@ void ManaDistributionConfigDialog::retranslateUi()
|
|||
displayType->setItemText(0, tr("pie"));
|
||||
displayType->setItemText(1, tr("bar"));
|
||||
|
||||
labelFilters->setText(tr("Colors:"));
|
||||
labelFilters->setText(tr("Essence types:"));
|
||||
|
||||
showColorRows->setText(tr("Show per-color rows"));
|
||||
showColorRows->setText(tr("Show per-Essence rows"));
|
||||
|
||||
// QDialogButtonBox buttons are automatically translated
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,5 +48,5 @@ void ManaDistributionSingleDisplayWidget::setDevotion(int pips, int cards, int p
|
|||
void ManaDistributionSingleDisplayWidget::setProduction(int pips, int cards, int percent)
|
||||
{
|
||||
productionBar->setValue(percent);
|
||||
productionLabel->setText(QString(tr("%1 mana (%2 cards)")).arg(pips).arg(cards));
|
||||
productionLabel->setText(QString(tr("%1 Essence (%2 cards)")).arg(pips).arg(cards));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,18 +14,25 @@
|
|||
#include <QList>
|
||||
#include <QSharedPointer>
|
||||
#include <QStringList>
|
||||
#include <libcockatrice/card/essence_types.h>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
namespace
|
||||
{
|
||||
AnalyticsPanelWidgetRegistrar registerManaDistribution{
|
||||
"manaProdDevotion", ManaDistributionWidget::tr("Mana Production + Devotion"),
|
||||
"manaProdDevotion", ManaDistributionWidget::tr("Essence Production + Costs"),
|
||||
[](QWidget *parent, DeckListStatisticsAnalyzer *analyzer) { return new ManaDistributionWidget(parent, analyzer); }};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
static const QStringList kColors = {"W", "U", "B", "R", "G", "C"};
|
||||
static const QStringList kEssences = [] {
|
||||
QStringList codes;
|
||||
for (const QChar symbol : Essence::symbols()) {
|
||||
codes.append(QString(symbol));
|
||||
}
|
||||
return codes;
|
||||
}();
|
||||
|
||||
ManaDistributionWidget::ManaDistributionWidget(QWidget *parent, DeckListStatisticsAnalyzer *analyzer)
|
||||
: AbstractAnalyticsPanelWidget(parent, analyzer)
|
||||
|
|
@ -49,7 +56,7 @@ ManaDistributionWidget::ManaDistributionWidget(QWidget *parent, DeckListStatisti
|
|||
row = new QHBoxLayout();
|
||||
containerLayout->addLayout(row);
|
||||
|
||||
for (const QString &c : kColors) {
|
||||
for (const QString &c : kEssences) {
|
||||
auto *w = new ManaDistributionSingleDisplayWidget(c, this);
|
||||
row->addWidget(w);
|
||||
rows[c] = w;
|
||||
|
|
@ -65,7 +72,7 @@ void ManaDistributionWidget::updateDisplay()
|
|||
const auto &prodPips = analyzer->getProductionPipCount();
|
||||
const auto &prodCards = analyzer->getProductionCardCount();
|
||||
|
||||
QStringList filtered = config.filters.isEmpty() ? kColors : config.filters;
|
||||
QStringList filtered = config.filters.isEmpty() ? kEssences : config.filters;
|
||||
|
||||
QMap<QString, int> devMap, prodMap;
|
||||
for (const QString &c : filtered) {
|
||||
|
|
@ -89,7 +96,7 @@ void ManaDistributionWidget::updateDisplay()
|
|||
productionBarTop->setColors(prodMap);
|
||||
}
|
||||
|
||||
for (const QString &c : kColors) {
|
||||
for (const QString &c : kEssences) {
|
||||
auto *w = rows.value(c);
|
||||
|
||||
if (!w) {
|
||||
|
|
@ -126,7 +133,7 @@ void ManaDistributionWidget::updateDisplay()
|
|||
QDialog *ManaDistributionWidget::createConfigDialog(QWidget *parent)
|
||||
{
|
||||
auto *dlg = new ManaDistributionConfigDialog(analyzer, parent);
|
||||
dlg->setWindowTitle(tr("Mana Distribution Settings"));
|
||||
dlg->setWindowTitle(tr("Essence Distribution Settings"));
|
||||
dlg->setFromConfig(config);
|
||||
|
||||
connect(dlg, &QDialog::accepted, [this, dlg]() {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "deck_list_model.h"
|
||||
#include "libcockatrice/card/card_info.h"
|
||||
#include <libcockatrice/card/essence_types.h>
|
||||
#include "libcockatrice/card/database/card_database_querier.h"
|
||||
#include "libcockatrice/deck_list/tree/deck_list_card_node.h"
|
||||
#include "libcockatrice/deck_list/tree/inner_deck_list_node.h"
|
||||
|
|
@ -127,68 +128,39 @@ void DeckListStatisticsAnalyzer::analyze()
|
|||
|
||||
QHash<QString, int> DeckListStatisticsAnalyzer::determineManaProduction(const QString &rulesText)
|
||||
{
|
||||
QHash<QString, int> manaCounts = {{"W", 0}, {"U", 0}, {"B", 0}, {"R", 0}, {"G", 0}, {"C", 0}};
|
||||
|
||||
QString text = rulesText.toLower(); // Normalize case for matching
|
||||
|
||||
// Quick keyword-based checks for any color and colorless mana
|
||||
if (text.contains("{t}: add one mana of any color") || text.contains("add one mana of any color")) {
|
||||
for (const auto &color : {QStringLiteral("W"), QStringLiteral("U"), QStringLiteral("B"), QStringLiteral("R"),
|
||||
QStringLiteral("G")}) {
|
||||
manaCounts[color]++;
|
||||
}
|
||||
}
|
||||
if (text.contains("{t}: add {c}") || text.contains("add one colorless mana")) {
|
||||
manaCounts["C"]++;
|
||||
QHash<QString, int> result;
|
||||
for (const QChar code : Essence::symbols()) {
|
||||
result.insert(QString(code), 0);
|
||||
}
|
||||
|
||||
// Optimized regex for specific mana symbols
|
||||
static const QRegularExpression specificColorRegex(R"(\{T\}:\s*Add\s*\{([WUBRG])\})");
|
||||
QRegularExpressionMatch match = specificColorRegex.match(rulesText);
|
||||
if (match.hasMatch()) {
|
||||
manaCounts[match.captured(1)]++;
|
||||
}
|
||||
// Explicit Essence grants only. Inferring additional resources from MTG
|
||||
// tap/mana rules would invent Continuum Universes gameplay semantics.
|
||||
// E.g. "Add {M}" / "add {N}" both work.
|
||||
static const QRegularExpression grantedEssence(
|
||||
QStringLiteral(R"(\badd\s*\{([MSOGEAQCJBRLVTPN])\})"),
|
||||
QRegularExpression::CaseInsensitiveOption);
|
||||
|
||||
return manaCounts;
|
||||
auto matches = grantedEssence.globalMatch(rulesText);
|
||||
while (matches.hasNext()) {
|
||||
const QString code = matches.next().captured(1).toUpper();
|
||||
result[code]++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unordered_map<char, int> DeckListStatisticsAnalyzer::countManaSymbols(const QString &manaString)
|
||||
std::unordered_map<char, int> DeckListStatisticsAnalyzer::countManaSymbols(const QString &essenceCost)
|
||||
{
|
||||
std::unordered_map<char, int> manaCounts = {{'W', 0}, {'U', 0}, {'B', 0}, {'R', 0}, {'G', 0}};
|
||||
|
||||
int len = manaString.length();
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (manaString[i] == '{') {
|
||||
++i; // Move past '{'
|
||||
if (i < len && manaCounts.find(manaString[i].toLatin1()) != manaCounts.end()) {
|
||||
char mana1 = manaString[i].toLatin1();
|
||||
++i; // Move to next character
|
||||
if (i < len && manaString[i] == '/') {
|
||||
++i; // Move past '/'
|
||||
if (i < len && manaCounts.find(manaString[i].toLatin1()) != manaCounts.end()) {
|
||||
char mana2 = manaString[i].toLatin1();
|
||||
manaCounts[mana1]++;
|
||||
manaCounts[mana2]++;
|
||||
} else {
|
||||
// Handle cases like "{W/}" where second part is invalid
|
||||
manaCounts[mana1]++;
|
||||
}
|
||||
} else {
|
||||
manaCounts[mana1]++;
|
||||
}
|
||||
}
|
||||
// Ensure we always skip to the closing '}'
|
||||
while (i < len && manaString[i] != '}') {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
// Check if the character is a standalone mana symbol (not inside {})
|
||||
else if (manaCounts.find(manaString[i].toLatin1()) != manaCounts.end()) {
|
||||
manaCounts[manaString[i].toLatin1()]++;
|
||||
}
|
||||
std::unordered_map<char, int> result;
|
||||
for (const QChar code : Essence::symbols()) {
|
||||
result.emplace(code.toLatin1(), 0);
|
||||
}
|
||||
|
||||
return manaCounts;
|
||||
for (const QString &token : Essence::parseCost(essenceCost)) {
|
||||
if (token.size() == 1 && Essence::isSymbol(token.at(0))) {
|
||||
result[token.at(0).toLatin1()]++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Hypergeometric probability: P(X=k)
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ private:
|
|||
std::unordered_map<int, int> manaCurveMap;
|
||||
std::unordered_map<char, int> manaDevotionMap;
|
||||
|
||||
QHash<QString, int> devotionPipCount; // W/U/B/R/G total symbols
|
||||
QHash<QString, int> devotionPipCount; // Counts for the fifteen Continuum Universes Essence codes
|
||||
QHash<QString, int> devotionCardCount; // how many cards provide devotion
|
||||
|
||||
QHash<QString, int> productionPipCount; // mana produced by cards
|
||||
|
|
|
|||
|
|
@ -36,9 +36,9 @@
|
|||
|
||||
// Executable that, when it sits next to the downloaded update installer, is installed instead of
|
||||
// it. A packager shipping a custom build - or someone testing one - can drop the file there and
|
||||
// have Cockatrice run it rather than the official installer, without the release channel having to
|
||||
// have Continuumatrice run it rather than the official installer, without the release channel having to
|
||||
// host an installer for that build. It is a drop-in and is run with the same arguments.
|
||||
static const QString UPDATE_INSTALLER_OVERRIDE = "Cockatrice-Update-Override.exe";
|
||||
static const QString UPDATE_INSTALLER_OVERRIDE = "Continuumatrice-Update-Override.exe";
|
||||
|
||||
DlgUpdate::DlgUpdate(QWidget *parent) : QDialog(parent)
|
||||
{
|
||||
|
|
@ -87,7 +87,7 @@ DlgUpdate::DlgUpdate(QWidget *parent) : QDialog(parent)
|
|||
if (!QSslSocket::supportsSsl()) {
|
||||
enableUpdateButton(false);
|
||||
QMessageBox::critical(this, tr("Error"),
|
||||
tr("Cockatrice was not built with SSL support, therefore you cannot download updates "
|
||||
tr("Continuumatrice was not built with SSL support, therefore you cannot download updates "
|
||||
"automatically! \nPlease visit the download page to update manually."));
|
||||
}
|
||||
|
||||
|
|
@ -161,7 +161,7 @@ void DlgUpdate::finishedUpdateCheck(bool needToUpdate, bool isCompatible, Releas
|
|||
// downloader themselves if there's a compatible build
|
||||
QMessageBox::information(
|
||||
window(), tr("No Update Available"),
|
||||
tr("Cockatrice is up to date!") + "<br><br>" +
|
||||
tr("Continuumatrice is up to date!") + "<br><br>" +
|
||||
tr("You are already running the latest version available in the chosen release channel.") + "<br>" +
|
||||
"<b>" + tr("Current version") + QString(":</b> %1<br>").arg(VERSION_STRING) + "<b>" +
|
||||
tr("Selected release channel") +
|
||||
|
|
@ -171,7 +171,7 @@ void DlgUpdate::finishedUpdateCheck(bool needToUpdate, bool isCompatible, Releas
|
|||
|
||||
publishDate = release->getPublishDate().toString(QLocale().dateFormat(QLocale::LongFormat));
|
||||
if (isCompatible) {
|
||||
const QString message = tr("A new version of Cockatrice is available!") + "<br><br>" + "<b>" +
|
||||
const QString message = tr("A new version of Continuumatrice is available!") + "<br><br>" + "<b>" +
|
||||
tr("New version") + QString(":</b> %1<br>").arg(release->getName()) + "<b>" +
|
||||
tr("Released") +
|
||||
QString(":</b> %1 (<a href=\"%2\">").arg(publishDate, release->getDescriptionUrl()) +
|
||||
|
|
@ -185,7 +185,7 @@ void DlgUpdate::finishedUpdateCheck(bool needToUpdate, bool isCompatible, Releas
|
|||
} else {
|
||||
QMessageBox::information(
|
||||
window(), tr("Update Available"),
|
||||
tr("A new version of Cockatrice is available!") + "<br><br>" + "<b>" + tr("New version") +
|
||||
tr("A new version of Continuumatrice is available!") + "<br><br>" + "<b>" + tr("New version") +
|
||||
QString(":</b> %1<br>").arg(release->getName()) + "<b>" + tr("Released") +
|
||||
QString(":</b> %1 (<a href=\"%2\">").arg(publishDate, release->getDescriptionUrl()) + tr("Changelog") +
|
||||
"</a>)<br><br>" +
|
||||
|
|
@ -235,7 +235,7 @@ void DlgUpdate::warnInstallerIsWaiting()
|
|||
// would therefore swallow the installer's one and only request for the rest of its wait.
|
||||
auto *notification = new QMessageBox(QMessageBox::Warning, tr("Update"),
|
||||
tr("The update installer is already running and waits about a "
|
||||
"minute for Cockatrice to close. Cockatrice is still busy, so "
|
||||
"minute for Continuumatrice to close. Continuumatrice is still busy, so "
|
||||
"save your work and close it before then. Otherwise the "
|
||||
"installer gives up and the update is cancelled."),
|
||||
QMessageBox::Ok, parentWidget());
|
||||
|
|
@ -293,9 +293,9 @@ void DlgUpdate::downloadSuccessful(const QUrl &filepath)
|
|||
process.setArguments({"/R", QString("/D=%1").arg(appDir)});
|
||||
#endif
|
||||
|
||||
// Try to open the installer. If it opens, quit Cockatrice
|
||||
// Try to open the installer. If it opens, quit Continuumatrice
|
||||
if (process.startDetached()) {
|
||||
qCInfo(DlgUpdateLog) << "Opened update installer successfully - closing Cockatrice";
|
||||
qCInfo(DlgUpdateLog) << "Opened update installer successfully - closing Continuumatrice";
|
||||
// Close the main window synchronously so file locks are released before the NSIS installer
|
||||
// (already launched) starts replacing files. This also flushes settings and shuts down the
|
||||
// tabs, but only when the close is actually accepted: MainWindow may veto it for a running
|
||||
|
|
@ -324,8 +324,8 @@ void DlgUpdate::downloadSuccessful(const QUrl &filepath)
|
|||
} else {
|
||||
setLabel(tr("Error"));
|
||||
QMessageBox::critical(this, tr("Update Error"),
|
||||
tr("Cockatrice is unable to open the installer.") + "<br><br>" +
|
||||
tr("Try to update manually by closing Cockatrice and running the installer.") +
|
||||
tr("Continuumatrice is unable to open the installer.") + "<br><br>" +
|
||||
tr("Try to update manually by closing Continuumatrice and running the installer.") +
|
||||
"<br>" + tr("Download location") + QString(": %1").arg(filepath.toLocalFile()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#include "color_bar.h"
|
||||
|
||||
#include "libcockatrice/utility/color.h"
|
||||
#include "../../../../../essence_chart_colors.h"
|
||||
|
||||
#include <QBrush>
|
||||
#include <QLinearGradient>
|
||||
|
|
@ -19,14 +19,14 @@
|
|||
#include <utility>
|
||||
|
||||
ColorBar::ColorBar(const QMap<QString, int> &_colors, QWidget *parent)
|
||||
: QWidget(parent), colors(GameSpecificColors::MTG::sortManaMapWUBRGCFirst(_colors))
|
||||
: QWidget(parent), colors(EssenceCharts::ordered(_colors))
|
||||
{
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
void ColorBar::setColors(const QMap<QString, int> &_colors)
|
||||
{
|
||||
colors = GameSpecificColors::MTG::sortManaMapWUBRGCFirst(_colors);
|
||||
colors = EssenceCharts::ordered(_colors);
|
||||
update();
|
||||
}
|
||||
|
||||
|
|
@ -150,19 +150,5 @@ QString ColorBar::tooltipForPosition(int x) const
|
|||
|
||||
QColor ColorBar::colorFromName(const QString &name) const
|
||||
{
|
||||
static QMap<QString, QColor> map = {
|
||||
{"R", QColor(220, 30, 30)}, {"G", QColor(40, 170, 40)}, {"U", QColor(40, 90, 200)},
|
||||
{"W", QColor(235, 235, 230)}, {"B", QColor(30, 30, 30)},
|
||||
};
|
||||
|
||||
if (map.contains(name)) {
|
||||
return map[name];
|
||||
}
|
||||
|
||||
QColor c(name);
|
||||
if (!c.isValid()) {
|
||||
c = Qt::gray;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
return EssenceCharts::colorFor(name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#include "color_pie.h"
|
||||
|
||||
#include "libcockatrice/utility/color.h"
|
||||
#include "../../../../../essence_chart_colors.h"
|
||||
|
||||
#include <QBrush>
|
||||
#include <QFontMetrics>
|
||||
|
|
@ -20,14 +20,14 @@
|
|||
#include <utility>
|
||||
|
||||
ColorPie::ColorPie(const QMap<QString, int> &_colors, QWidget *parent)
|
||||
: QWidget(parent), colors(GameSpecificColors::MTG::sortManaMapWUBRGCFirst(_colors))
|
||||
: QWidget(parent), colors(EssenceCharts::ordered(_colors))
|
||||
{
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
void ColorPie::setColors(const QMap<QString, int> &_colors)
|
||||
{
|
||||
colors = GameSpecificColors::MTG::sortManaMapWUBRGCFirst(_colors);
|
||||
colors = EssenceCharts::ordered(_colors);
|
||||
update();
|
||||
}
|
||||
|
||||
|
|
@ -184,19 +184,5 @@ QString ColorPie::tooltipForPoint(const QPoint &pt) const
|
|||
|
||||
QColor ColorPie::colorFromName(const QString &name) const
|
||||
{
|
||||
static QMap<QString, QColor> map = {
|
||||
{"R", QColor(220, 30, 30)}, {"G", QColor(40, 170, 40)}, {"U", QColor(40, 90, 200)},
|
||||
{"W", QColor(235, 235, 230)}, {"B", QColor(30, 30, 30)},
|
||||
};
|
||||
|
||||
if (map.contains(name)) {
|
||||
return map[name];
|
||||
}
|
||||
|
||||
QColor c(name);
|
||||
if (!c.isValid()) {
|
||||
c = Qt::gray;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
return EssenceCharts::colorFor(name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ void FirstRunWizard::changeEvent(QEvent *event)
|
|||
|
||||
void FirstRunWizard::retranslateUi()
|
||||
{
|
||||
setWindowTitle(tr("Welcome to Cockatrice"));
|
||||
setWindowTitle(tr("Welcome to Continuumatrice"));
|
||||
backButton->setText(tr("Back"));
|
||||
skipButton->setText(tr("Skip"));
|
||||
for (FirstRunWizardPage *page : std::as_const(pages)) {
|
||||
|
|
|
|||
|
|
@ -1511,7 +1511,7 @@ void TabSupervisor::processNotifyUserEvent(const Event_NotifyUser &event)
|
|||
QMessageBox::information(
|
||||
this, tr("Unknown Event"),
|
||||
tr("The server has sent you a message that your client does not understand.\nThis message might mean "
|
||||
"there is a new version of Cockatrice available or this server is running a custom or pre-release "
|
||||
"there is a new version of Continuumatrice available or this server is running a custom or pre-release "
|
||||
"version.\n\nTo update your client, go to Help -> Check for Updates."));
|
||||
break;
|
||||
case Event_NotifyUser::IDLEWARNING:
|
||||
|
|
|
|||
|
|
@ -1,48 +1,44 @@
|
|||
#include "visual_database_display_color_filter_widget.h"
|
||||
|
||||
#include "../cards/additional_info/mana_symbol_widget.h"
|
||||
#include "../cards/additional_info/essence_filter_picker.h"
|
||||
#include "filter_tree_model.h"
|
||||
#include "libcockatrice/filters/filter_card.h"
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QComboBox>
|
||||
#include <QDebug>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPersistentModelIndex>
|
||||
#include <QSharedPointer>
|
||||
#include <QSizePolicy>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QtGlobal>
|
||||
#include <libcockatrice/card/essence_types.h>
|
||||
#include <libcockatrice/filters/filter_tree.h>
|
||||
|
||||
/**
|
||||
* This widget provides a graphical control element for the CardFilter::Attr::AttrColor filters applied to the filter
|
||||
* model.
|
||||
* @param parent The Qt Widget that this widget will parent to
|
||||
* @param _filterModel The filter model that this widget will manipulate
|
||||
*/
|
||||
VisualDatabaseDisplayColorFilterWidget::VisualDatabaseDisplayColorFilterWidget(QWidget *parent,
|
||||
FilterTreeModel *_filterModel)
|
||||
: QWidget(parent), filterModel(_filterModel), layout(new QHBoxLayout(this))
|
||||
{
|
||||
setLayout(layout);
|
||||
layout->setSpacing(5);
|
||||
layout->setSpacing(3);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
QString fullColorIdentity = "WUBRG";
|
||||
for (const QChar &color : fullColorIdentity) {
|
||||
auto *manaSymbol = new ManaSymbolWidget(this, color, false, true);
|
||||
manaSymbol->setFixedWidth(25);
|
||||
// Share the same compact Essence selection UI as Visual Deck Storage.
|
||||
// A 15-icon horizontal row cannot fit the database filter toolbar.
|
||||
essencePicker = new EssenceFilterPicker(this);
|
||||
layout->addWidget(essencePicker);
|
||||
// FlowWidget measures THIS parent widget, not its nested button. Give it
|
||||
// an explicit size hint that cannot clip the picker.
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
|
||||
setMinimumWidth(essencePicker->width());
|
||||
|
||||
layout->addWidget(manaSymbol);
|
||||
connect(essencePicker, &EssenceFilterPicker::essenceToggled, this,
|
||||
&VisualDatabaseDisplayColorFilterWidget::handleColorToggled);
|
||||
|
||||
// Connect the color toggled signal
|
||||
connect(manaSymbol, &ManaSymbolWidget::colorToggled, this,
|
||||
&VisualDatabaseDisplayColorFilterWidget::handleColorToggled);
|
||||
}
|
||||
connect(essencePicker, &EssenceFilterPicker::selectionCleared, this, [this]() {
|
||||
filterModel->clearFiltersOfType(CardFilter::Attr::AttrColor);
|
||||
});
|
||||
|
||||
modeComboBox = new QComboBox(this);
|
||||
layout->addWidget(modeComboBox);
|
||||
essencePicker->addPopupControl(modeComboBox);
|
||||
|
||||
connect(modeComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&VisualDatabaseDisplayColorFilterWidget::updateFilterMode);
|
||||
|
|
@ -50,108 +46,83 @@ VisualDatabaseDisplayColorFilterWidget::VisualDatabaseDisplayColorFilterWidget(Q
|
|||
connect(filterModel, &FilterTreeModel::layoutChanged, this,
|
||||
[this]() { syncDebouncer.call([this] { syncWithFilterModel(); }); });
|
||||
|
||||
// Call retranslateUi to set the initial text
|
||||
retranslateUi();
|
||||
syncWithFilterModel();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayColorFilterWidget::retranslateUi()
|
||||
{
|
||||
essencePicker->retranslateUi();
|
||||
|
||||
modeComboBox->blockSignals(true);
|
||||
modeComboBox->clear();
|
||||
|
||||
modeComboBox->addItem(tr("Exact match"), QVariant::fromValue(FilterMode::ExactMatch));
|
||||
modeComboBox->addItem(tr("Includes"), QVariant::fromValue(FilterMode::Includes));
|
||||
modeComboBox->addItem(tr("Include / Exclude"), QVariant::fromValue(FilterMode::IncludeExclude));
|
||||
modeComboBox->setToolTip(tr("How selected and unselected Essence types are combined in the filter"));
|
||||
|
||||
modeComboBox->setToolTip(tr("How selected and unselected colors are combined in the filter"));
|
||||
|
||||
// Restore current mode
|
||||
const int index = modeComboBox->findData(QVariant::fromValue(currentMode));
|
||||
if (index >= 0) {
|
||||
modeComboBox->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
modeComboBox->blockSignals(false);
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayColorFilterWidget::handleColorToggled(QChar color, bool active)
|
||||
void VisualDatabaseDisplayColorFilterWidget::handleColorToggled(QChar essence, bool active)
|
||||
{
|
||||
if (active) {
|
||||
addFilter(color);
|
||||
addFilter(essence);
|
||||
} else {
|
||||
removeFilter(color);
|
||||
removeFilter(essence);
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayColorFilterWidget::addFilter(QChar color)
|
||||
void VisualDatabaseDisplayColorFilterWidget::addFilter(QChar essence)
|
||||
{
|
||||
QString colorString = color;
|
||||
QString typeStr;
|
||||
|
||||
// Remove previous filters
|
||||
|
||||
QList<const CardFilter *> allColorFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor);
|
||||
QList<const CardFilter *> matchingFilters;
|
||||
|
||||
for (const CardFilter *filter : allColorFilters) {
|
||||
if (filter->term() == color) {
|
||||
matchingFilters.append(filter);
|
||||
QString essenceString(essence);
|
||||
const QList<const CardFilter *> filters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor);
|
||||
for (const CardFilter *filter : filters) {
|
||||
if (filter->term() == essenceString) {
|
||||
filterModel->removeFilter(filter);
|
||||
}
|
||||
}
|
||||
|
||||
for (const CardFilter *filter : matchingFilters) {
|
||||
filterModel->removeFilter(filter);
|
||||
}
|
||||
|
||||
// Add actual filter
|
||||
|
||||
switch (currentMode) {
|
||||
case FilterMode::ExactMatch:
|
||||
filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrColor));
|
||||
filterModel->addFilter(
|
||||
new CardFilter(essenceString, CardFilter::Type::TypeAnd, CardFilter::Attr::AttrColor));
|
||||
break;
|
||||
|
||||
case FilterMode::Includes:
|
||||
filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor));
|
||||
break;
|
||||
|
||||
case FilterMode::IncludeExclude:
|
||||
filterModel->addFilter(new CardFilter(colorString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor));
|
||||
filterModel->addFilter(
|
||||
new CardFilter(essenceString, CardFilter::Type::TypeOr, CardFilter::Attr::AttrColor));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayColorFilterWidget::removeFilter(QChar color)
|
||||
void VisualDatabaseDisplayColorFilterWidget::removeFilter(QChar essence)
|
||||
{
|
||||
QString colorString = color;
|
||||
|
||||
// Remove inclusion filters
|
||||
QList<const CardFilter *> allColorFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor);
|
||||
QList<const CardFilter *> matchingFilters;
|
||||
|
||||
for (const CardFilter *filter : allColorFilters) {
|
||||
if (filter->term() == color) {
|
||||
matchingFilters.append(filter);
|
||||
QString essenceString(essence);
|
||||
const QList<const CardFilter *> filters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor);
|
||||
for (const CardFilter *filter : filters) {
|
||||
if (filter->term() == essenceString) {
|
||||
filterModel->removeFilter(filter);
|
||||
}
|
||||
}
|
||||
|
||||
for (const CardFilter *filter : matchingFilters) {
|
||||
filterModel->removeFilter(filter);
|
||||
// The empty selection means "Any Essence", not "exclude everything".
|
||||
if (essencePicker->activeEssences().isEmpty()) {
|
||||
filterModel->clearFiltersOfType(CardFilter::Attr::AttrColor);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add exclusion filters if the mode demands it
|
||||
switch (currentMode) {
|
||||
case FilterMode::ExactMatch:
|
||||
filterModel->addFilter(
|
||||
new CardFilter(colorString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor));
|
||||
break;
|
||||
|
||||
case FilterMode::IncludeExclude:
|
||||
filterModel->addFilter(
|
||||
new CardFilter(colorString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor));
|
||||
new CardFilter(essenceString, CardFilter::Type::TypeAndNot, CardFilter::Attr::AttrColor));
|
||||
break;
|
||||
|
||||
case FilterMode::Includes:
|
||||
// No exclusion in Includes mode
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -170,10 +141,12 @@ void VisualDatabaseDisplayColorFilterWidget::updateFilterMode()
|
|||
|
||||
filterModel->clearFiltersOfType(CardFilter::Attr::AttrColor);
|
||||
|
||||
const QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>();
|
||||
|
||||
for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) {
|
||||
handleColorToggled(manaSymbolWidget->getSymbolChar(), manaSymbolWidget->isColorActive());
|
||||
const QSet<QChar> selected = essencePicker->activeEssences();
|
||||
// No selections should never generate 15 mutually excluding filters.
|
||||
if (!selected.isEmpty()) {
|
||||
for (const QChar essence : Essence::symbols()) {
|
||||
handleColorToggled(essence, selected.contains(essence));
|
||||
}
|
||||
}
|
||||
|
||||
filterModel->blockSignals(false);
|
||||
|
|
@ -184,79 +157,44 @@ void VisualDatabaseDisplayColorFilterWidget::updateFilterMode()
|
|||
emit filterModeChanged(currentMode);
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayColorFilterWidget::setManaSymbolActive(QChar color, bool active)
|
||||
void VisualDatabaseDisplayColorFilterWidget::setEssenceActive(QChar essence, bool active)
|
||||
{
|
||||
QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>();
|
||||
|
||||
for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) {
|
||||
if (manaSymbolWidget->getSymbolChar() == color) {
|
||||
manaSymbolWidget->setColorActive(active);
|
||||
}
|
||||
}
|
||||
essencePicker->setEssenceActive(essence, active);
|
||||
}
|
||||
|
||||
QList<QChar> VisualDatabaseDisplayColorFilterWidget::getActiveColors()
|
||||
{
|
||||
QList<QChar> activeColors;
|
||||
QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>();
|
||||
|
||||
for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) {
|
||||
if (manaSymbolWidget->isColorActive()) {
|
||||
activeColors.append(manaSymbolWidget->getSymbolChar());
|
||||
QList<QChar> result;
|
||||
const QSet<QChar> selected = essencePicker->activeEssences();
|
||||
for (const QChar essence : Essence::symbols()) {
|
||||
if (selected.contains(essence)) {
|
||||
result.append(essence);
|
||||
}
|
||||
}
|
||||
|
||||
return activeColors;
|
||||
return result;
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayColorFilterWidget::syncWithFilterModel()
|
||||
{
|
||||
QList<const CardFilter *> allColorFilters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor);
|
||||
const QList<const CardFilter *> filters = filterModel->getFiltersOfType(CardFilter::Attr::AttrColor);
|
||||
|
||||
QList<ManaSymbolWidget *> manaSymbolWidgets = findChildren<ManaSymbolWidget *>();
|
||||
|
||||
for (ManaSymbolWidget *manaSymbolWidget : manaSymbolWidgets) {
|
||||
bool found = false;
|
||||
for (const CardFilter *filter : allColorFilters) {
|
||||
if (manaSymbolWidget->getSymbolChar() == filter->term()) {
|
||||
switch (currentMode) {
|
||||
case FilterMode::ExactMatch:
|
||||
switch (filter->type()) {
|
||||
case CardFilter::Type::TypeAnd:
|
||||
setManaSymbolActive(filter->term().at(0), true);
|
||||
break;
|
||||
default:
|
||||
setManaSymbolActive(filter->term().at(0), false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case FilterMode::Includes:
|
||||
switch (filter->type()) {
|
||||
case CardFilter::Type::TypeOr:
|
||||
setManaSymbolActive(filter->term().at(0), true);
|
||||
break;
|
||||
default:
|
||||
setManaSymbolActive(filter->term().at(0), false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case FilterMode::IncludeExclude:
|
||||
switch (filter->type()) {
|
||||
case CardFilter::Type::TypeOr:
|
||||
setManaSymbolActive(filter->term().at(0), true);
|
||||
break;
|
||||
default:
|
||||
setManaSymbolActive(filter->term().at(0), false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
found = true;
|
||||
for (const QChar essence : Essence::symbols()) {
|
||||
bool selected = false;
|
||||
for (const CardFilter *filter : filters) {
|
||||
if (filter->term() != QString(essence)) {
|
||||
continue;
|
||||
}
|
||||
if (currentMode == FilterMode::ExactMatch) {
|
||||
selected = filter->type() == CardFilter::Type::TypeAnd;
|
||||
} else {
|
||||
selected = filter->type() == CardFilter::Type::TypeOr;
|
||||
}
|
||||
if (selected) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
setManaSymbolActive(manaSymbolWidget->getSymbolChar(), false);
|
||||
}
|
||||
// Signals are blocked inside the picker; loading saved filters must
|
||||
// not mutate the FilterTreeModel again.
|
||||
setEssenceActive(essence, selected);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
class FilterTreeModel;
|
||||
class QComboBox;
|
||||
class QHBoxLayout;
|
||||
class EssenceFilterPicker;
|
||||
|
||||
class VisualDatabaseDisplayColorFilterCircleWidget : public QWidget
|
||||
{
|
||||
|
|
@ -63,7 +64,7 @@ private slots:
|
|||
void addFilter(QChar color);
|
||||
void removeFilter(QChar color);
|
||||
void updateFilterMode();
|
||||
void setManaSymbolActive(QChar color, bool active);
|
||||
void setEssenceActive(QChar essence, bool active);
|
||||
QList<QChar> getActiveColors();
|
||||
void syncWithFilterModel();
|
||||
|
||||
|
|
@ -71,6 +72,7 @@ private:
|
|||
FilterTreeModel *filterModel;
|
||||
QHBoxLayout *layout;
|
||||
QComboBox *modeComboBox;
|
||||
EssenceFilterPicker *essencePicker = nullptr;
|
||||
FilterMode currentMode = FilterMode::Includes; // Default mode
|
||||
QtUtils::Debouncer syncDebouncer{100};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
#include "deck_preview_color_identity_filter_widget.h"
|
||||
|
||||
#include "../../cards/additional_info/mana_symbol_widget.h"
|
||||
#include "../../cards/additional_info/essence_filter_picker.h"
|
||||
#include "../visual_deck_storage_sort_filter_proxy_model.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QSizePolicy>
|
||||
#include <QSet>
|
||||
#include <QSharedPointer>
|
||||
#include <QString>
|
||||
|
||||
DeckPreviewColorIdentityFilterWidget::DeckPreviewColorIdentityFilterWidget(QWidget *parent)
|
||||
|
|
@ -16,34 +16,30 @@ DeckPreviewColorIdentityFilterWidget::DeckPreviewColorIdentityFilterWidget(QWidg
|
|||
layout->setSpacing(5);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
QString fullColorIdentity = "WUBRG";
|
||||
for (const QChar &color : fullColorIdentity) {
|
||||
auto *manaSymbol = new ManaSymbolWidget(this, color, false, true);
|
||||
manaSymbol->setFixedWidth(25);
|
||||
// Fifteen Essence codes don't fit as inline buttons beside sorting and
|
||||
// search. The popup keeps all codes selectable without expanding the row.
|
||||
essencePicker = new EssenceFilterPicker(this);
|
||||
layout->addWidget(essencePicker);
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
|
||||
setMinimumWidth(essencePicker->width());
|
||||
|
||||
layout->addWidget(manaSymbol);
|
||||
|
||||
// Initialize the activeColors map
|
||||
activeColors[color] = false;
|
||||
|
||||
// Connect the color toggled signal
|
||||
connect(manaSymbol, &ManaSymbolWidget::colorToggled, this,
|
||||
&DeckPreviewColorIdentityFilterWidget::handleColorToggled);
|
||||
}
|
||||
connect(essencePicker, &EssenceFilterPicker::essenceToggled, this,
|
||||
[this](QChar /*essence*/, bool /*active*/) { emit activeColorsChanged(); });
|
||||
connect(essencePicker, &EssenceFilterPicker::selectionCleared, this,
|
||||
[this]() { emit activeColorsChanged(); });
|
||||
|
||||
toggleButton = new QPushButton(this);
|
||||
layout->addWidget(toggleButton);
|
||||
|
||||
// Connect the button's clicked signal
|
||||
essencePicker->addPopupControl(toggleButton);
|
||||
connect(toggleButton, &QPushButton::clicked, this, &DeckPreviewColorIdentityFilterWidget::updateFilterMode);
|
||||
|
||||
// Call retranslateUi to set the initial text
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void DeckPreviewColorIdentityFilterWidget::retranslateUi()
|
||||
{
|
||||
// Set the toggle button text based on the current mode
|
||||
essencePicker->retranslateUi();
|
||||
|
||||
// Preserve existing Exact / Includes / Excludes filter semantics.
|
||||
switch (filterMode) {
|
||||
case VisualDeckStorageSortFilterProxyModel::FilterMode::ExactMatch:
|
||||
toggleButton->setText(tr("Mode: Exact Match"));
|
||||
|
|
@ -55,32 +51,21 @@ void DeckPreviewColorIdentityFilterWidget::retranslateUi()
|
|||
toggleButton->setText(tr("Mode: Excludes"));
|
||||
break;
|
||||
}
|
||||
toggleButton->setToolTip(tr("Color identity filter mode (AND/OR/NOT conjunctions of filters)"));
|
||||
toggleButton->setToolTip(tr("Essence filter mode (exact match, includes, or excludes)"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The colors that are currently toggled on.
|
||||
*/
|
||||
QSet<QChar> DeckPreviewColorIdentityFilterWidget::getActiveColors() const
|
||||
{
|
||||
QSet<QChar> activeColorSet;
|
||||
for (auto it = activeColors.constBegin(); it != activeColors.constEnd(); ++it) {
|
||||
if (it.value()) {
|
||||
activeColorSet.insert(it.key());
|
||||
}
|
||||
}
|
||||
return activeColorSet;
|
||||
return essencePicker->activeEssences();
|
||||
}
|
||||
|
||||
void DeckPreviewColorIdentityFilterWidget::handleColorToggled(QChar color, bool active)
|
||||
void DeckPreviewColorIdentityFilterWidget::handleColorToggled(QChar /*essence*/, bool /*active*/)
|
||||
{
|
||||
activeColors[color] = active;
|
||||
emit activeColorsChanged();
|
||||
}
|
||||
|
||||
void DeckPreviewColorIdentityFilterWidget::updateFilterMode()
|
||||
{
|
||||
// Cycle through the modes
|
||||
switch (filterMode) {
|
||||
case VisualDeckStorageSortFilterProxyModel::FilterMode::ExactMatch:
|
||||
filterMode = VisualDeckStorageSortFilterProxyModel::FilterMode::Includes;
|
||||
|
|
@ -93,6 +78,6 @@ void DeckPreviewColorIdentityFilterWidget::updateFilterMode()
|
|||
break;
|
||||
}
|
||||
|
||||
retranslateUi(); // Update the button text
|
||||
retranslateUi();
|
||||
emit filterModeChanged(filterMode);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@
|
|||
#include "../visual_deck_storage_sort_filter_proxy_model.h"
|
||||
|
||||
#include <QChar>
|
||||
#include <QMap>
|
||||
#include <QSet>
|
||||
#include <QWidget>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
class QHBoxLayout;
|
||||
class QPushButton;
|
||||
class EssenceFilterPicker;
|
||||
|
||||
class DeckPreviewColorIdentityFilterWidget : public QWidget
|
||||
{
|
||||
|
|
@ -57,7 +57,7 @@ private slots:
|
|||
private:
|
||||
QHBoxLayout *layout;
|
||||
QPushButton *toggleButton;
|
||||
QMap<QChar, bool> activeColors;
|
||||
EssenceFilterPicker *essencePicker = nullptr;
|
||||
VisualDeckStorageSortFilterProxyModel::FilterMode filterMode = VisualDeckStorageSortFilterProxyModel::Includes;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -120,17 +120,17 @@
|
|||
|
||||
class LocalServerInterface;
|
||||
|
||||
#define GITHUB_PAGES_URL "https://cockatrice.github.io"
|
||||
#define GITHUB_PAGES_URL "https://github.com/Continuum-Universes-Studio/Continuumatrice"
|
||||
#define GITHUB_CONTRIBUTORS_URL "https://github.com/Cockatrice/Cockatrice/graphs/contributors"
|
||||
#define GITHUB_CONTRIBUTE_URL "https://github.com/Cockatrice/Cockatrice#"
|
||||
#define GITHUB_TRANSIFEX_TRANSLATORS_URL "https://github.com/Cockatrice/Cockatrice/wiki/Translator-Hall-of-Fame"
|
||||
#define GITHUB_TRANSLATOR_FAQ_URL "https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ"
|
||||
#define GITHUB_ISSUES_URL "https://github.com/Cockatrice/Cockatrice/issues"
|
||||
#define GITHUB_ISSUES_URL "https://github.com/Continuum-Universes-Studio/Continuumatrice/issues"
|
||||
#define GITHUB_TROUBLESHOOTING_URL "https://github.com/Cockatrice/Cockatrice/wiki/Troubleshooting"
|
||||
#define GITHUB_FAQ_URL "https://github.com/Cockatrice/Cockatrice/wiki/Frequently-Asked-Questions"
|
||||
|
||||
const QString MainWindow::appName = "Cockatrice";
|
||||
const QStringList MainWindow::fileNameFilters = QStringList() << QObject::tr("Cockatrice card database (*.xml)")
|
||||
const QString MainWindow::appName = "Continuumatrice";
|
||||
const QStringList MainWindow::fileNameFilters = QStringList() << QObject::tr("Continuumatrice card database (*.xml)")
|
||||
<< QObject::tr("All files (*.*)");
|
||||
inline Q_LOGGING_CATEGORY(MainWindowLog, "main_window");
|
||||
|
||||
|
|
@ -231,7 +231,7 @@ void MainWindow::actWatchReplay()
|
|||
{
|
||||
QFileDialog dlg(this, tr("Load replay"));
|
||||
dlg.setDirectory(SettingsCache::instance().paths().getReplaysPath());
|
||||
dlg.setNameFilters(QStringList() << QObject::tr("Cockatrice replays (*.cor)"));
|
||||
dlg.setNameFilters(QStringList() << QObject::tr("Continuumatrice replays (*.cor)"));
|
||||
if (!dlg.exec()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -292,27 +292,24 @@ void MainWindow::actExit()
|
|||
void MainWindow::actAbout()
|
||||
{
|
||||
QMessageBox mb(
|
||||
QMessageBox::NoIcon, tr("About Cockatrice"),
|
||||
QString("<font size=\"8\"><b>Cockatrice</b></font> (" + QString::fromStdString(BUILD_ARCHITECTURE) + ")<br>" +
|
||||
tr("Version") + QString(" %1").arg(VERSION_STRING) + "<br><br><b><a href='" + GITHUB_PAGES_URL + "'>" +
|
||||
tr("Cockatrice Webpage") + "</a></b><br>" + "<br><b>" + tr("Project Manager:") +
|
||||
"</b><br>Zach Halpern<br><br>" + "<b>" + tr("Past Project Managers:") +
|
||||
"</b><br>Gavin Bisesi<br>Max-Wilhelm Bruker<br>Marcus Schütz<br><br>" + "<b>" + tr("Developers:") +
|
||||
"</b><br>" + "<a href='" + GITHUB_CONTRIBUTORS_URL + "'>" + tr("Our Developers") + "</a><br>" +
|
||||
"<a href='" + GITHUB_CONTRIBUTE_URL + "'>" + tr("Help Develop!") + "</a><br><br>" + "<b>" +
|
||||
tr("Translators:") + "</b><br>" + "<a href='" + GITHUB_TRANSIFEX_TRANSLATORS_URL + "'>" +
|
||||
tr("Our Translators") + "</a><br>" + "<a href='" + GITHUB_TRANSLATOR_FAQ_URL + "'>" +
|
||||
tr("Help Translate!") + "</a><br><br>" + "<b>" + tr("Support:") + "</b><br>" + "<a href='" +
|
||||
GITHUB_ISSUES_URL + "'>" + tr("Report an Issue") + "</a><br>" + "<a href='" +
|
||||
GITHUB_TROUBLESHOOTING_URL + "'>" + tr("Troubleshooting") + "</a><br>" + "<a href='" + GITHUB_FAQ_URL +
|
||||
"'>" + tr("F.A.Q.") + "</a><br>"),
|
||||
QMessageBox::NoIcon, tr("About Continuumatrice"),
|
||||
QStringLiteral("<font size=\"8\"><b>Continuumatrice</b></font><br>") +
|
||||
tr("Continuum Universes Studio") + QStringLiteral(" — ") + tr("Version") +
|
||||
QStringLiteral(" ") + QString(VERSION_STRING) +
|
||||
QStringLiteral("<br><br>") +
|
||||
tr("Continuumatrice is a standalone Continuum Universes card-game client.") +
|
||||
QStringLiteral("<br><a href='https://github.com/Continuum-Universes-Studio/Continuumatrice'>") +
|
||||
tr("Source code and issues") + QStringLiteral("</a><br><br>") +
|
||||
tr("Based on the open-source Cockatrice project. The original developers and contributors "
|
||||
"retain credit for their work, and the applicable open-source license remains in force.") +
|
||||
QStringLiteral("<br><a href='https://github.com/Cockatrice/Cockatrice'>") +
|
||||
tr("Upstream Cockatrice project") + QStringLiteral("</a>"),
|
||||
QMessageBox::Ok, this);
|
||||
mb.setIconPixmap(
|
||||
themePixmap(QStringLiteral("cockatrice")).scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
themePixmap(QStringLiteral("continuumatrice")).scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
mb.setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
mb.exec();
|
||||
}
|
||||
|
||||
void MainWindow::actTips()
|
||||
{
|
||||
if (tip != nullptr) {
|
||||
|
|
@ -369,7 +366,7 @@ void MainWindow::retranslateUi()
|
|||
#if defined(__APPLE__) /* For OSX */
|
||||
cockatriceMenu->setTitle(tr("A&ctions"));
|
||||
#else
|
||||
cockatriceMenu->setTitle(tr("&Cockatrice"));
|
||||
cockatriceMenu->setTitle(tr("&Continuumatrice"));
|
||||
#endif
|
||||
|
||||
dbMenu->setTitle(tr("C&ard Database"));
|
||||
|
|
@ -383,11 +380,13 @@ void MainWindow::retranslateUi()
|
|||
tabsMenu->setTitle(tr("Tabs"));
|
||||
|
||||
helpMenu->setTitle(tr("&Help"));
|
||||
aAbout->setText(tr("&About Cockatrice"));
|
||||
aAbout->setText(tr("&About Continuumatrice"));
|
||||
aTips->setText(tr("&Tip of the Day"));
|
||||
aUpdate->setText(tr("Check for Client Updates"));
|
||||
aCheckCardUpdates->setText(tr("Check for Card Updates..."));
|
||||
aCheckCardUpdatesBackground->setText(tr("Check for Card Updates (Automatic)"));
|
||||
aCheckCardUpdates->setText(tr("Legacy MTG Oracle importer (disabled)"));
|
||||
aCheckCardUpdates->setEnabled(false);
|
||||
aCheckCardUpdatesBackground->setText(tr("Automatic MTG card updates (disabled)"));
|
||||
aCheckCardUpdatesBackground->setEnabled(false);
|
||||
aStatusBar->setText(tr("Show Status Bar"));
|
||||
aViewLog->setText(tr("View &Debug Log"));
|
||||
aOpenSettingsFolder->setText(tr("Open Settings Folder"));
|
||||
|
|
@ -651,20 +650,23 @@ void MainWindow::startupConfigCheck()
|
|||
startLocalGame(options);
|
||||
}
|
||||
|
||||
if (SettingsCache::instance().updates().getCheckUpdatesOnStartup()) {
|
||||
actCheckClientUpdates();
|
||||
}
|
||||
|
||||
actCheckCommanderBracketDefinitionUpdates();
|
||||
// The fork has independent release channels. Until a Continuumatrice
|
||||
// release is published, do not auto-check or run upstream Cockatrice updates.
|
||||
SettingsCache::instance().updates().setCheckUpdatesOnStartup(false);
|
||||
|
||||
// No MTG Commander-bracket network requests in Continuumatrice.
|
||||
if (isCleanInstall) {
|
||||
// no config found, 99% new clean install
|
||||
qCInfo(WindowMainStartupVersionLog)
|
||||
<< "Startup: old client version empty, assuming first start after clean install";
|
||||
SettingsCache::instance().downloads().resetToDefaultURLs(); // populate the download urls
|
||||
SettingsCache::instance().downloads().resetToDefaultURLs(); // Empty in Continuumatrice: no MTG providers
|
||||
SettingsCache::instance().network().setClientVersion(VERSION_STRING);
|
||||
actCheckServerUpdates();
|
||||
runFirstRunWizard(true);
|
||||
// No upstream Cockatrice first-run wizard, Magic importer, or default
|
||||
// server discovery. Cards are loaded from this application's own XML.
|
||||
QMessageBox::information(this, tr("Welcome to Continuumatrice"),
|
||||
tr("Continuumatrice uses its own card database. "
|
||||
"Add your card XML files using Card Database > Add custom sets/cards "
|
||||
"or place them in the Continuumatrice customsets directory."));
|
||||
|
||||
if (QString(VERSION_STRING).contains("custom", Qt::CaseInsensitive)) {
|
||||
SettingsCache::instance().updates().setCheckUpdatesOnStartup(false);
|
||||
|
|
@ -677,7 +679,7 @@ void MainWindow::startupConfigCheck()
|
|||
<< "Startup: old client version" << SettingsCache::instance().network().getClientVersion()
|
||||
<< "differs, assuming first start after update";
|
||||
if (SettingsCache::instance().updates().getNotifyAboutNewVersion()) {
|
||||
alertForcedOracleRun(VERSION_STRING, true);
|
||||
// Do not automatically launch the upstream Magic-card Oracle importer.
|
||||
}
|
||||
|
||||
qCInfo(WindowMainStartupShortcutsLog) << "Migrating shortcuts after update detected.";
|
||||
|
|
@ -704,35 +706,8 @@ void MainWindow::startupConfigCheck()
|
|||
// previous config from this version found
|
||||
qCInfo(WindowMainStartupVersionLog) << "Startup: found config with current version";
|
||||
|
||||
if (SettingsCache::instance().updates().getCardUpdateCheckRequired()) {
|
||||
if (SettingsCache::instance().updates().getStartupCardUpdateCheckPromptForUpdate()) {
|
||||
auto startupCardCheckDialog = new DlgStartupCardCheck(this);
|
||||
// No automatic upstream card database downloads in Continuumatrice.
|
||||
|
||||
if (startupCardCheckDialog->exec() == QDialog::Accepted) {
|
||||
switch (startupCardCheckDialog->group->checkedId()) {
|
||||
case 0: // foreground
|
||||
actCheckCardUpdates();
|
||||
break;
|
||||
case 1: // background
|
||||
actCheckCardUpdatesBackground();
|
||||
break;
|
||||
case 2: // background + always
|
||||
SettingsCache::instance().updates().setStartupCardUpdateCheckPromptForUpdate(false);
|
||||
SettingsCache::instance().updates().setStartupCardUpdateCheckAlwaysUpdate(true);
|
||||
actCheckCardUpdatesBackground();
|
||||
break;
|
||||
case 3: // don't prompt again + don't run
|
||||
SettingsCache::instance().updates().setStartupCardUpdateCheckPromptForUpdate(false);
|
||||
SettingsCache::instance().updates().setStartupCardUpdateCheckAlwaysUpdate(false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (SettingsCache::instance().updates().getStartupCardUpdateCheckAlwaysUpdate()) {
|
||||
actCheckCardUpdatesBackground();
|
||||
}
|
||||
}
|
||||
|
||||
// Run the tips dialog only on subsequent startups.
|
||||
// On the first run after an install/update the startup is already crowded enough
|
||||
|
|
@ -860,12 +835,12 @@ void MainWindow::alertForcedOracleRun(const QString &version, bool isUpdate)
|
|||
{
|
||||
if (isUpdate) {
|
||||
QMessageBox::information(this, tr("New Version"),
|
||||
tr("Congratulations on updating to Cockatrice %1!\n"
|
||||
tr("Congratulations on updating to Continuumatrice %1!\n"
|
||||
"Oracle will now launch to update your card database.")
|
||||
.arg(version));
|
||||
} else {
|
||||
QMessageBox::information(this, tr("Cockatrice installed"),
|
||||
tr("Congratulations on installing Cockatrice %1!\n"
|
||||
QMessageBox::information(this, tr("Continuumatrice installed"),
|
||||
tr("Congratulations on installing Continuumatrice %1!\n"
|
||||
"Oracle will now launch to install the initial card database.")
|
||||
.arg(version));
|
||||
}
|
||||
|
|
@ -1110,7 +1085,7 @@ void MainWindow::cardDatabaseLoadingFailed()
|
|||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Card database"));
|
||||
msgBox.setIcon(QMessageBox::Question);
|
||||
msgBox.setText(tr("Cockatrice is unable to load the card database.\n"
|
||||
msgBox.setText(tr("Continuumatrice is unable to load the card database.\n"
|
||||
"Do you want to update your card database now?\n"
|
||||
"If unsure or first time user, choose \"Yes\""));
|
||||
|
||||
|
|
@ -1193,7 +1168,7 @@ void MainWindow::cardDatabaseAllNewSetsEnabled()
|
|||
}
|
||||
QMessageBox::information(
|
||||
this, tr("Welcome"),
|
||||
tr("Hi! It seems like you're running this version of Cockatrice for the first time.\nAll the sets in the "
|
||||
tr("This is the first time you've loaded this card database in Continuumatrice.\nAll the sets in the "
|
||||
"card "
|
||||
"database have been enabled.\nRead more about changing the set order or disabling specific sets and "
|
||||
"consequent effects in the \"Manage Sets\" dialog."));
|
||||
|
|
@ -1207,16 +1182,28 @@ void MainWindow::cardDatabaseAllNewSetsEnabled()
|
|||
/* CARD UPDATER */
|
||||
void MainWindow::actCheckCardUpdates()
|
||||
{
|
||||
createCardUpdateProcess();
|
||||
QMessageBox::information(this, tr("Continuumatrice card data"),
|
||||
tr("The upstream Oracle importer downloads Magic card data and is disabled "
|
||||
"in Continuumatrice. Import Continuum Universes XML cards using "
|
||||
"Card Database > Add custom sets/cards instead."));
|
||||
}
|
||||
|
||||
void MainWindow::actCheckCardUpdatesBackground()
|
||||
{
|
||||
createCardUpdateProcess(true);
|
||||
// Never run the upstream Oracle MTG importer unattended.
|
||||
qCInfo(CardDatabaseUpdateLog) << "The legacy Magic Oracle importer is disabled in Continuumatrice";
|
||||
}
|
||||
|
||||
void MainWindow::createCardUpdateProcess(bool background)
|
||||
{
|
||||
Q_UNUSED(background);
|
||||
// Direct callers must also be unable to launch the upstream MTG importer.
|
||||
QMessageBox::information(this, tr("Importer unavailable"),
|
||||
tr("Continuumatrice does not run the upstream Magic Oracle importer. "
|
||||
"Import Continuum Universes XML sets directly."));
|
||||
return;
|
||||
|
||||
// Legacy importer implementation retained for upstream reference only.
|
||||
if (cardUpdateProcess) {
|
||||
qCDebug(CardDatabaseUpdateLog) << "A card database update is already running; asking it to come to the front";
|
||||
if (requestCardUpdateRaise()) {
|
||||
|
|
@ -1309,7 +1296,7 @@ void MainWindow::createCardUpdateProcess(bool background)
|
|||
statusBar()->showMessage(tr("Card database update running."));
|
||||
if (trayIcon && SettingsCache::instance().updates().getCardDatabaseUpdateTrayNotification()) {
|
||||
trayIcon->showMessage(tr("Card database update started"),
|
||||
tr("Cockatrice is updating the card database in the background."));
|
||||
tr("Continuumatrice is updating the card database in the background."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1451,7 +1438,7 @@ void MainWindow::cardUpdateFinished(int exitCode, QProcess::ExitStatus exitStatu
|
|||
|
||||
bool MainWindow::requestCardUpdateRaise()
|
||||
{
|
||||
const QString serverName = scopedLocalServerName(QStringLiteral("CockatriceOracleRaise"));
|
||||
const QString serverName = scopedLocalServerName(QStringLiteral("ContinuumatriceOracleRaise"));
|
||||
QLocalSocket socket;
|
||||
socket.connectToServer(serverName);
|
||||
if (!socket.waitForConnected(250)) {
|
||||
|
|
@ -1469,7 +1456,7 @@ void MainWindow::announceCardUpdateRunning()
|
|||
statusBar()->showMessage(tr("Card database update running."));
|
||||
} else if (trayIcon && SettingsCache::instance().updates().getCardDatabaseUpdateTrayNotification()) {
|
||||
trayIcon->showMessage(tr("Card database update running"),
|
||||
tr("Cockatrice is updating the card database in the background."));
|
||||
tr("Continuumatrice is updating the card database in the background."));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1485,9 +1472,8 @@ void MainWindow::notifyCardUpdateFinishedFromTray(bool success)
|
|||
|
||||
void MainWindow::actCheckServerUpdates()
|
||||
{
|
||||
auto hps = new HandlePublicServers(this);
|
||||
hps->downloadPublicServers();
|
||||
connect(hps, &HandlePublicServers::sigPublicServersDownloadedSuccessfully, [=]() { hps->deleteLater(); });
|
||||
// Upstream Cockatrice public servers are not Continuumatrice game servers.
|
||||
qCInfo(MainWindowLog) << "Upstream public server discovery disabled in Continuumatrice.";
|
||||
}
|
||||
|
||||
void MainWindow::actCheckClientUpdates()
|
||||
|
|
@ -1507,12 +1493,8 @@ void MainWindow::checkClientUpdatesFinished(bool needToUpdate, bool /* isCompati
|
|||
|
||||
void MainWindow::actCheckCommanderBracketDefinitionUpdates()
|
||||
{
|
||||
auto *handler = new HandleCommanderBrackets(this);
|
||||
|
||||
connect(handler, &HandleCommanderBrackets::sigBracketDefinitionsDownloaded, this,
|
||||
[]() { qDebug() << "Bracket definitions loaded"; });
|
||||
|
||||
handler->downloadBracketDefinitions();
|
||||
// Commander brackets are a Magic: The Gathering system, not Continuum Universes.
|
||||
qCInfo(MainWindowLog) << "Upstream Commander bracket updates disabled in Continuumatrice.";
|
||||
}
|
||||
|
||||
void MainWindow::refreshShortcuts()
|
||||
|
|
|
|||
|
|
@ -224,11 +224,11 @@ public:
|
|||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
void setConnectTo(QString url)
|
||||
{
|
||||
connectTo = QUrl(QString("cockatrice://%1").arg(url));
|
||||
connectTo = QUrl(QString("continuumatrice://%1").arg(url));
|
||||
}
|
||||
// When set, the window's own startup connection (--connect or auto-connect
|
||||
// on first activation) is skipped. Used for activation launches: the intent
|
||||
// chain triggered by a cockatrice:// URL owns the connection, and letting
|
||||
// chain triggered by a continuumatrice:// URL owns the connection, and letting
|
||||
// auto-connect race against it caused two connectToServer calls to tear
|
||||
// each other down. onUrlChainFinished() clears this and retries the startup
|
||||
// connection when the link's chain ended without connecting.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue