Compare commits

...

12 Commits

Author SHA1 Message Date
dependabot[bot]
b86853b65c Bump actions/cache from 4 to 5 (#6496) 2026-01-05 21:18:21 +01:00
RickyRister
192dac0396 [DeckListModel] Consolidate methods and signals for card change (#6466) 2026-01-05 18:28:59 +01:00
RickyRister
85c9d8a9ff [DeckEditor] Fix tokens being added to maindeck (#6495) 2026-01-05 01:18:38 -08:00
RickyRister
ee2699413c [TabDeckEditor] Make card database a dock widget (#6472)
* [TabDeckEditor] Make card database a dock widget

* delete eventFilter implementation in abstract
2026-01-05 00:06:22 -08:00
RickyRister
d50297bbe6 [AnalyticsPanel] Use cogwheel icon for configure button (#6494) 2026-01-05 00:03:22 -08:00
RickyRister
489ce416c3 [VDS] Add search query option for comments (#6477) 2026-01-05 08:31:10 +01:00
RickyRister
731c487ccb [ServerGame] null check participant in getPlayer (#6493) 2026-01-05 01:43:40 -05:00
RickyRister
2d5e8deb75 [Server_AbstractParticipant] Rename bool getters (#6492)
* [Server_AbstractParticipant] Rename bool getters

* reformat
2026-01-05 00:34:32 -05:00
RickyRister
746f2af044 [DeckListModel] optimize by iterating over cardNodes instead of ExactCards (#6485)
* [DeckListModel] optimize by iterating over cardNodes instead of ExactCards

* fix build failure

* another optimization

* fix build failure
2026-01-03 19:19:04 -08:00
RickyRister
f16c552d97 [PrintingSelector] Don't refresh display if "bump cards to top" is off (#6486) 2026-01-04 01:08:39 +01:00
Bruno Alexandre Rosa
72a85b58cf ci: make fat qt libs thin (#6281)
* ci: strip fat qt binaries

* parallelize

* cache thin qt

* print libs

* change qt install dir in the action

* move qt install logic to separate job

* lookup only

* debug: show contents of QTDIR

* enableCrossOsArchive also when saving

* check one dir up

* change install dir

* keep debugging

* try deleting cache

* force delete cache

* pass gh_token

* pass missing params

* use api

* change cache key, disable cross os archive

* move job directly to steps

* add comments

* set cache param directly

* address comments

* fixup

* Update .ci/thin_macos_qtlib.sh

* resolve qt version

* move resolution to separate script

* use single line for run:

* improve error handling in new scripts

---------

Co-authored-by: ebbit1q <ebbit1q@gmail.com>
2026-01-04 01:00:05 +01:00
transifex-integration[bot]
b88a98b09a Translate cockatrice/cockatrice_en@source.ts in fr (#6488)
100% translated source file: 'cockatrice/cockatrice_en@source.ts'
on 'fr'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2026-01-03 18:58:59 +01:00
33 changed files with 510 additions and 204 deletions

View File

@@ -156,6 +156,18 @@ function ccachestatsverbose() {
# Compile
if [[ $RUNNER_OS == macOS ]]; then
# QTDIR is needed for macOS since we actually only use the cached thin Qt binaries instead of the install-qt-action,
# which sets a few environment variables
if QTDIR=$(find "$GITHUB_WORKSPACE/Qt" -depth -maxdepth 2 -name macos -type d -print -quit); then
echo "found QTDIR at $QTDIR"
else
echo "could not find QTDIR!"
exit 2
fi
# the qtdir is located at Qt/[qtversion]/macos
# we use find to get the first subfolder with the name "macos"
# this works independent of the qt version as there should be only one version installed on the runner at a time
export QTDIR
if [[ $TARGET_MACOS_VERSION ]]; then
# CMAKE_OSX_DEPLOYMENT_TARGET is a vanilla cmake flag needed to compile to target macOS version

View File

@@ -0,0 +1,40 @@
#!/bin/bash
# This script is used to resolve the latest patch version of Qt using aqtinstall.
# It interprets wildcards to get the latest patch version. E.g. "6.6.*" -> "6.6.3".
# This script is meant to be used by the ci enironment.
# It uses the runner's GITHUB_OUTPUT env variable.
# Usage example: .ci/resolve_latest_aqt_qt_version.sh "6.6.*"
qt_spec=$1
if [[ ! $qt_spec ]]; then
echo "usage: $0 [version]"
exit 2
fi
# If version is already specific (no wildcard), use it as-is
if [[ $qt_spec != *"*" ]]; then
echo "version $qt_spec is already resolved"
echo "version=$qt_spec" >> "$GITHUB_OUTPUT"
exit 0
fi
if ! hash aqt; then
echo "aqt could not be found, has aqtinstall been installed?"
exit 2
fi
# Resolve latest patch
if ! qt_resolved=$(aqt list-qt mac desktop --spec "$qt_spec" --latest-version); then
exit 1
fi
echo "resolved $qt_spec to $qt_resolved"
if [[ ! $qt_resolved ]]; then
echo "Error: Could not resolve Qt version for $qt_spec"
exit 1
fi
echo "version=$qt_resolved" >> "$GITHUB_OUTPUT"

25
.ci/thin_macos_qtlib.sh Executable file
View File

@@ -0,0 +1,25 @@
#!/bin/bash
# The macos binaries from aqt are fat (universal), so we thin them to the target architecture to reduce the size of
# the packages and caches using lipo.
# This script is meant to be used by the ci enironment on macos runners only.
# It uses the runner's GITHUB_WORKSPACE env variable.
arch=$(uname -m)
nproc=$(sysctl -n hw.ncpu)
function thin() {
local libfile=$1
if [[ $(file -b --mime-type "$libfile") == application/x-mach-binary* ]]; then
echo "Processing $libfile"
lipo "$libfile" -thin "$arch" -output "$libfile"
fi
return 0
}
export -f thin # export to allow use in xargs
export arch
set -eo pipefail
echo "::group::Thinning Qt libraries to $arch using $nproc cores"
find "$GITHUB_WORKSPACE/Qt" -type f -print0 | xargs -0 -n1 -P"$nproc" -I{} bash -c "thin '{}'"
echo "::endgroup::"

View File

@@ -262,7 +262,6 @@ jobs:
qt_version: 6.6.*
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cache_qt: false # qt caches take too much space for macOS (1.1Gi)
cmake_generator: Ninja
use_ccache: 1
@@ -278,7 +277,6 @@ jobs:
qt_version: 6.6.*
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cache_qt: false
cmake_generator: Ninja
use_ccache: 1
@@ -294,7 +292,6 @@ jobs:
qt_version: 6.6.*
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cache_qt: false
cmake_generator: Ninja
use_ccache: 1
@@ -307,7 +304,6 @@ jobs:
qt_version: 6.6.*
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cache_qt: false
cmake_generator: Ninja
use_ccache: 1
@@ -320,7 +316,6 @@ jobs:
artifact_name: Windows7-installer
qt_version: 5.15.*
qt_arch: win64_msvc2019_64
cache_qt: true
cmake_generator: "Visual Studio 17 2022"
cmake_generator_platform: x64
@@ -334,7 +329,6 @@ jobs:
qt_version: 6.6.*
qt_arch: win64_msvc2019_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cache_qt: true
cmake_generator: "Visual Studio 17 2022"
cmake_generator_platform: x64
@@ -375,13 +369,57 @@ jobs:
key: ccache-${{matrix.runner}}-${{matrix.soc}}-${{matrix.type}}-${{env.BRANCH_NAME}}
restore-keys: ccache-${{matrix.runner}}-${{matrix.soc}}-${{matrix.type}}-
- name: Install Qt ${{matrix.qt_version}}
- name: Install aqtinstall
if: matrix.os == 'macOS'
run: pipx install aqtinstall
# Checking if there's a newer, uncached version of Qt available to install via aqtinstall
- name: Resolve latest Qt patch version
if: matrix.os == 'macOS'
id: resolve_qt_version
shell: bash
# Ouputs the version of Qt to install via aqtinstall
run: .ci/resolve_latest_aqt_qt_version.sh "${{matrix.qt_version}}"
- name: Restore thin Qt ${{ steps.resolve_qt_version.outputs.version }} libraries (${{ matrix.soc }} macOS)
if: matrix.os == 'macOS'
id: restore_qt
uses: actions/cache/restore@v5
with:
path: ${{ github.workspace }}/Qt
key: thin-qt-macos-${{ matrix.soc }}-${{ steps.resolve_qt_version.outputs.version }}
# Using jurplel/install-qt-action to install Qt without using brew
# qt build using vcpkg either just fails or takes too long to build
- name: Install fat Qt ${{ steps.resolve_qt_version.outputs.version }} (${{ matrix.soc }} macOS)
if: matrix.os == 'macOS' && steps.restore_qt.outputs.cache-hit != 'true'
uses: jurplel/install-qt-action@v4
with:
cache: false
version: ${{ steps.resolve_qt_version.outputs.version }}
arch: ${{matrix.qt_arch}}
modules: ${{matrix.qt_modules}}
dir: ${{github.workspace}}
- name: Thin Qt libraries (${{ matrix.soc }} macOS)
if: matrix.os == 'macOS' && steps.restore_qt.outputs.cache-hit != 'true'
run: .ci/thin_macos_qtlib.sh
- name: Cache thin Qt libraries (${{ matrix.soc }} macOS)
if: matrix.os == 'macOS' && steps.restore_qt.outputs.cache-hit != 'true'
uses: actions/cache/save@v5
with:
path: ${{ github.workspace }}/Qt
key: thin-qt-macos-${{ matrix.soc }}-${{ steps.resolve_qt_version.outputs.version }}
- name: Install Qt ${{matrix.qt_version}} (Windows)
if: matrix.os == 'Windows'
uses: jurplel/install-qt-action@v4
with:
version: ${{matrix.qt_version}}
arch: ${{matrix.qt_arch}}
modules: ${{matrix.qt_modules}}
cache: ${{matrix.cache_qt}}
cache: true
- name: Setup vcpkg cache
id: vcpkg-cache

View File

@@ -170,6 +170,7 @@ set(cockatrice_SOURCES
src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_total_widget.cpp
src/interface/widgets/deck_analytics/analyzer_modules/mana_curve/mana_curve_category_widget.cpp
src/interface/widgets/deck_editor/deck_list_history_manager_widget.cpp
src/interface/widgets/deck_editor/deck_editor_card_database_dock_widget.cpp
src/interface/widgets/deck_editor/deck_editor_card_info_dock_widget.cpp
src/interface/widgets/deck_editor/deck_editor_database_display_widget.cpp
src/interface/widgets/deck_editor/deck_editor_deck_dock_widget.cpp

View File

@@ -29,6 +29,11 @@ searches are case insensitive.
<dt><u>F</u>ormat:</dt>
<dd>[f:standard](#f:standard) <small>(Any deck with format set to standard)</small></dd>
<dt><u>C</u>omments:</dt>
<dd>[c:good](#c:good) <small>(Any deck with comments containing the word good)</small></dd>
<dd>[c:good c:deck](#c:good c:deck) <small>(Any deck with comments containing the words good and deck)</small></dd>
<dd>[c:"good deck"](#c:%22good deck%22) <small>(Any deck with comments containing the exact phrase "good deck")</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

@@ -13,7 +13,7 @@ QueryPartList <- ComplexQueryPart ( ws ("AND" ws)? ComplexQueryPart)* ws*
ComplexQueryPart <- SomewhatComplexQueryPart ws "OR" ws ComplexQueryPart / SomewhatComplexQueryPart
SomewhatComplexQueryPart <- [(] QueryPartList [)] / QueryPart
QueryPart <- NotQuery / DeckContentQuery / DeckNameQuery / FileNameQuery / PathQuery / FormatQuery / GenericQuery
QueryPart <- NotQuery / DeckContentQuery / DeckNameQuery / FileNameQuery / PathQuery / FormatQuery / CommentQuery / GenericQuery
NotQuery <- ('NOT' ws/'-') SomewhatComplexQueryPart
@@ -25,6 +25,7 @@ DeckNameQuery <- ([Dd] 'eck')? [Nn] 'ame'? [:] String
FileNameQuery <- [Ff] ([Nn] / 'ile' ([Nn] 'ame')?) [:] String
PathQuery <- [Pp] 'ath'? [:] String
FormatQuery <- [Ff] 'ormat'? [:] String
CommentQuery <- [Cc] ('omment' 's'?)? [:] String
GenericQuery <- String
@@ -166,6 +167,14 @@ static void setupParserRules()
};
};
search["CommentQuery"] = [](const peg::SemanticValues &sv) -> DeckFilter {
auto value = std::any_cast<QString>(sv[0]);
return [=](const DeckPreviewWidget *deck, const ExtraDeckSearchInfo &) {
auto comments = deck->deckLoader->getDeck().deckList.getComments();
return comments.contains(value, 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

@@ -2,6 +2,7 @@
#include "card_group_display_widgets/flat_card_group_display_widget.h"
#include "card_group_display_widgets/overlapped_card_group_display_widget.h"
#include "libcockatrice/card/database/card_database_manager.h"
#include <QResizeEvent>
#include <libcockatrice/models/deck_list/deck_list_model.h>
@@ -230,10 +231,13 @@ QList<QString> DeckCardZoneDisplayWidget::getGroupCriteriaValueList()
{
QList<QString> groupCriteriaValues;
QList<ExactCard> cardsInZone = deckListModel->getCardsForZone(zoneName);
QList<const DecklistCardNode *> nodes = deckListModel->getCardNodesForZone(zoneName);
for (const ExactCard &cardInZone : cardsInZone) {
groupCriteriaValues.append(cardInZone.getInfo().getProperty(activeGroupCriteria));
for (auto node : nodes) {
CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(node->getName());
if (info) {
groupCriteriaValues.append(info->getProperty(activeGroupCriteria));
}
}
groupCriteriaValues.removeDuplicates();

View File

@@ -19,7 +19,8 @@ AbstractAnalyticsPanelWidget::AbstractAnalyticsPanelWidget(QWidget *parent, Deck
bannerAndSettingsLayout->addWidget(bannerWidget, 1);
// config button
configureButton = new QPushButton(tr("Configure"), this);
configureButton = new QPushButton(this);
configureButton->setIcon(QPixmap("theme:icons/cogwheel"));
configureButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
connect(configureButton, &QPushButton::clicked, this, &AbstractAnalyticsPanelWidget::applyConfigFromDialog);
bannerAndSettingsLayout->addWidget(configureButton, 0);

View File

@@ -170,7 +170,7 @@ void DrawProbabilityWidget::updateFilterOptions()
const auto nodes = analyzer->getModel()->getCardNodes();
for (auto *node : nodes) {
CardInfoPtr info = CardDatabaseManager::query()->getCard({node->getName()}).getCardPtr();
CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(node->getName());
if (!info) {
continue;
}

View File

@@ -12,89 +12,98 @@ DeckListStatisticsAnalyzer::DeckListStatisticsAnalyzer(QObject *parent,
DeckListStatisticsAnalyzerConfig _config)
: QObject(parent), model(_model), config(_config)
{
connect(model, &DeckListModel::dataChanged, this, &DeckListStatisticsAnalyzer::analyze);
connect(model, &DeckListModel::cardsChanged, this, &DeckListStatisticsAnalyzer::analyze);
}
void DeckListStatisticsAnalyzer::analyze()
{
clearData();
QList<ExactCard> cards = model->getCards();
QList<const DecklistCardNode *> nodes = model->getCardNodes();
for (auto card : cards) {
auto info = card.getInfo();
const int cmc = info.getCmc().toInt();
for (auto node : nodes) {
CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(node->getName());
if (!info) {
continue;
}
const int amount = node->getNumber();
QStringList copiesOfName;
for (int i = 0; i < amount; i++) {
copiesOfName.append(node->getName());
}
// Convert once
QStringList types = info.getMainCardType().split(' ');
QStringList subtypes = info.getCardType().split('-').last().split(" ");
QString colors = info.getColors();
int power = info.getPowTough().split("/").first().toInt();
int toughness = info.getPowTough().split("/").last().toInt();
const int cmc = info->getCmc().toInt();
QStringList types = info->getMainCardType().split(' ');
QStringList subtypes = info->getCardType().split('-').last().split(" ");
QString colors = info->getColors();
int power = info->getPowTough().split("/").first().toInt();
int toughness = info->getPowTough().split("/").last().toInt();
// For each copy of card
// ---------------- Mana Curve ----------------
if (config.computeManaCurve) {
manaCurveMap[cmc]++;
manaCurveMap[cmc] += amount;
}
// per-type curve
for (auto &t : types) {
manaCurveByType[t][cmc]++;
manaCurveCardsByType[t][cmc].append(info.getName());
manaCurveByType[t][cmc] += amount;
manaCurveCardsByType[t][cmc] << copiesOfName;
}
// Per-subtype curve
for (auto &st : subtypes) {
manaCurveBySubtype[st][cmc]++;
manaCurveCardsBySubtype[st][cmc].append(info.getName());
manaCurveBySubtype[st][cmc] += amount;
manaCurveCardsBySubtype[st][cmc] << copiesOfName;
}
// per-color curve
for (auto &c : colors) {
manaCurveByColor[c][cmc]++;
manaCurveCardsByColor[c][cmc].append(info.getName());
manaCurveByColor[c][cmc] += amount;
manaCurveCardsByColor[c][cmc] << copiesOfName;
}
// Power/toughness
manaCurveByPower[QString::number(power)][cmc]++;
manaCurveCardsByPower[QString::number(power)][cmc].append(info.getName());
manaCurveByToughness[QString::number(toughness)][cmc]++;
manaCurveCardsByToughness[QString::number(toughness)][cmc].append(info.getName());
manaCurveByPower[QString::number(power)][cmc] += amount;
manaCurveCardsByPower[QString::number(power)][cmc] << copiesOfName;
manaCurveByToughness[QString::number(toughness)][cmc] += amount;
manaCurveCardsByToughness[QString::number(toughness)][cmc] << copiesOfName;
// ========== Category Counts ===========
for (auto &t : types) {
typeCount[t]++;
typeCount[t] += amount;
}
for (auto &st : subtypes) {
subtypeCount[st]++;
subtypeCount[st] += amount;
}
for (auto &c : colors) {
colorCount[c]++;
colorCount[c] += amount;
}
manaValueCount[cmc]++;
manaValueCount[cmc] += amount;
// ---------------- Mana Base ----------------
if (config.computeManaBase) {
auto prod = determineManaProduction(info.getText());
auto prod = determineManaProduction(info->getText());
for (auto it = prod.begin(); it != prod.end(); ++it) {
if (it.value() > 0) {
productionPipCount[it.key()] += it.value();
productionCardCount[it.key()]++;
productionPipCount[it.key()] += it.value() * amount;
productionCardCount[it.key()] += amount;
}
manaBaseMap[it.key()] += it.value();
manaBaseMap[it.key()] += it.value() * amount;
}
}
// ---------------- Devotion ----------------
if (config.computeDevotion) {
auto devo = countManaSymbols(info.getManaCost());
auto devo = countManaSymbols(info->getManaCost());
for (auto &d : devo) {
if (d.second > 0) {
devotionPipCount[QString(d.first)] += d.second;
devotionCardCount[QString(d.first)]++;
devotionPipCount[QString(d.first)] += d.second * amount;
devotionCardCount[QString(d.first)] += amount;
}
manaDevotionMap[d.first] += d.second;
manaDevotionMap[d.first] += d.second * amount;
}
}
}

View File

@@ -0,0 +1,65 @@
#include "deck_editor_card_database_dock_widget.h"
DeckEditorCardDatabaseDockWidget::DeckEditorCardDatabaseDockWidget(AbstractTabDeckEditor *parent) : QDockWidget(parent)
{
setObjectName("databaseDisplayDock");
setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
createDatabaseDisplayDock(parent);
retranslateUi();
}
void DeckEditorCardDatabaseDockWidget::createDatabaseDisplayDock(AbstractTabDeckEditor *deckEditor)
{
databaseDisplayWidget = new DeckEditorDatabaseDisplayWidget(this, deckEditor);
auto *frame = new QVBoxLayout;
frame->setObjectName("databaseDisplayFrame");
frame->addWidget(databaseDisplayWidget);
auto *dockContents = new QWidget();
dockContents->setObjectName("databaseDisplayDockContents");
dockContents->setLayout(frame);
setWidget(dockContents);
installEventFilter(deckEditor);
connect(this, &QDockWidget::topLevelChanged, deckEditor, &AbstractTabDeckEditor::dockTopLevelChanged);
// connect signals
connect(databaseDisplayWidget, &DeckEditorDatabaseDisplayWidget::cardChanged, deckEditor,
&AbstractTabDeckEditor::updateCard);
connect(databaseDisplayWidget, &DeckEditorDatabaseDisplayWidget::addCardToMainDeck, deckEditor,
&AbstractTabDeckEditor::actAddCard);
connect(databaseDisplayWidget, &DeckEditorDatabaseDisplayWidget::addCardToSideboard, deckEditor,
&AbstractTabDeckEditor::actAddCardToSideboard);
connect(databaseDisplayWidget, &DeckEditorDatabaseDisplayWidget::decrementCardFromMainDeck, deckEditor,
&AbstractTabDeckEditor::actDecrementCard);
connect(databaseDisplayWidget, &DeckEditorDatabaseDisplayWidget::decrementCardFromSideboard, deckEditor,
&AbstractTabDeckEditor::actDecrementCardFromSideboard);
}
CardDatabase *DeckEditorCardDatabaseDockWidget::getDatabase() const
{
return databaseDisplayWidget->databaseModel->getDatabase();
}
void DeckEditorCardDatabaseDockWidget::retranslateUi()
{
setWindowTitle(tr("Card Database"));
}
void DeckEditorCardDatabaseDockWidget::setFilterTree(FilterTree *filterTree)
{
databaseDisplayWidget->setFilterTree(filterTree);
}
void DeckEditorCardDatabaseDockWidget::clearAllDatabaseFilters()
{
databaseDisplayWidget->clearAllDatabaseFilters();
}
void DeckEditorCardDatabaseDockWidget::highlightAllSearchEdit()
{
databaseDisplayWidget->searchEdit->setSelection(0, databaseDisplayWidget->searchEdit->text().length());
}

View File

@@ -0,0 +1,32 @@
#ifndef COCKATRICE_DECK_EDITOR_CARD_DATABASE_DOCK_WIDGET_H
#define COCKATRICE_DECK_EDITOR_CARD_DATABASE_DOCK_WIDGET_H
#include "../../../interface/widgets/tabs/abstract_tab_deck_editor.h"
#include <QDockWidget>
class AbstractTabDeckEditor;
class CardDatabase;
class DeckEditorDatabaseDisplayWidget;
class FilterTree;
class DeckEditorCardDatabaseDockWidget : public QDockWidget
{
public:
explicit DeckEditorCardDatabaseDockWidget(AbstractTabDeckEditor *parent);
DeckEditorDatabaseDisplayWidget *databaseDisplayWidget;
CardDatabase *getDatabase() const;
void setFilterTree(FilterTree *filterTree);
public slots:
void retranslateUi();
void clearAllDatabaseFilters();
void highlightAllSearchEdit();
private:
void createDatabaseDisplayDock(AbstractTabDeckEditor *deckEditor);
};
#endif // COCKATRICE_DECK_EDITOR_CARD_DATABASE_DOCK_WIDGET_H

View File

@@ -21,10 +21,10 @@ static bool canBeCommander(const CardInfo &cardInfo)
cardInfo.getText().contains("can be your commander", Qt::CaseInsensitive);
}
DeckEditorDatabaseDisplayWidget::DeckEditorDatabaseDisplayWidget(AbstractTabDeckEditor *parent)
: QWidget(parent), deckEditor(parent)
DeckEditorDatabaseDisplayWidget::DeckEditorDatabaseDisplayWidget(QWidget *parent, AbstractTabDeckEditor *deckEditor)
: QWidget(parent), deckEditor(deckEditor)
{
setObjectName("centralWidget");
setObjectName("databaseDisplayWidget");
centralFrame = new QVBoxLayout(this);
centralFrame->setObjectName("centralFrame");

View File

@@ -23,7 +23,7 @@ class DeckEditorDatabaseDisplayWidget : public QWidget
Q_OBJECT
public:
explicit DeckEditorDatabaseDisplayWidget(AbstractTabDeckEditor *parent);
explicit DeckEditorDatabaseDisplayWidget(QWidget *parent, AbstractTabDeckEditor *deckEditor);
AbstractTabDeckEditor *deckEditor;
SearchLineEdit *searchEdit;
CardDatabaseModel *databaseModel;

View File

@@ -154,7 +154,7 @@ void DeckEditorDeckDockWidget::createDeckDock()
bannerCardLabel->setText(tr("Banner Card"));
bannerCardLabel->setHidden(!SettingsCache::instance().getDeckEditorBannerCardComboBoxVisible());
bannerCardComboBox = new QComboBox(this);
connect(getModel(), &DeckListModel::dataChanged, this, [this]() {
connect(getModel(), &DeckListModel::cardNodesChanged, this, [this]() {
// Delay the update to avoid race conditions
QTimer::singleShot(100, this, &DeckEditorDeckDockWidget::updateBannerCardComboBox);
});
@@ -597,13 +597,12 @@ QModelIndexList DeckEditorDeckDockWidget::getSelectedCardNodeSourceIndices() con
return selectedRows;
}
void DeckEditorDeckDockWidget::actAddCard(const ExactCard &card, const QString &_zoneName)
void DeckEditorDeckDockWidget::actAddCard(const ExactCard &card, const QString &zoneName)
{
if (!card) {
return;
}
QString zoneName = card.getInfo().getIsToken() ? DECK_ZONE_TOKENS : _zoneName;
deckStateManager->addCard(card, zoneName);
}

View File

@@ -11,8 +11,7 @@ DeckStateManager::DeckStateManager(QObject *parent)
setModified(true);
emit historyChanged();
});
connect(deckListModel, &DeckListModel::rowsInserted, this, &DeckStateManager::uniqueCardsChanged);
connect(deckListModel, &DeckListModel::rowsRemoved, this, &DeckStateManager::uniqueCardsChanged);
connect(deckListModel, &DeckListModel::cardNodesChanged, this, &DeckStateManager::uniqueCardsChanged);
}
const DeckList &DeckStateManager::getDeckList() const
@@ -174,11 +173,13 @@ QModelIndex DeckStateManager::addCard(const ExactCard &card, const QString &zone
return {};
}
QString zone = card.getInfo().getIsToken() ? DECK_ZONE_TOKENS : zoneName;
QString reason = tr("Added (%1): %2 (%3) %4")
.arg(zoneName, card.getName(), card.getPrinting().getSet()->getCorrectedShortName(),
.arg(zone, card.getName(), card.getPrinting().getSet()->getCorrectedShortName(),
card.getPrinting().getProperty("num"));
QModelIndex idx = modifyDeck(reason, [&card, &zoneName](auto model) { return model->addCard(card, zoneName); });
QModelIndex idx = modifyDeck(reason, [&card, &zone](auto model) { return model->addCard(card, zone); });
if (idx.isValid()) {
emit focusIndexChanged(idx);

View File

@@ -152,7 +152,7 @@ static bool swapPrinting(DeckListModel *model, const QString &modifiedSet, const
return false;
}
int amount = model->data(idx.siblingAtColumn(DeckListModelColumns::CARD_AMOUNT), Qt::DisplayRole).toInt();
model->removeRow(idx.row(), idx.parent());
model->removeCardAtIndex(idx);
CardInfoPtr cardInfo = CardDatabaseManager::query()->getCardInfo(cardName);
PrintingInfo printing = CardDatabaseManager::query()->getSpecificPrinting(cardName, modifiedSet, "");
for (int i = 0; i < amount; i++) {

View File

@@ -152,7 +152,7 @@ static QModelIndex addAndReplacePrintings(DeckListModel *model,
// Check if a card without a providerId already exists in the deckModel and replace it, if so.
if (existing.isValid() && existing != newCardIndex && replaceProviderless) {
model->offsetCountAtIndex(newCardIndex, extraCopies);
model->removeRow(existing.row(), existing.parent());
model->removeCardAtIndex(existing);
}
// Set Index and Focus as if the user had just clicked the new card and modify the deckEditor saveState

View File

@@ -90,8 +90,10 @@ void PrintingSelector::retranslateUi()
void PrintingSelector::printingsInDeckChanged()
{
// Delay the update to avoid race conditions
QTimer::singleShot(100, this, &PrintingSelector::updateDisplay);
if (SettingsCache::instance().getBumpSetsWithCardsInDeckToTop()) {
// Delay the update to avoid race conditions
QTimer::singleShot(100, this, &PrintingSelector::updateDisplay);
}
}
/**

View File

@@ -55,7 +55,7 @@ AbstractTabDeckEditor::AbstractTabDeckEditor(TabSupervisor *_tabSupervisor) : Ta
deckStateManager = new DeckStateManager(this);
databaseDisplayDockWidget = new DeckEditorDatabaseDisplayWidget(this);
cardDatabaseDockWidget = new DeckEditorCardDatabaseDockWidget(this);
deckDockWidget = new DeckEditorDeckDockWidget(this);
cardInfoDockWidget = new DeckEditorCardInfoDockWidget(this);
filterDockWidget = new DeckEditorFilterDockWidget(this);
@@ -68,21 +68,9 @@ AbstractTabDeckEditor::AbstractTabDeckEditor(TabSupervisor *_tabSupervisor) : Ta
connect(deckStateManager, &DeckStateManager::isModifiedChanged, this, &AbstractTabDeckEditor::onDeckModified);
connect(deckDockWidget, &DeckEditorDeckDockWidget::selectedCardChanged, this, &AbstractTabDeckEditor::updateCard);
// Connect database display signals to this tab
connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::cardChanged, this,
&AbstractTabDeckEditor::updateCard);
connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::addCardToMainDeck, this,
&AbstractTabDeckEditor::actAddCard);
connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::addCardToSideboard, this,
&AbstractTabDeckEditor::actAddCardToSideboard);
connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::decrementCardFromMainDeck, this,
&AbstractTabDeckEditor::actDecrementCard);
connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::decrementCardFromSideboard, this,
&AbstractTabDeckEditor::actDecrementCardFromSideboard);
// Connect filter signals
connect(filterDockWidget, &DeckEditorFilterDockWidget::clearAllDatabaseFilters, databaseDisplayDockWidget,
&DeckEditorDatabaseDisplayWidget::clearAllDatabaseFilters);
connect(filterDockWidget, &DeckEditorFilterDockWidget::clearAllDatabaseFilters, cardDatabaseDockWidget,
&DeckEditorCardDatabaseDockWidget::clearAllDatabaseFilters);
// Connect shortcut changes
connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
@@ -122,7 +110,7 @@ void AbstractTabDeckEditor::addCardHelper(const ExactCard &card, const QString &
{
deckStateManager->addCard(card, zoneName);
databaseDisplayDockWidget->searchEdit->setSelection(0, databaseDisplayDockWidget->searchEdit->text().length());
cardDatabaseDockWidget->highlightAllSearchEdit();
}
/**
@@ -544,21 +532,21 @@ void AbstractTabDeckEditor::actExportDeckDecklistXyz()
/** @brief Analyzes the deck using DeckStats. */
void AbstractTabDeckEditor::actAnalyzeDeckDeckstats()
{
auto *interface = new DeckStatsInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(), this);
auto *interface = new DeckStatsInterface(*cardDatabaseDockWidget->getDatabase(), this);
interface->analyzeDeck(deckStateManager->getDeckList());
}
/** @brief Analyzes the deck using TappedOut. */
void AbstractTabDeckEditor::actAnalyzeDeckTappedout()
{
auto *interface = new TappedOutInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(), this);
auto *interface = new TappedOutInterface(*cardDatabaseDockWidget->getDatabase(), this);
interface->analyzeDeck(deckStateManager->getDeckList());
}
/** @brief Applies a new filter tree to the database display. */
void AbstractTabDeckEditor::filterTreeChanged(FilterTree *filterTree)
{
databaseDisplayDockWidget->setFilterTree(filterTree);
cardDatabaseDockWidget->setFilterTree(filterTree);
}
/**
@@ -571,43 +559,6 @@ void AbstractTabDeckEditor::closeEvent(QCloseEvent *event)
event->accept();
}
/**
* @brief Event filter for dock visibility and geometry changes.
* @param o Object sending the event.
* @param e Event.
* @return False always.
*/
bool AbstractTabDeckEditor::eventFilter(QObject *o, QEvent *e)
{
if (e->type() == QEvent::Close) {
if (o == cardInfoDockWidget) {
aCardInfoDockVisible->setChecked(false);
aCardInfoDockFloating->setEnabled(false);
} else if (o == deckDockWidget) {
aDeckDockVisible->setChecked(false);
aDeckDockFloating->setEnabled(false);
} else if (o == filterDockWidget) {
aFilterDockVisible->setChecked(false);
aFilterDockFloating->setEnabled(false);
} else if (o == printingSelectorDockWidget) {
aPrintingSelectorDockVisible->setChecked(false);
aPrintingSelectorDockFloating->setEnabled(false);
}
}
if (o == this && e->type() == QEvent::Hide) {
LayoutsSettings &layouts = SettingsCache::instance().layouts();
layouts.setDeckEditorLayoutState(saveState());
layouts.setDeckEditorGeometry(saveGeometry());
layouts.setDeckEditorCardSize(cardInfoDockWidget->size());
layouts.setDeckEditorFilterSize(filterDockWidget->size());
layouts.setDeckEditorDeckSize(deckDockWidget->size());
layouts.setDeckEditorPrintingSelectorSize(printingSelectorDockWidget->size());
}
return false;
}
/** @brief Shows a confirmation dialog before closing. */
bool AbstractTabDeckEditor::confirmClose()
{

View File

@@ -8,6 +8,7 @@
#ifndef TAB_GENERIC_DECK_EDITOR_H
#define TAB_GENERIC_DECK_EDITOR_H
#include "../interface/widgets/deck_editor/deck_editor_card_database_dock_widget.h"
#include "../interface/widgets/deck_editor/deck_editor_card_info_dock_widget.h"
#include "../interface/widgets/deck_editor/deck_editor_database_display_widget.h"
#include "../interface/widgets/deck_editor/deck_editor_deck_dock_widget.h"
@@ -27,7 +28,7 @@ class CardInfoFrameWidget;
class DeckLoader;
class DeckEditorMenu;
class DeckEditorCardInfoDockWidget;
class DeckEditorDatabaseDisplayWidget;
class DeckEditorCardDatabaseDockWidget;
class DeckEditorDeckDockWidget;
class DeckEditorFilterDockWidget;
class DeckEditorPrintingSelectorDockWidget;
@@ -126,7 +127,7 @@ public:
// UI Elements
DeckStateManager *deckStateManager;
DeckEditorMenu *deckMenu; ///< Menu for deck operations
DeckEditorDatabaseDisplayWidget *databaseDisplayDockWidget; ///< Database dock
DeckEditorCardDatabaseDockWidget *cardDatabaseDockWidget; ///< Database dock
DeckEditorCardInfoDockWidget *cardInfoDockWidget; ///< Card info dock
DeckEditorDeckDockWidget *deckDockWidget; ///< Deck dock
DeckEditorFilterDockWidget *filterDockWidget; ///< Filter dock
@@ -245,9 +246,6 @@ protected slots:
/** @brief Handles dock close events. */
void closeEvent(QCloseEvent *event) override;
/** @brief Event filter for dock state changes. */
bool eventFilter(QObject *o, QEvent *e) override;
/** @brief Slot triggered when a dock visibility changes. Pure virtual. */
virtual void dockVisibleTriggered() = 0;
@@ -295,11 +293,15 @@ protected:
virtual void openDeckFromFile(const QString &fileName, DeckOpenLocation deckOpenLocation);
// UI Menu Elements
QMenu *viewMenu, *cardInfoDockMenu, *deckDockMenu, *filterDockMenu, *printingSelectorDockMenu;
QMenu *viewMenu, *cardInfoDockMenu, *cardDatabaseDockMenu, *deckDockMenu, *filterDockMenu,
*printingSelectorDockMenu;
QAction *aResetLayout;
QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aDeckDockVisible, *aDeckDockFloating;
QAction *aFilterDockVisible, *aFilterDockFloating, *aPrintingSelectorDockVisible, *aPrintingSelectorDockFloating;
QAction *aCardInfoDockVisible, *aCardInfoDockFloating;
QAction *aCardDatabaseDockVisible, *aCardDatabaseDockFloating;
QAction *aDeckDockVisible, *aDeckDockFloating;
QAction *aFilterDockVisible, *aFilterDockFloating;
QAction *aPrintingSelectorDockVisible, *aPrintingSelectorDockFloating;
};
#endif // TAB_GENERIC_DECK_EDITOR_H

View File

@@ -55,6 +55,7 @@ void TabDeckEditor::createMenus()
viewMenu = new QMenu(this);
cardInfoDockMenu = viewMenu->addMenu(QString());
cardDatabaseDockMenu = viewMenu->addMenu(QString());
deckDockMenu = viewMenu->addMenu(QString());
filterDockMenu = viewMenu->addMenu(QString());
printingSelectorDockMenu = viewMenu->addMenu(QString());
@@ -67,6 +68,14 @@ void TabDeckEditor::createMenus()
aCardInfoDockFloating->setCheckable(true);
connect(aCardInfoDockFloating, &QAction::triggered, this, &TabDeckEditor::dockFloatingTriggered);
// Card Database dock
aCardDatabaseDockVisible = cardDatabaseDockMenu->addAction(QString());
aCardDatabaseDockVisible->setCheckable(true);
connect(aCardDatabaseDockVisible, &QAction::triggered, this, &TabDeckEditor::dockVisibleTriggered);
aCardDatabaseDockFloating = cardDatabaseDockMenu->addAction(QString());
aCardDatabaseDockFloating->setCheckable(true);
connect(aCardDatabaseDockFloating, &QAction::triggered, this, &TabDeckEditor::dockFloatingTriggered);
// Deck dock
aDeckDockVisible = deckDockMenu->addAction(QString());
aDeckDockVisible->setCheckable(true);
@@ -126,18 +135,22 @@ void TabDeckEditor::retranslateUi()
{
deckMenu->retranslateUi();
cardInfoDockWidget->retranslateUi();
cardDatabaseDockWidget->retranslateUi();
deckDockWidget->retranslateUi();
filterDockWidget->retranslateUi();
printingSelectorDockWidget->retranslateUi();
viewMenu->setTitle(tr("&View"));
cardInfoDockMenu->setTitle(tr("Card Info"));
cardDatabaseDockMenu->setTitle(tr("Card Database"));
deckDockMenu->setTitle(tr("Deck"));
filterDockMenu->setTitle(tr("Filters"));
printingSelectorDockMenu->setTitle(tr("Printing"));
aCardInfoDockVisible->setText(tr("Visible"));
aCardInfoDockFloating->setText(tr("Floating"));
aCardDatabaseDockVisible->setText(tr("Visible"));
aCardDatabaseDockFloating->setText(tr("Floating"));
aDeckDockVisible->setText(tr("Visible"));
aDeckDockFloating->setText(tr("Floating"));
aFilterDockVisible->setText(tr("Visible"));
@@ -171,7 +184,6 @@ void TabDeckEditor::showPrintingSelector()
void TabDeckEditor::loadLayout()
{
LayoutsSettings &layouts = SettingsCache::instance().layouts();
setCentralWidget(databaseDisplayDockWidget);
auto &layoutState = layouts.getDeckEditorLayoutState();
if (layoutState.isNull())
@@ -189,16 +201,19 @@ void TabDeckEditor::loadLayout()
}
aCardInfoDockVisible->setChecked(!cardInfoDockWidget->isHidden());
aCardDatabaseDockVisible->setChecked(!cardDatabaseDockWidget->isHidden());
aFilterDockVisible->setChecked(!filterDockWidget->isHidden());
aDeckDockVisible->setChecked(!deckDockWidget->isHidden());
aPrintingSelectorDockVisible->setChecked(!printingSelectorDockWidget->isHidden());
aCardInfoDockFloating->setEnabled(aCardInfoDockVisible->isChecked());
aCardDatabaseDockFloating->setChecked(aCardDatabaseDockVisible->isChecked());
aDeckDockFloating->setEnabled(aDeckDockVisible->isChecked());
aFilterDockFloating->setEnabled(aFilterDockVisible->isChecked());
aPrintingSelectorDockFloating->setEnabled(aPrintingSelectorDockVisible->isChecked());
aCardInfoDockFloating->setChecked(cardInfoDockWidget->isFloating());
aCardDatabaseDockFloating->setChecked(cardDatabaseDockWidget->isFloating());
aFilterDockFloating->setChecked(filterDockWidget->isFloating());
aDeckDockFloating->setChecked(deckDockWidget->isFloating());
aPrintingSelectorDockFloating->setChecked(printingSelectorDockWidget->isFloating());
@@ -226,27 +241,31 @@ void TabDeckEditor::restartLayout()
// Update menu checkboxes
aCardInfoDockVisible->setChecked(true);
aCardDatabaseDockVisible->setChecked(true);
aDeckDockVisible->setChecked(true);
aFilterDockVisible->setChecked(true);
aPrintingSelectorDockVisible->setChecked(!SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference());
aCardInfoDockFloating->setChecked(false);
aCardDatabaseDockFloating->setChecked(false);
aDeckDockFloating->setChecked(false);
aFilterDockFloating->setChecked(false);
aPrintingSelectorDockFloating->setChecked(false);
setCentralWidget(databaseDisplayDockWidget);
addDockWidget(Qt::LeftDockWidgetArea, cardDatabaseDockWidget);
addDockWidget(Qt::RightDockWidgetArea, deckDockWidget);
addDockWidget(Qt::RightDockWidgetArea, cardInfoDockWidget);
addDockWidget(Qt::RightDockWidgetArea, filterDockWidget);
addDockWidget(Qt::RightDockWidgetArea, printingSelectorDockWidget);
// Show/hide and reset floating
cardDatabaseDockWidget->setFloating(false);
deckDockWidget->setFloating(false);
cardInfoDockWidget->setFloating(false);
filterDockWidget->setFloating(false);
printingSelectorDockWidget->setFloating(false);
cardDatabaseDockWidget->setVisible(true);
deckDockWidget->setVisible(true);
cardInfoDockWidget->setVisible(true);
filterDockWidget->setVisible(true);
@@ -269,6 +288,9 @@ void TabDeckEditor::freeDocksSize()
deckDockWidget->setMinimumSize(minSize);
deckDockWidget->setMaximumSize(maxSize);
cardDatabaseDockWidget->setMinimumSize(minSize);
cardDatabaseDockWidget->setMaximumSize(maxSize);
cardInfoDockWidget->setMinimumSize(minSize);
cardInfoDockWidget->setMaximumSize(maxSize);
@@ -286,6 +308,9 @@ void TabDeckEditor::dockVisibleTriggered()
if (o == aCardInfoDockVisible) {
cardInfoDockWidget->setHidden(!aCardInfoDockVisible->isChecked());
aCardInfoDockFloating->setEnabled(aCardInfoDockVisible->isChecked());
} else if (o == aCardDatabaseDockVisible) {
cardDatabaseDockWidget->setHidden(!aCardDatabaseDockVisible->isChecked());
aCardDatabaseDockFloating->setEnabled(aCardDatabaseDockVisible->isChecked());
} else if (o == aDeckDockVisible) {
deckDockWidget->setHidden(!aDeckDockVisible->isChecked());
aDeckDockFloating->setEnabled(aDeckDockVisible->isChecked());
@@ -304,6 +329,8 @@ void TabDeckEditor::dockFloatingTriggered()
QObject *o = sender();
if (o == aCardInfoDockFloating)
cardInfoDockWidget->setFloating(aCardInfoDockFloating->isChecked());
else if (o == aCardDatabaseDockFloating)
cardDatabaseDockWidget->setFloating(aCardDatabaseDockFloating->isChecked());
else if (o == aDeckDockFloating)
deckDockWidget->setFloating(aDeckDockFloating->isChecked());
else if (o == aFilterDockFloating)
@@ -318,6 +345,8 @@ void TabDeckEditor::dockTopLevelChanged(bool topLevel)
QObject *o = sender();
if (o == cardInfoDockWidget)
aCardInfoDockFloating->setChecked(topLevel);
else if (o == aCardDatabaseDockFloating)
aCardDatabaseDockFloating->setChecked(topLevel);
else if (o == deckDockWidget)
aDeckDockFloating->setChecked(topLevel);
else if (o == filterDockWidget)
@@ -338,6 +367,9 @@ bool TabDeckEditor::eventFilter(QObject *o, QEvent *e)
if (o == cardInfoDockWidget) {
aCardInfoDockVisible->setChecked(false);
aCardInfoDockFloating->setEnabled(false);
} else if (o == cardDatabaseDockWidget) {
aCardDatabaseDockVisible->setChecked(false);
aCardDatabaseDockFloating->setEnabled(false);
} else if (o == deckDockWidget) {
aDeckDockVisible->setChecked(false);
aDeckDockFloating->setEnabled(false);

View File

@@ -6,9 +6,9 @@ TabVisualDatabaseDisplay::TabVisualDatabaseDisplay(TabSupervisor *_tabSupervisor
{
deckEditor = new TabDeckEditor(_tabSupervisor);
deckEditor->setHidden(true);
visualDatabaseDisplayWidget =
new VisualDatabaseDisplayWidget(this, deckEditor, deckEditor->databaseDisplayDockWidget->databaseModel,
deckEditor->databaseDisplayDockWidget->databaseDisplayModel);
visualDatabaseDisplayWidget = new VisualDatabaseDisplayWidget(
this, deckEditor, deckEditor->cardDatabaseDockWidget->databaseDisplayWidget->databaseModel,
deckEditor->cardDatabaseDockWidget->databaseDisplayWidget->databaseDisplayModel);
setCentralWidget(visualDatabaseDisplayWidget);

View File

@@ -50,7 +50,7 @@ TabDeckEditorVisual::TabDeckEditorVisual(TabSupervisor *_tabSupervisor) : Abstra
refreshShortcuts();
loadLayout();
databaseDisplayDockWidget->setHidden(true);
cardDatabaseDockWidget->setHidden(true);
}
/** @brief Creates the central frame containing the tab container. */
@@ -62,9 +62,9 @@ void TabDeckEditorVisual::createCentralFrame()
centralFrame = new QVBoxLayout;
centralWidget->setLayout(centralFrame);
tabContainer = new TabDeckEditorVisualTabWidget(centralWidget, this, deckStateManager->getModel(),
databaseDisplayDockWidget->databaseModel,
databaseDisplayDockWidget->databaseDisplayModel);
tabContainer = new TabDeckEditorVisualTabWidget(
centralWidget, this, deckStateManager->getModel(), cardDatabaseDockWidget->databaseDisplayWidget->databaseModel,
cardDatabaseDockWidget->databaseDisplayWidget->databaseDisplayModel);
connect(tabContainer, &TabDeckEditorVisualTabWidget::cardChanged, this,
&TabDeckEditorVisual::changeModelIndexAndCardInfo);

View File

@@ -74,25 +74,27 @@ VisualDatabaseDisplayWidget::VisualDatabaseDisplayWidget(QWidget *parent,
searchKeySignals.setObjectName("searchKeySignals");
connect(searchEdit, &SearchLineEdit::textChanged, this, &VisualDatabaseDisplayWidget::updateSearch);
connect(&searchKeySignals, &KeySignals::onEnter, deckEditor->databaseDisplayDockWidget,
DeckEditorDatabaseDisplayWidget *databaseDisplayWidget = deckEditor->cardDatabaseDockWidget->databaseDisplayWidget;
connect(&searchKeySignals, &KeySignals::onEnter, databaseDisplayWidget,
&DeckEditorDatabaseDisplayWidget::actAddCardToMainDeck);
connect(&searchKeySignals, &KeySignals::onCtrlAltEqual, deckEditor->databaseDisplayDockWidget,
connect(&searchKeySignals, &KeySignals::onCtrlAltEqual, databaseDisplayWidget,
&DeckEditorDatabaseDisplayWidget::actAddCardToMainDeck);
connect(&searchKeySignals, &KeySignals::onCtrlAltRBracket, deckEditor->databaseDisplayDockWidget,
connect(&searchKeySignals, &KeySignals::onCtrlAltRBracket, databaseDisplayWidget,
&DeckEditorDatabaseDisplayWidget::actAddCardToSideboard);
connect(&searchKeySignals, &KeySignals::onCtrlAltMinus, deckEditor->databaseDisplayDockWidget,
connect(&searchKeySignals, &KeySignals::onCtrlAltMinus, databaseDisplayWidget,
&DeckEditorDatabaseDisplayWidget::actDecrementCardFromMainDeck);
connect(&searchKeySignals, &KeySignals::onCtrlAltLBracket, deckEditor->databaseDisplayDockWidget,
connect(&searchKeySignals, &KeySignals::onCtrlAltLBracket, databaseDisplayWidget,
&DeckEditorDatabaseDisplayWidget::actDecrementCardFromSideboard);
connect(&searchKeySignals, &KeySignals::onCtrlAltEnter, deckEditor->databaseDisplayDockWidget,
connect(&searchKeySignals, &KeySignals::onCtrlAltEnter, databaseDisplayWidget,
&DeckEditorDatabaseDisplayWidget::actAddCardToSideboard);
connect(&searchKeySignals, &KeySignals::onCtrlEnter, deckEditor->databaseDisplayDockWidget,
connect(&searchKeySignals, &KeySignals::onCtrlEnter, databaseDisplayWidget,
&DeckEditorDatabaseDisplayWidget::actAddCardToSideboard);
connect(&searchKeySignals, &KeySignals::onCtrlC, deckEditor->databaseDisplayDockWidget,
connect(&searchKeySignals, &KeySignals::onCtrlC, databaseDisplayWidget,
&DeckEditorDatabaseDisplayWidget::copyDatabaseCellContents);
connect(help, &QAction::triggered, this, [this] { createSearchSyntaxHelpWindow(searchEdit); });
databaseView = deckEditor->databaseDisplayDockWidget->getDatabaseView();
databaseView = databaseDisplayWidget->getDatabaseView();
databaseView->setFocusProxy(searchEdit);
databaseView->setItemDelegate(nullptr);
databaseView->setVisible(false);

View File

@@ -81,13 +81,30 @@ void VisualDeckEditorSampleHandWidget::updateDisplay()
}
}
static QList<ExactCard> cardNodesToExactCards(QList<const DecklistCardNode *> nodes)
{
QList<ExactCard> cards;
for (auto node : nodes) {
ExactCard card = CardDatabaseManager::query()->getCard(node->toCardRef());
if (card) {
for (int k = 0; k < node->getNumber(); ++k) {
cards.append(card);
}
} else {
qDebug() << "Card not found in database!";
}
}
return cards;
}
QList<ExactCard> VisualDeckEditorSampleHandWidget::getRandomCards(int amountToGet)
{
QList<ExactCard> randomCards;
if (!deckListModel)
return randomCards;
QList<ExactCard> mainDeckCards = deckListModel->getCardsForZone(DECK_ZONE_MAIN);
QList<ExactCard> mainDeckCards = cardNodesToExactCards(deckListModel->getCardNodesForZone(DECK_ZONE_MAIN));
if (mainDeckCards.isEmpty())
return randomCards;

View File

@@ -154,7 +154,13 @@ You will not be able to manage printing preferences on a per-deck basis, or see
You will have to use the Set Manager, available through Card Database -&gt; Manage Sets.
Are you sure you would like to enable this feature?</source>
<translation>L&apos;activation de cette fonction désactivera l&apos;utilisation du sélecteur d&apos;impression.Vous ne pourrez plus gérer les préférences d&apos;impression pour chaque deck ni voir les impressions sélectionnées par d&apos;autres joueurs.Vous devrez utiliser le Gestionnaire d&apos;extensions, accessible via Base de données de cartes -&gt; Gérer les extensions.Êtes-vous sûr de vouloir activer cette fonction ?</translation>
<translation>L&apos;activation de cette fonction désactivera l&apos;utilisation du sélecteur d&apos;impression.
Vous ne pourrez plus gérer les préférences d&apos;impression pour chaque deck ni voir les impressions sélectionnées par d&apos;autres joueurs.
Vous devrez utiliser le Gestionnaire d&apos;extensions, accessible via Base de données de cartes -&gt; Gérer les extensions.
Êtes-vous sûr de vouloir activer cette fonction ?</translation>
</message>
<message>
<location filename="src/interface/widgets/dialogs/dlg_settings.cpp" line="668"/>
@@ -165,7 +171,13 @@ You can now choose printings on a per-deck basis in the Deck Editor and configur
You can also use the Set Manager to adjust custom sort order for printings in the Printing Selector (other sort orders like alphabetical or release date are available).
Are you sure you would like to disable this feature?</source>
<translation>Désactiver cette fonction activera le sélecteur d&apos;impression.Vous pouvez désormais choisir les impressions pour chaque deck dans l&apos;éditeur de deck et configurer l&apos;impression ajoutée par défaut en l&apos;épinglant dans le sélecteur d&apos;impression.Vous pouvez également utiliser le gestionnaire de d&apos;extension pour personnaliser l&apos;ordre de tri des impressions dans le sélecteur d&apos;impression (d&apos;autres ordres de tri, comme l&apos;ordre alphabétique ou la date de parution, sont disponibles).Êtes-vous sûr de vouloir désactiver cette fonction ?</translation>
<translation>Désactiver cette fonction activera le sélecteur d&apos;impression.
Vous pouvez désormais choisir les impressions pour chaque deck dans l&apos;éditeur de deck et configurer l&apos;impression ajoutée par défaut en l&apos;épinglant dans le sélecteur d&apos;impression.
Vous pouvez également utiliser le gestionnaire de d&apos;extension pour personnaliser l&apos;ordre de tri des impressions dans le sélecteur d&apos;impression (d&apos;autres ordres de tri, comme l&apos;ordre alphabétique ou la date de parution, sont disponibles).
Êtes-vous sûr de vouloir désactiver cette fonction ?</translation>
</message>
<message>
<location filename="src/interface/widgets/dialogs/dlg_settings.cpp" line="677"/>
@@ -2784,7 +2796,13 @@ https://archidekt.com/decks/9999999
https://deckstats.net/decks/99999/9999999-your-deck-name/en
https://moxfield.com/decks/XYZxx-XYZ99Yyy-xyzXzzz
https://tappedout.net/mtg-decks/your-deck-name/</source>
<translation>L&apos;URL fournie n&apos;est pas reconnue comme une URL de deck valide.Les URL de deck valides ressemblent à ceci :https://archidekt.com/decks/9999999https://deckstats.net/decks/99999/9999999-your-deck-name/enhttps://moxfield.com/decks/XYZxx-XYZ99Yyy-xyzXzzzhttps://tappedout.net/mtg-decks/your-deck-name/</translation>
<translation>L&apos;URL fournie n&apos;est pas reconnue comme une URL de deck valide.
Les URL de deck valides ressemblent à ceci:
https://archidekt.com/decks/9999999
https://deckstats.net/decks/99999/9999999-your-deck-name/en
https://moxfield.com/decks/XYZxx-XYZ99Yyy-xyzXzzz
https://tappedout.net/mtg-decks/your-deck-name/</translation>
</message>
</context>
<context>

View File

@@ -12,6 +12,15 @@ DeckListModel::DeckListModel(QObject *parent)
DeckListModel::DeckListModel(QObject *parent, const QSharedPointer<DeckList> &deckList) : DeckListModel(parent)
{
setDeckList(deckList);
// forward change signals
connect(this, &DeckListModel::cardAddedAt, this, &DeckListModel::cardsChanged);
connect(this, &DeckListModel::cardRemoved, this, &DeckListModel::cardsChanged);
connect(this, &DeckListModel::deckReplaced, this, &DeckListModel::cardsChanged);
connect(this, &DeckListModel::cardNodeAddedAt, this, &DeckListModel::cardNodesChanged);
connect(this, &DeckListModel::cardNodeRemoved, this, &DeckListModel::cardNodesChanged);
connect(this, &DeckListModel::deckReplaced, this, &DeckListModel::cardNodesChanged);
}
DeckListModel::~DeckListModel()
@@ -421,6 +430,7 @@ QModelIndex DeckListModel::addCard(const ExactCard &card, const QString &zoneNam
card.getName(), printingInfo.getUuid(), printingInfo.getProperty("num")));
const auto cardSetName = printingInfo.getSet().isNull() ? "" : printingInfo.getSet()->getCorrectedShortName();
bool cardNodeAdded = false;
if (!cardNode) {
// Determine the correct index
int insertRow = findSortedInsertRow(groupNode, cardInfo);
@@ -432,6 +442,8 @@ QModelIndex DeckListModel::addCard(const ExactCard &card, const QString &zoneNam
beginInsertRows(parentIndex, insertRow, insertRow);
cardNode = new DecklistModelCardNode(decklistCard, groupNode, insertRow);
endInsertRows();
cardNodeAdded = true;
} else {
cardNode->setNumber(cardNode->getNumber() + 1);
cardNode->setCardSetShortName(cardSetName);
@@ -444,6 +456,10 @@ QModelIndex DeckListModel::addCard(const ExactCard &card, const QString &zoneNam
emitRecursiveUpdates(parentIndex);
auto index = nodeToIndex(cardNode);
if (cardNodeAdded) {
emit cardNodeAddedAt(index);
}
emit cardAddedAt(index);
return index;
@@ -468,17 +484,42 @@ bool DeckListModel::offsetCountAtIndex(const QModelIndex &idx, int offset)
if (newCount <= 0) {
removeRow(idx.row(), idx.parent());
emit cardNodeRemoved();
} else {
setData(numberIndex, newCount, Qt::EditRole);
}
if (offset > 0) {
emit cardAddedAt(idx);
} else if (offset < 0) {
emit cardRemoved();
}
return true;
}
bool DeckListModel::removeCardAtIndex(const QModelIndex &idx)
{
if (!idx.isValid()) {
return false;
}
auto *node = static_cast<AbstractDecklistNode *>(idx.internalPointer());
auto *card = dynamic_cast<DecklistModelCardNode *>(node);
if (!card) {
return false;
}
bool success = removeRow(idx.row(), idx.parent());
if (success) {
emit cardRemoved();
}
return success;
}
int DeckListModel::findSortedInsertRow(const InnerDecklistNode *parent, const CardInfoPtr &cardInfo) const
{
if (!cardInfo) {
@@ -608,35 +649,6 @@ void DeckListModel::forEachCard(const std::function<void(InnerDecklistNode *, De
deckList->forEachCard(func);
}
static QList<ExactCard> cardNodesToExactCards(QList<const DecklistCardNode *> nodes)
{
QList<ExactCard> cards;
for (auto node : nodes) {
ExactCard card = CardDatabaseManager::query()->getCard(node->toCardRef());
if (card) {
for (int k = 0; k < node->getNumber(); ++k) {
cards.append(card);
}
} else {
qDebug() << "Card not found in database!";
}
}
return cards;
}
QList<ExactCard> DeckListModel::getCards() const
{
auto nodes = deckList->getCardNodes();
return cardNodesToExactCards(nodes);
}
QList<ExactCard> DeckListModel::getCardsForZone(const QString &zoneName) const
{
auto nodes = deckList->getCardNodes({zoneName});
return cardNodesToExactCards(nodes);
}
QList<const DecklistCardNode *> DeckListModel::getCardNodes() const
{
return deckList->getCardNodes();

View File

@@ -197,6 +197,12 @@ public:
* InnerDecklistNode containers and supports grouping, sorting, adding/removing
* cards, and printing decklists.
*
* Outside code should refrain from modifying the model with methods inherited from QAbstractItemModel, such as with
* `setData` or `removeRow`.
* Instead, use the custom methods on this class to modify the model, such as `addCard`, `offsetCountAtIndex`, or
* `removeCardAtIndex`.
* This ensures the custom signals for this class are correctly emitted.
*
* Signals:
* - deckHashChanged(): emitted when the deck contents change in a way that
* affects its hash.
@@ -231,6 +237,11 @@ signals:
*/
void deckHashChanged();
/**
* @brief Emitted whenever the cards in the deck changes. This includes when the deck is replaced.
*/
void cardsChanged();
/**
* @brief Emitted whenever a card is added to the deck, regardless of whether it's an entirely new card or an
* existing card that got incremented.
@@ -238,6 +249,28 @@ signals:
*/
void cardAddedAt(const QModelIndex &index);
/**
* @brief Emitted whenever a card is removed from the deck, regardless of whether a card node was removed or an
* existing card got decremented.
*/
void cardRemoved();
/**
* @brief Emitted whenever a card node is added or removed. This includes when the deck is replaced.
*/
void cardNodesChanged();
/**
* @brief Emitted whenever a new card node is added.
* @param index The index of the card node that got added.
*/
void cardNodeAddedAt(const QModelIndex &index);
/**
* @brief Emitted whenever a card node is removed.
*/
void cardNodeRemoved();
/**
* @brief Emitted whenever the deck in the model has been replaced with a new one
*/
@@ -311,6 +344,13 @@ public:
*/
bool offsetCountAtIndex(const QModelIndex &idx, int offset);
/**
* @brief Removes the card node at the index
* @param idx The index of a card node. No-ops if the index is invalid or not a card node.
* @return Whether the node was removed.
*/
bool removeCardAtIndex(const QModelIndex &idx);
/**
* @brief Removes all cards and resets the model.
*/
@@ -329,16 +369,6 @@ public:
*/
void forEachCard(const std::function<void(InnerDecklistNode *, DecklistCardNode *)> &func);
/**
* @brief Creates a list consisting of the entries of the model mapped into ExactCards (with each entry looked up
* in the card database).
* If a card node has number > 1, it will be added that many times to the list.
* If an entry's card is not found in the card database, that entry will be left out of the list.
* @return An ordered list of ExactCards
*/
[[nodiscard]] QList<ExactCard> getCards() const;
[[nodiscard]] QList<ExactCard> getCardsForZone(const QString &zoneName) const;
/**
* @brief Gets a list of all card nodes in the deck.
*/

View File

@@ -89,11 +89,11 @@ public:
{
return playerId;
}
bool getSpectator() const
bool isSpectator() const
{
return spectator;
}
bool getJudge() const
bool isJudge() const
{
return judge;
}

View File

@@ -1516,7 +1516,7 @@ Server_AbstractPlayer::cmdRevealCards(const Command_RevealCards &cmd, ResponseCo
zone->addWritePermission(cmd.player_id());
}
if (getJudge()) {
if (isJudge()) {
ges.setOverwriteOwnership(true);
}

View File

@@ -189,7 +189,7 @@ void Server_Game::pingClockTimeout()
if (participant == nullptr)
continue;
if (!participant->getSpectator()) {
if (!participant->isSpectator()) {
++playerCount;
}
@@ -200,7 +200,7 @@ void Server_Game::pingClockTimeout()
}
if ((participant->getPingTime() != -1) &&
(!participant->getSpectator() || participant->getPlayerId() == hostId)) {
(!participant->isSpectator() || participant->getPlayerId() == hostId)) {
allPlayersInactive = false;
}
}
@@ -222,7 +222,7 @@ QMap<int, Server_AbstractPlayer *> Server_Game::getPlayers() const // copies poi
QMutexLocker locker(&gameMutex);
for (int id : participants.keys()) {
auto *participant = participants[id];
if (!participant->getSpectator()) {
if (!participant->isSpectator()) {
players[id] = static_cast<Server_AbstractPlayer *>(participant);
}
}
@@ -232,7 +232,7 @@ QMap<int, Server_AbstractPlayer *> Server_Game::getPlayers() const // copies poi
Server_AbstractPlayer *Server_Game::getPlayer(int id) const
{
auto *participant = participants.value(id);
if (!participant->getSpectator()) {
if (participant && !participant->isSpectator()) {
return static_cast<Server_AbstractPlayer *>(participant);
} else {
return nullptr;
@@ -250,7 +250,7 @@ int Server_Game::getSpectatorCount() const
int result = 0;
for (Server_AbstractParticipant *participant : participants.values()) {
if (participant->getSpectator())
if (participant->isSpectator())
++result;
}
return result;
@@ -295,8 +295,8 @@ void Server_Game::sendGameStateToPlayers()
// send game state info to clients according to their role in the game
for (auto *participant : participants.values()) {
GameEventContainer *gec;
if (participant->getSpectator()) {
if (spectatorsSeeEverything || participant->getJudge()) {
if (participant->isSpectator()) {
if (spectatorsSeeEverything || participant->isJudge()) {
gec = prepareGameEvent(omniscientEvent, -1);
} else {
gec = prepareGameEvent(spectatorNormalEvent, -1);
@@ -527,7 +527,7 @@ void Server_Game::removeParticipant(Server_AbstractParticipant *participant, Eve
gameId, participant->getPlayerId());
participants.remove(participant->getPlayerId());
bool spectator = participant->getSpectator();
bool spectator = participant->isSpectator();
GameEventStorage ges;
if (!spectator) {
auto *player = static_cast<Server_AbstractPlayer *>(participant);
@@ -728,8 +728,8 @@ void Server_Game::createGameJoinedEvent(Server_AbstractParticipant *joiningParti
getInfo(*event1.mutable_game_info());
event1.set_host_id(hostId);
event1.set_player_id(joiningParticipant->getPlayerId());
event1.set_spectator(joiningParticipant->getSpectator());
event1.set_judge(joiningParticipant->getJudge());
event1.set_spectator(joiningParticipant->isSpectator());
event1.set_judge(joiningParticipant->isJudge());
event1.set_resuming(resuming);
if (resuming) {
const QStringList &allGameTypes = room->getGameTypes();
@@ -747,7 +747,7 @@ void Server_Game::createGameJoinedEvent(Server_AbstractParticipant *joiningParti
event2.set_active_player_id(activePlayer);
event2.set_active_phase(activePhase);
bool omniscient = joiningParticipant->getSpectator() && (spectatorsSeeEverything || joiningParticipant->getJudge());
bool omniscient = joiningParticipant->isSpectator() && (spectatorsSeeEverything || joiningParticipant->isJudge());
for (auto *participant : participants.values()) {
participant->getInfo(event2.add_player_list(), joiningParticipant, omniscient, true);
}
@@ -763,9 +763,8 @@ void Server_Game::sendGameEventContainer(GameEventContainer *cont,
cont->set_game_id(gameId);
for (auto *participant : participants.values()) {
const bool playerPrivate =
(participant->getPlayerId() == privatePlayerId) ||
(participant->getSpectator() && (spectatorsSeeEverything || participant->getJudge()));
const bool playerPrivate = (participant->getPlayerId() == privatePlayerId) ||
(participant->isSpectator() && (spectatorsSeeEverything || participant->isJudge()));
if ((recipients.testFlag(GameEventStorageItem::SendToPrivate) && playerPrivate) ||
(recipients.testFlag(GameEventStorageItem::SendToOthers) && !playerPrivate))
participant->sendGameEvent(*cont);