mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-29 09:22:18 -07:00
Address comments
This commit is contained in:
parent
1627f377ef
commit
6f631e0954
4 changed files with 216 additions and 218 deletions
|
|
@ -20,8 +20,8 @@ namespace
|
||||||
*/
|
*/
|
||||||
struct DeckScanResult
|
struct DeckScanResult
|
||||||
{
|
{
|
||||||
QList<DeckPreviewData> decks;
|
QList<DeckPreviewData> decks; ///< Deck rows in scan order.
|
||||||
QStringList folderPaths;
|
QStringList folderPaths; ///< Sorted list of subfolder paths.
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -30,8 +30,8 @@ struct DeckScanResult
|
||||||
*/
|
*/
|
||||||
struct DeckLoadResult
|
struct DeckLoadResult
|
||||||
{
|
{
|
||||||
LoadedDeck deck;
|
LoadedDeck deck; ///< The parsed deck.
|
||||||
QDateTime lastModified;
|
QDateTime lastModified; ///< File modification time at load.
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -49,6 +49,63 @@ QString relativePathFromDeckRoot(const QString &path, const QString &deckPath)
|
||||||
}
|
}
|
||||||
return relativePath;
|
return relativePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The path of \a filePath relative to \a deckPath, or the bare file name
|
||||||
|
* if \a filePath is not below \a deckPath.
|
||||||
|
*/
|
||||||
|
QString relativeFilePathFor(const QString &filePath, const QString &deckPath)
|
||||||
|
{
|
||||||
|
if (filePath.startsWith(deckPath)) {
|
||||||
|
return filePath.mid(deckPath.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
return QFileInfo(filePath).fileName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The directory of \a filePath relative to \a deckPath, or empty if the
|
||||||
|
* file sits directly in the deck root.
|
||||||
|
*/
|
||||||
|
QString folderPathFor(const QString &filePath, const QString &deckPath)
|
||||||
|
{
|
||||||
|
return relativePathFromDeckRoot(QFileInfo(filePath).absolutePath(), deckPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Scans a deck directory on a worker thread, returning discovered deck
|
||||||
|
* files and subfolder paths.
|
||||||
|
*/
|
||||||
|
DeckScanResult scanDeckDirectory(const QString &deckPath)
|
||||||
|
{
|
||||||
|
DeckScanResult result;
|
||||||
|
|
||||||
|
QDirIterator fileIt(deckPath, DeckLoader::ACCEPTED_FILE_EXTENSIONS, QDir::Files,
|
||||||
|
QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
||||||
|
while (fileIt.hasNext()) {
|
||||||
|
const QString filePath = fileIt.next();
|
||||||
|
DeckPreviewData data;
|
||||||
|
data.filePath = filePath;
|
||||||
|
data.relativeFilePath = relativeFilePathFor(filePath, deckPath);
|
||||||
|
data.folderPath = folderPathFor(filePath, deckPath);
|
||||||
|
data.lastModified = QFileInfo(filePath).lastModified();
|
||||||
|
result.decks.append(std::move(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
QSet<QString> seenFolders;
|
||||||
|
QDirIterator folderIt(deckPath, QDir::Dirs | QDir::NoDotAndDotDot,
|
||||||
|
QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
||||||
|
while (folderIt.hasNext()) {
|
||||||
|
const QString folderPath = relativePathFromDeckRoot(folderIt.next(), deckPath);
|
||||||
|
if (!folderPath.isEmpty() && !seenFolders.contains(folderPath)) {
|
||||||
|
seenFolders.insert(folderPath);
|
||||||
|
result.folderPaths.append(folderPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.folderPaths.sort();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
VisualDeckStorageModel::VisualDeckStorageModel(QObject *parent) : QAbstractListModel(parent)
|
VisualDeckStorageModel::VisualDeckStorageModel(QObject *parent) : QAbstractListModel(parent)
|
||||||
|
|
@ -57,16 +114,16 @@ VisualDeckStorageModel::VisualDeckStorageModel(QObject *parent) : QAbstractListM
|
||||||
|
|
||||||
int VisualDeckStorageModel::rowCount(const QModelIndex &parent) const
|
int VisualDeckStorageModel::rowCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
return parent.isValid() ? 0 : decks_.size();
|
return parent.isValid() ? 0 : decks.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant VisualDeckStorageModel::data(const QModelIndex &index, int role) const
|
QVariant VisualDeckStorageModel::data(const QModelIndex &index, int role) const
|
||||||
{
|
{
|
||||||
if (!index.isValid() || index.row() < 0 || index.row() >= decks_.size()) {
|
if (!index.isValid() || index.row() < 0 || index.row() >= decks.size()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const DeckPreviewData &data = decks_.at(index.row());
|
const DeckPreviewData &data = decks.at(index.row());
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
case VisualDeckStorageRoles::DisplayNameRole:
|
case VisualDeckStorageRoles::DisplayNameRole:
|
||||||
|
|
@ -86,21 +143,21 @@ QVariant VisualDeckStorageModel::data(const QModelIndex &index, int role) const
|
||||||
case VisualDeckStorageRoles::LastLoadedRole:
|
case VisualDeckStorageRoles::LastLoadedRole:
|
||||||
return data.lastLoaded;
|
return data.lastLoaded;
|
||||||
case VisualDeckStorageRoles::BannerCardNameRole:
|
case VisualDeckStorageRoles::BannerCardNameRole:
|
||||||
return data.bannerCardName;
|
return data.bannerCard.name;
|
||||||
case VisualDeckStorageRoles::BannerCardProviderIdRole:
|
case VisualDeckStorageRoles::BannerCardProviderIdRole:
|
||||||
return data.bannerCardProviderId;
|
return data.bannerCard.providerId;
|
||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisualDeckStorageModel::setDeckPath(const QString &deckPath)
|
void VisualDeckStorageModel::setDeckPath(const QString &path)
|
||||||
{
|
{
|
||||||
QString cleanedPath = QDir::cleanPath(deckPath);
|
QString cleanedPath = QDir::cleanPath(path);
|
||||||
if (cleanedPath == ".") {
|
if (cleanedPath == ".") {
|
||||||
cleanedPath.clear();
|
cleanedPath.clear();
|
||||||
}
|
}
|
||||||
deckPath_ = cleanedPath;
|
deckPath = cleanedPath;
|
||||||
startScan();
|
startScan();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,10 +169,10 @@ void VisualDeckStorageModel::refresh()
|
||||||
const DeckPreviewData &VisualDeckStorageModel::dataForRow(int row) const
|
const DeckPreviewData &VisualDeckStorageModel::dataForRow(int row) const
|
||||||
{
|
{
|
||||||
static const DeckPreviewData emptyData;
|
static const DeckPreviewData emptyData;
|
||||||
if (row < 0 || row >= decks_.size()) {
|
if (row < 0 || row >= decks.size()) {
|
||||||
return emptyData;
|
return emptyData;
|
||||||
}
|
}
|
||||||
return decks_.at(row);
|
return decks.at(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
const LoadedDeck &VisualDeckStorageModel::deckForRow(int row) const
|
const LoadedDeck &VisualDeckStorageModel::deckForRow(int row) const
|
||||||
|
|
@ -125,8 +182,8 @@ const LoadedDeck &VisualDeckStorageModel::deckForRow(int row) const
|
||||||
|
|
||||||
int VisualDeckStorageModel::rowForFilePath(const QString &filePath) const
|
int VisualDeckStorageModel::rowForFilePath(const QString &filePath) const
|
||||||
{
|
{
|
||||||
for (int i = 0; i < decks_.size(); ++i) {
|
for (int i = 0; i < decks.size(); ++i) {
|
||||||
if (decks_.at(i).filePath == filePath) {
|
if (decks.at(i).filePath == filePath) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -135,18 +192,18 @@ int VisualDeckStorageModel::rowForFilePath(const QString &filePath) const
|
||||||
|
|
||||||
void VisualDeckStorageModel::startScan()
|
void VisualDeckStorageModel::startScan()
|
||||||
{
|
{
|
||||||
++scanGeneration_;
|
++scanGeneration;
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
decks_.clear();
|
decks.clear();
|
||||||
folderPaths_.clear();
|
folderPaths.clear();
|
||||||
endResetModel();
|
endResetModel();
|
||||||
|
|
||||||
if (deckPath_.isEmpty()) {
|
if (deckPath.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString deckPath = deckPath_;
|
const QString currentDeckPath = deckPath;
|
||||||
const int generation = scanGeneration_;
|
const int generation = scanGeneration;
|
||||||
|
|
||||||
// The scan (directory walk + one stat per file) runs on a worker thread so that
|
// The scan (directory walk + one stat per file) runs on a worker thread so that
|
||||||
// constructing the widget never stalls the UI thread on a large deck folder.
|
// constructing the widget never stalls the UI thread on a large deck folder.
|
||||||
|
|
@ -154,74 +211,49 @@ void VisualDeckStorageModel::startScan()
|
||||||
connect(watcher, &QFutureWatcher<DeckScanResult>::finished, this, [this, watcher, generation] {
|
connect(watcher, &QFutureWatcher<DeckScanResult>::finished, this, [this, watcher, generation] {
|
||||||
watcher->deleteLater();
|
watcher->deleteLater();
|
||||||
|
|
||||||
if (generation != scanGeneration_) {
|
if (generation != scanGeneration) {
|
||||||
return; // A newer scan started while this one was running; drop the stale result.
|
return; // A newer scan started while this one was running; drop the stale result.
|
||||||
}
|
}
|
||||||
|
|
||||||
const DeckScanResult result = watcher->result();
|
const DeckScanResult result = watcher->result();
|
||||||
folderPaths_ = result.folderPaths;
|
folderPaths = result.folderPaths;
|
||||||
|
|
||||||
if (result.decks.isEmpty()) {
|
if (result.decks.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
beginInsertRows(QModelIndex(), 0, result.decks.size() - 1);
|
beginInsertRows(QModelIndex(), 0, result.decks.size() - 1);
|
||||||
decks_ = result.decks;
|
decks = result.decks;
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
|
|
||||||
for (int row = 0; row < decks_.size(); ++row) {
|
for (int row = 0; row < decks.size(); ++row) {
|
||||||
beginLoad(row);
|
beginLoad(row);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
watcher->setFuture(QtConcurrent::run([deckPath]() -> DeckScanResult {
|
watcher->setFuture(
|
||||||
DeckScanResult result;
|
QtConcurrent::run([currentDeckPath]() -> DeckScanResult { return scanDeckDirectory(currentDeckPath); }));
|
||||||
|
|
||||||
QDirIterator fileIt(deckPath, DeckLoader::ACCEPTED_FILE_EXTENSIONS, QDir::Files,
|
|
||||||
QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
|
||||||
while (fileIt.hasNext()) {
|
|
||||||
const QString filePath = fileIt.next();
|
|
||||||
DeckPreviewData data;
|
|
||||||
data.filePath = filePath;
|
|
||||||
data.relativeFilePath = VisualDeckStorageModel::relativeFilePathFor(filePath, deckPath);
|
|
||||||
data.folderPath = VisualDeckStorageModel::folderPathFor(filePath, deckPath);
|
|
||||||
data.lastModified = QFileInfo(filePath).lastModified();
|
|
||||||
result.decks.append(std::move(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
QDirIterator folderIt(deckPath, QDir::Dirs | QDir::NoDotAndDotDot,
|
|
||||||
QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
|
||||||
while (folderIt.hasNext()) {
|
|
||||||
const QString folderPath = relativePathFromDeckRoot(folderIt.next(), deckPath);
|
|
||||||
if (!folderPath.isEmpty() && !result.folderPaths.contains(folderPath)) {
|
|
||||||
result.folderPaths.append(folderPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.folderPaths.sort();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisualDeckStorageModel::beginLoad(int row)
|
void VisualDeckStorageModel::beginLoad(int row)
|
||||||
{
|
{
|
||||||
if (row < 0 || row >= decks_.size() || decks_.at(row).loadInProgress) {
|
if (row < 0 || row >= decks.size() || decks.at(row).loadInProgress) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeckPreviewData &data = decks_[row];
|
DeckPreviewData &data = decks[row];
|
||||||
data.loadInProgress = true;
|
data.loadInProgress = true;
|
||||||
|
|
||||||
const QString filePath = data.filePath;
|
const QString filePath = data.filePath;
|
||||||
const DeckFileFormat::Format fmt = DeckFileFormat::getFormatFromName(filePath);
|
const DeckFileFormat::Format fmt = DeckFileFormat::getFormatFromName(filePath);
|
||||||
const int generation = scanGeneration_;
|
const int generation = scanGeneration;
|
||||||
|
|
||||||
auto *watcher = new QFutureWatcher<std::optional<DeckLoadResult>>(this);
|
auto *watcher = new QFutureWatcher<std::optional<DeckLoadResult>>(this);
|
||||||
connect(watcher, &QFutureWatcher<std::optional<DeckLoadResult>>::finished, this,
|
connect(watcher, &QFutureWatcher<std::optional<DeckLoadResult>>::finished, this,
|
||||||
[this, watcher, filePath, generation] {
|
[this, watcher, filePath, generation] {
|
||||||
watcher->deleteLater();
|
watcher->deleteLater();
|
||||||
|
|
||||||
if (generation != scanGeneration_) {
|
if (generation != scanGeneration) {
|
||||||
return; // The deck list was re-scanned while this load was running; drop the stale result.
|
return; // The deck list was re-scanned while this load was running; drop the stale result.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -230,7 +262,7 @@ void VisualDeckStorageModel::beginLoad(int row)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeckPreviewData &data = decks_[row];
|
DeckPreviewData &data = decks[row];
|
||||||
data.loadInProgress = false;
|
data.loadInProgress = false;
|
||||||
|
|
||||||
std::optional<DeckLoadResult> result = watcher->result();
|
std::optional<DeckLoadResult> result = watcher->result();
|
||||||
|
|
@ -241,7 +273,7 @@ void VisualDeckStorageModel::beginLoad(int row)
|
||||||
data.deck = std::move(result->deck);
|
data.deck = std::move(result->deck);
|
||||||
data.loadSucceeded = true;
|
data.loadSucceeded = true;
|
||||||
data.lastModified = result->lastModified;
|
data.lastModified = result->lastModified;
|
||||||
computeDeckMetadata(data);
|
recomputeDeckMetadata(data);
|
||||||
|
|
||||||
emit dataChanged(index(row), index(row));
|
emit dataChanged(index(row), index(row));
|
||||||
emit deckLoaded(row);
|
emit deckLoaded(row);
|
||||||
|
|
@ -293,7 +325,7 @@ static QString computeColorIdentity(const LoadedDeck &deck)
|
||||||
/**
|
/**
|
||||||
* @brief Recomputes all derived metadata of a row from its loaded deck.
|
* @brief Recomputes all derived metadata of a row from its loaded deck.
|
||||||
*/
|
*/
|
||||||
void VisualDeckStorageModel::computeDeckMetadata(DeckPreviewData &data) const
|
void VisualDeckStorageModel::recomputeDeckMetadata(DeckPreviewData &data)
|
||||||
{
|
{
|
||||||
const DeckList &deckList = data.deck.deckList;
|
const DeckList &deckList = data.deck.deckList;
|
||||||
|
|
||||||
|
|
@ -301,64 +333,46 @@ void VisualDeckStorageModel::computeDeckMetadata(DeckPreviewData &data) const
|
||||||
data.displayName = !data.deckName.isEmpty() ? data.deckName : QFileInfo(data.deck.lastLoadInfo.fileName).fileName();
|
data.displayName = !data.deckName.isEmpty() ? data.deckName : QFileInfo(data.deck.lastLoadInfo.fileName).fileName();
|
||||||
data.tags = deckList.getTags();
|
data.tags = deckList.getTags();
|
||||||
data.lastLoaded = QDateTime::fromString(deckList.getLastLoadedTimestamp());
|
data.lastLoaded = QDateTime::fromString(deckList.getLastLoadedTimestamp());
|
||||||
|
data.bannerCard = deckList.getBannerCard();
|
||||||
const CardRef bannerCard = deckList.getBannerCard();
|
|
||||||
data.bannerCardName = bannerCard.name;
|
|
||||||
data.bannerCardProviderId = bannerCard.providerId;
|
|
||||||
|
|
||||||
data.colorIdentity = computeColorIdentity(data.deck);
|
data.colorIdentity = computeColorIdentity(data.deck);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisualDeckStorageModel::setFilePathForRow(int row, const QString &newFilePath)
|
void VisualDeckStorageModel::setFilePathForRow(int row, const QString &newFilePath)
|
||||||
{
|
{
|
||||||
if (row < 0 || row >= decks_.size()) {
|
if (row < 0 || row >= decks.size()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeckPreviewData &data = decks_[row];
|
DeckPreviewData &data = decks[row];
|
||||||
data.filePath = newFilePath;
|
data.filePath = newFilePath;
|
||||||
data.relativeFilePath = relativeFilePathFor(newFilePath, deckPath_);
|
data.relativeFilePath = relativeFilePathFor(newFilePath, deckPath);
|
||||||
data.folderPath = folderPathFor(newFilePath, deckPath_);
|
data.folderPath = folderPathFor(newFilePath, deckPath);
|
||||||
}
|
|
||||||
|
|
||||||
QString VisualDeckStorageModel::relativeFilePathFor(const QString &filePath, const QString &deckPath)
|
|
||||||
{
|
|
||||||
if (filePath.startsWith(deckPath)) {
|
|
||||||
return filePath.mid(deckPath.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
return QFileInfo(filePath).fileName();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString VisualDeckStorageModel::folderPathFor(const QString &filePath, const QString &deckPath)
|
|
||||||
{
|
|
||||||
return relativePathFromDeckRoot(QFileInfo(filePath).absolutePath(), deckPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VisualDeckStorageModel::renameDeck(int row, const QString &newName)
|
bool VisualDeckStorageModel::renameDeck(int row, const QString &newName)
|
||||||
{
|
{
|
||||||
if (row < 0 || row >= decks_.size() || decks_.at(row).deck.isEmpty()) {
|
if (row < 0 || row >= decks.size() || decks.at(row).deck.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeckPreviewData &data = decks_[row];
|
DeckPreviewData &data = decks[row];
|
||||||
data.deck.deckList.setName(newName);
|
data.deck.deckList.setName(newName);
|
||||||
if (!DeckLoader::saveToFile(data.deck)) {
|
if (!DeckLoader::saveToFile(data.deck)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
computeDeckMetadata(data);
|
recomputeDeckMetadata(data);
|
||||||
emit dataChanged(index(row), index(row), {VisualDeckStorageRoles::DisplayNameRole});
|
emit dataChanged(index(row), index(row), {VisualDeckStorageRoles::DisplayNameRole});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VisualDeckStorageModel::renameFile(int row, const QString &newBaseName)
|
bool VisualDeckStorageModel::renameFile(int row, const QString &newBaseName)
|
||||||
{
|
{
|
||||||
if (row < 0 || row >= decks_.size() || newBaseName.isEmpty()) {
|
if (row < 0 || row >= decks.size() || newBaseName.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeckPreviewData &data = decks_[row];
|
DeckPreviewData &data = decks[row];
|
||||||
const QFileInfo info(data.filePath);
|
const QFileInfo info(data.filePath);
|
||||||
if (newBaseName == info.baseName()) {
|
if (newBaseName == info.baseName()) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -386,28 +400,28 @@ bool VisualDeckStorageModel::renameFile(int row, const QString &newBaseName)
|
||||||
|
|
||||||
bool VisualDeckStorageModel::deleteFile(int row)
|
bool VisualDeckStorageModel::deleteFile(int row)
|
||||||
{
|
{
|
||||||
if (row < 0 || row >= decks_.size()) {
|
if (row < 0 || row >= decks.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString filePath = decks_.at(row).filePath;
|
const QString filePath = decks.at(row).filePath;
|
||||||
if (!QFile::remove(QFileInfo(filePath).filePath())) {
|
if (!QFile::remove(QFileInfo(filePath).filePath())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
beginRemoveRows(QModelIndex(), row, row);
|
beginRemoveRows(QModelIndex(), row, row);
|
||||||
decks_.removeAt(row);
|
decks.removeAt(row);
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VisualDeckStorageModel::setTags(int row, const QStringList &tags)
|
bool VisualDeckStorageModel::setTags(int row, const QStringList &tags)
|
||||||
{
|
{
|
||||||
if (row < 0 || row >= decks_.size() || decks_.at(row).deck.isEmpty()) {
|
if (row < 0 || row >= decks.size() || decks.at(row).deck.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeckPreviewData &data = decks_[row];
|
DeckPreviewData &data = decks[row];
|
||||||
data.deck.deckList.setTags(tags);
|
data.deck.deckList.setTags(tags);
|
||||||
if (!DeckLoader::saveToFile(data.deck)) {
|
if (!DeckLoader::saveToFile(data.deck)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -420,18 +434,17 @@ bool VisualDeckStorageModel::setTags(int row, const QStringList &tags)
|
||||||
|
|
||||||
bool VisualDeckStorageModel::setBannerCard(int row, const CardRef &cardRef)
|
bool VisualDeckStorageModel::setBannerCard(int row, const CardRef &cardRef)
|
||||||
{
|
{
|
||||||
if (row < 0 || row >= decks_.size() || decks_.at(row).deck.isEmpty()) {
|
if (row < 0 || row >= decks.size() || decks.at(row).deck.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeckPreviewData &data = decks_[row];
|
DeckPreviewData &data = decks[row];
|
||||||
data.deck.deckList.setBannerCard(cardRef);
|
data.deck.deckList.setBannerCard(cardRef);
|
||||||
if (!DeckLoader::saveToFile(data.deck)) {
|
if (!DeckLoader::saveToFile(data.deck)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.bannerCardName = cardRef.name;
|
data.bannerCard = cardRef;
|
||||||
data.bannerCardProviderId = cardRef.providerId;
|
|
||||||
emit dataChanged(index(row), index(row),
|
emit dataChanged(index(row), index(row),
|
||||||
{VisualDeckStorageRoles::BannerCardNameRole, VisualDeckStorageRoles::BannerCardProviderIdRole});
|
{VisualDeckStorageRoles::BannerCardNameRole, VisualDeckStorageRoles::BannerCardProviderIdRole});
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -439,11 +452,11 @@ bool VisualDeckStorageModel::setBannerCard(int row, const CardRef &cardRef)
|
||||||
|
|
||||||
bool VisualDeckStorageModel::convertToCockatriceFormat(int row)
|
bool VisualDeckStorageModel::convertToCockatriceFormat(int row)
|
||||||
{
|
{
|
||||||
if (row < 0 || row >= decks_.size() || decks_.at(row).deck.isEmpty()) {
|
if (row < 0 || row >= decks.size() || decks.at(row).deck.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeckPreviewData &data = decks_[row];
|
DeckPreviewData &data = decks[row];
|
||||||
const QString oldFilePath = data.filePath;
|
const QString oldFilePath = data.filePath;
|
||||||
if (!DeckLoader::convertToCockatriceFormat(data.deck)) {
|
if (!DeckLoader::convertToCockatriceFormat(data.deck)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -451,7 +464,7 @@ bool VisualDeckStorageModel::convertToCockatriceFormat(int row)
|
||||||
|
|
||||||
setFilePathForRow(row, data.deck.lastLoadInfo.fileName);
|
setFilePathForRow(row, data.deck.lastLoadInfo.fileName);
|
||||||
data.lastModified = QFileInfo(data.filePath).lastModified();
|
data.lastModified = QFileInfo(data.filePath).lastModified();
|
||||||
computeDeckMetadata(data);
|
recomputeDeckMetadata(data);
|
||||||
|
|
||||||
emit dataChanged(index(row), index(row));
|
emit dataChanged(index(row), index(row));
|
||||||
if (oldFilePath != data.filePath) {
|
if (oldFilePath != data.filePath) {
|
||||||
|
|
@ -462,11 +475,11 @@ bool VisualDeckStorageModel::convertToCockatriceFormat(int row)
|
||||||
|
|
||||||
bool VisualDeckStorageModel::reloadIfModified(int row)
|
bool VisualDeckStorageModel::reloadIfModified(int row)
|
||||||
{
|
{
|
||||||
if (row < 0 || row >= decks_.size()) {
|
if (row < 0 || row >= decks.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeckPreviewData &data = decks_[row];
|
DeckPreviewData &data = decks[row];
|
||||||
QFileInfo fileInfo(data.filePath);
|
QFileInfo fileInfo(data.filePath);
|
||||||
const QDateTime newLastModified = fileInfo.lastModified();
|
const QDateTime newLastModified = fileInfo.lastModified();
|
||||||
if (!newLastModified.isValid() || newLastModified <= data.lastModified) {
|
if (!newLastModified.isValid() || newLastModified <= data.lastModified) {
|
||||||
|
|
@ -482,7 +495,7 @@ bool VisualDeckStorageModel::reloadIfModified(int row)
|
||||||
data.deck = *result;
|
data.deck = *result;
|
||||||
data.loadSucceeded = true;
|
data.loadSucceeded = true;
|
||||||
data.lastModified = fileInfo.lastModified();
|
data.lastModified = fileInfo.lastModified();
|
||||||
computeDeckMetadata(data);
|
recomputeDeckMetadata(data);
|
||||||
|
|
||||||
emit dataChanged(index(row), index(row));
|
emit dataChanged(index(row), index(row));
|
||||||
emit deckLoaded(row);
|
emit deckLoaded(row);
|
||||||
|
|
|
||||||
|
|
@ -50,20 +50,19 @@ enum
|
||||||
*/
|
*/
|
||||||
struct DeckPreviewData
|
struct DeckPreviewData
|
||||||
{
|
{
|
||||||
QString filePath; ///< Absolute file path.
|
QString filePath; ///< Absolute file path.
|
||||||
QString relativeFilePath; ///< File path relative to the deck folder.
|
QString relativeFilePath; ///< File path relative to the deck folder.
|
||||||
QString folderPath; ///< Directory relative to the deck folder ("" for the deck folder itself).
|
QString folderPath; ///< Directory relative to the deck folder ("" for the deck folder itself).
|
||||||
QString deckName; ///< The deck name as stored in the file (may be empty).
|
QString deckName; ///< The deck name as stored in the file (may be empty).
|
||||||
QString displayName; ///< Deck name, or the file name if the deck has no name.
|
QString displayName; ///< Deck name, or the file name if the deck has no name.
|
||||||
QStringList tags; ///< The deck's tags.
|
QStringList tags; ///< The deck's tags.
|
||||||
QString colorIdentity; ///< The deck's color identity in WUBRG order.
|
QString colorIdentity; ///< The deck's color identity in WUBRG order.
|
||||||
QDateTime lastModified; ///< File modification time at last check.
|
QDateTime lastModified; ///< File modification time at last check.
|
||||||
QDateTime lastLoaded; ///< When the deck was last loaded from the file.
|
QDateTime lastLoaded; ///< When the deck was last loaded from the file.
|
||||||
QString bannerCardName; ///< Name of the deck's banner card.
|
CardRef bannerCard; ///< The deck's banner card (name + provider id).
|
||||||
QString bannerCardProviderId; ///< Provider id of the deck's banner card.
|
LoadedDeck deck; ///< The parsed deck; empty until the file has been loaded.
|
||||||
LoadedDeck deck; ///< The parsed deck; empty until the file has been loaded.
|
bool loadSucceeded = false; ///< Whether the deck file finished loading successfully.
|
||||||
bool loadSucceeded = false; ///< Whether the deck file finished loading successfully.
|
bool loadInProgress = false; ///< Whether the deck file is currently being loaded.
|
||||||
bool loadInProgress = false; ///< Whether the deck file is currently being loaded.
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -86,27 +85,28 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the folder to scan for deck files and starts (re)loading.
|
* @brief Sets the folder to scan for deck files and starts (re)loading.
|
||||||
* Emits modelReset once the scan finishes and deck loads begin.
|
* Clears the model immediately (modelReset), then populates it asynchronously
|
||||||
|
* as the background scan discovers deck files.
|
||||||
*/
|
*/
|
||||||
void setDeckPath(const QString &deckPath);
|
void setDeckPath(const QString &path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Re-scans the current deck folder, reloading every deck file.
|
* @brief Re-scans the current deck folder, reloading every deck file.
|
||||||
*/
|
*/
|
||||||
void refresh();
|
void refresh();
|
||||||
|
|
||||||
[[nodiscard]] QString deckPath() const
|
[[nodiscard]] QString getDeckPath() const
|
||||||
{
|
{
|
||||||
return deckPath_;
|
return deckPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The relative paths of all subdirectories of the deck folder, one level at a time.
|
* @brief The relative paths of all subdirectories of the deck folder, one level at a time.
|
||||||
* Used by the view to build the folder tree. Sorted for deterministic order.
|
* Used by the view to build the folder tree. Sorted for deterministic order.
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] QStringList folderPaths() const
|
[[nodiscard]] QStringList getFolderPaths() const
|
||||||
{
|
{
|
||||||
return folderPaths_;
|
return folderPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @name Data accessors
|
/// @name Data accessors
|
||||||
|
|
@ -144,15 +144,13 @@ signals:
|
||||||
private:
|
private:
|
||||||
void startScan();
|
void startScan();
|
||||||
void beginLoad(int row);
|
void beginLoad(int row);
|
||||||
void computeDeckMetadata(DeckPreviewData &data) const;
|
static void recomputeDeckMetadata(DeckPreviewData &data);
|
||||||
void setFilePathForRow(int row, const QString &newFilePath);
|
void setFilePathForRow(int row, const QString &newFilePath);
|
||||||
[[nodiscard]] static QString relativeFilePathFor(const QString &filePath, const QString &deckPath);
|
|
||||||
[[nodiscard]] static QString folderPathFor(const QString &filePath, const QString &deckPath);
|
|
||||||
|
|
||||||
QString deckPath_;
|
QString deckPath;
|
||||||
QList<DeckPreviewData> decks_;
|
QList<DeckPreviewData> decks;
|
||||||
QStringList folderPaths_; ///< All subdirectories of the deck folder, sorted.
|
QStringList folderPaths; ///< All subdirectories of the deck folder, sorted.
|
||||||
int scanGeneration_ = 0; ///< Bumped on every scan so stale results are ignored.
|
int scanGeneration = 0; ///< Bumped on every scan so stale results are ignored.
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VISUAL_DECK_STORAGE_MODEL_H
|
#endif // VISUAL_DECK_STORAGE_MODEL_H
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include "../../filters/deck_filter_string.h"
|
#include "../../filters/deck_filter_string.h"
|
||||||
|
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
VisualDeckStorageSortFilterProxyModel::VisualDeckStorageSortFilterProxyModel(QObject *parent)
|
VisualDeckStorageSortFilterProxyModel::VisualDeckStorageSortFilterProxyModel(QObject *parent)
|
||||||
: QSortFilterProxyModel(parent)
|
: QSortFilterProxyModel(parent)
|
||||||
|
|
@ -36,36 +37,36 @@ void VisualDeckStorageSortFilterProxyModel::setSourceModel(QAbstractItemModel *m
|
||||||
|
|
||||||
void VisualDeckStorageSortFilterProxyModel::setSearchText(const QString &text)
|
void VisualDeckStorageSortFilterProxyModel::setSearchText(const QString &text)
|
||||||
{
|
{
|
||||||
if (searchText_ == text) {
|
if (searchText == text) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
searchText_ = text;
|
searchText = text;
|
||||||
updateSearchMatches();
|
updateSearchMatches();
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisualDeckStorageSortFilterProxyModel::setTagFilter(const QSet<QString> &selectedTags,
|
void VisualDeckStorageSortFilterProxyModel::setTagFilter(const QSet<QString> &newSelectedTags,
|
||||||
const QSet<QString> &excludedTags)
|
const QSet<QString> &newExcludedTags)
|
||||||
{
|
{
|
||||||
if (selectedTags_ == selectedTags && excludedTags_ == excludedTags) {
|
if (selectedTags == newSelectedTags && excludedTags == newExcludedTags) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedTags_ = selectedTags;
|
selectedTags = newSelectedTags;
|
||||||
excludedTags_ = excludedTags;
|
excludedTags = newExcludedTags;
|
||||||
updateTagMatches();
|
updateTagMatches();
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisualDeckStorageSortFilterProxyModel::setColorFilter(FilterMode mode, const QSet<QChar> &activeColors)
|
void VisualDeckStorageSortFilterProxyModel::setColorFilter(FilterMode mode, const QSet<QChar> &colors)
|
||||||
{
|
{
|
||||||
if (colorFilterMode_ == mode && activeColors_ == activeColors) {
|
if (colorFilterMode == mode && activeColors == colors) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
colorFilterMode_ = mode;
|
colorFilterMode = mode;
|
||||||
activeColors_ = activeColors;
|
activeColors = colors;
|
||||||
updateColorMatches();
|
updateColorMatches();
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
@ -74,25 +75,25 @@ void VisualDeckStorageSortFilterProxyModel::setSortOrder(SortOrder order)
|
||||||
{
|
{
|
||||||
// No equality guard: the initial reapply (with the default order) must still
|
// No equality guard: the initial reapply (with the default order) must still
|
||||||
// trigger sort(0), since without a sort the proxy would show scan order.
|
// trigger sort(0), since without a sort the proxy would show scan order.
|
||||||
sortOrder_ = order;
|
sortOrder = order;
|
||||||
sort(0);
|
sort(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisualDeckStorageSortFilterProxyModel::reapplyFilters()
|
void VisualDeckStorageSortFilterProxyModel::reapplyFilters()
|
||||||
{
|
{
|
||||||
const QList<bool> oldSearchMatches = searchMatches_;
|
const QList<bool> oldSearchMatches = searchMatches;
|
||||||
const QList<bool> oldTagMatches = tagMatches_;
|
const QList<bool> oldTagMatches = tagMatches;
|
||||||
const QList<bool> oldColorMatches = colorMatches_;
|
const QList<bool> oldColorMatches = colorMatches;
|
||||||
|
|
||||||
updateSearchMatches();
|
updateSearchMatches();
|
||||||
updateTagMatches();
|
updateTagMatches();
|
||||||
updateColorMatches();
|
updateColorMatches();
|
||||||
|
|
||||||
if (searchMatches_ != oldSearchMatches || tagMatches_ != oldTagMatches || colorMatches_ != oldColorMatches) {
|
if (searchMatches != oldSearchMatches || tagMatches != oldTagMatches || colorMatches != oldColorMatches) {
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sortOrder_ == ByName || sortOrder_ == ByLastLoaded) {
|
if (sortOrder == ByName || sortOrder == ByLastLoaded) {
|
||||||
// These orders depend on data that only becomes available when a deck finishes loading.
|
// These orders depend on data that only becomes available when a deck finishes loading.
|
||||||
sort(0);
|
sort(0);
|
||||||
}
|
}
|
||||||
|
|
@ -110,12 +111,12 @@ bool VisualDeckStorageSortFilterProxyModel::filterAcceptsRow(int sourceRow, cons
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the match lists aren't sized to the current model yet, don't hide anything.
|
// If the match lists aren't sized to the current model yet, don't hide anything.
|
||||||
if (sourceRow < 0 || sourceRow >= searchMatches_.size() || sourceRow >= tagMatches_.size() ||
|
if (sourceRow < 0 || sourceRow >= searchMatches.size() || sourceRow >= tagMatches.size() ||
|
||||||
sourceRow >= colorMatches_.size()) {
|
sourceRow >= colorMatches.size()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return searchMatches_.at(sourceRow) && tagMatches_.at(sourceRow) && colorMatches_.at(sourceRow);
|
return searchMatches.at(sourceRow) && tagMatches.at(sourceRow) && colorMatches.at(sourceRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VisualDeckStorageSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
bool VisualDeckStorageSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
||||||
|
|
@ -128,7 +129,7 @@ bool VisualDeckStorageSortFilterProxyModel::lessThan(const QModelIndex &left, co
|
||||||
const DeckPreviewData &leftData = source->dataForRow(left.row());
|
const DeckPreviewData &leftData = source->dataForRow(left.row());
|
||||||
const DeckPreviewData &rightData = source->dataForRow(right.row());
|
const DeckPreviewData &rightData = source->dataForRow(right.row());
|
||||||
|
|
||||||
switch (sortOrder_) {
|
switch (sortOrder) {
|
||||||
case ByName:
|
case ByName:
|
||||||
return leftData.deckName < rightData.deckName;
|
return leftData.deckName < rightData.deckName;
|
||||||
case Alphabetical:
|
case Alphabetical:
|
||||||
|
|
@ -146,43 +147,48 @@ bool VisualDeckStorageSortFilterProxyModel::lessThan(const QModelIndex &left, co
|
||||||
void VisualDeckStorageSortFilterProxyModel::resizeMatchLists()
|
void VisualDeckStorageSortFilterProxyModel::resizeMatchLists()
|
||||||
{
|
{
|
||||||
const int count = sourceModel() ? sourceModel()->rowCount() : 0;
|
const int count = sourceModel() ? sourceModel()->rowCount() : 0;
|
||||||
searchMatches_ = QList<bool>(count, true);
|
searchMatches.resize(count);
|
||||||
tagMatches_ = QList<bool>(count, true);
|
searchMatches.fill(true);
|
||||||
colorMatches_ = QList<bool>(count, true);
|
tagMatches.resize(count);
|
||||||
|
tagMatches.fill(true);
|
||||||
|
colorMatches.resize(count);
|
||||||
|
colorMatches.fill(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisualDeckStorageSortFilterProxyModel::updateSearchMatches()
|
void VisualDeckStorageSortFilterProxyModel::updateSearchMatches()
|
||||||
{
|
{
|
||||||
const auto *source = deckSourceModel();
|
const auto *source = deckSourceModel();
|
||||||
if (!source) {
|
if (!source) {
|
||||||
searchMatches_.clear();
|
searchMatches.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int count = source->rowCount();
|
const int count = source->rowCount();
|
||||||
if (searchText_.isEmpty()) {
|
searchMatches.resize(count);
|
||||||
searchMatches_ = QList<bool>(count, true);
|
if (searchText.isEmpty()) {
|
||||||
|
searchMatches.fill(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
searchMatches_.resize(count);
|
DeckFilterString filterString(searchText);
|
||||||
DeckFilterString filterString(searchText_);
|
|
||||||
for (int row = 0; row < count; ++row) {
|
for (int row = 0; row < count; ++row) {
|
||||||
const DeckPreviewData &data = source->dataForRow(row);
|
const DeckPreviewData &data = source->dataForRow(row);
|
||||||
|
|
||||||
// Decks that haven't finished loading yet can't be evaluated; show them and
|
// isEmpty() is intentional: if a deck fails to load, loadInProgress becomes false
|
||||||
// re-evaluate once the load finishes (reapplyFilters is triggered by deckLoaded).
|
// but the deck remains empty. Using loadInProgress alone would pass failed decks
|
||||||
|
// to DeckFilterString::check, which requires a non-empty deck.
|
||||||
if (data.deck.isEmpty()) {
|
if (data.deck.isEmpty()) {
|
||||||
searchMatches_[row] = true;
|
searchMatches[row] = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeckSearchData searchData;
|
DeckSearchData searchData{
|
||||||
searchData.deck = &data.deck;
|
.deck = &data.deck,
|
||||||
searchData.filePath = data.filePath;
|
.filePath = data.filePath,
|
||||||
searchData.displayName = data.displayName;
|
.displayName = data.displayName,
|
||||||
searchData.relativeFilePath = data.relativeFilePath;
|
.relativeFilePath = data.relativeFilePath,
|
||||||
searchMatches_[row] = filterString.check(searchData);
|
};
|
||||||
|
searchMatches[row] = filterString.check(searchData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,38 +196,27 @@ void VisualDeckStorageSortFilterProxyModel::updateTagMatches()
|
||||||
{
|
{
|
||||||
const auto *source = deckSourceModel();
|
const auto *source = deckSourceModel();
|
||||||
if (!source) {
|
if (!source) {
|
||||||
tagMatches_.clear();
|
tagMatches.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int count = source->rowCount();
|
const int count = source->rowCount();
|
||||||
tagMatches_.resize(count);
|
tagMatches.resize(count);
|
||||||
|
|
||||||
if (selectedTags_.isEmpty() && excludedTags_.isEmpty()) {
|
if (selectedTags.isEmpty() && excludedTags.isEmpty()) {
|
||||||
tagMatches_.fill(true);
|
tagMatches.fill(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int row = 0; row < count; ++row) {
|
for (int row = 0; row < count; ++row) {
|
||||||
const QStringList deckTags = source->dataForRow(row).tags;
|
const QStringList deckTags = source->dataForRow(row).tags;
|
||||||
|
|
||||||
bool hasAllSelected = true;
|
const bool hasAllSelected = std::all_of(selectedTags.begin(), selectedTags.end(),
|
||||||
for (const QString &tag : selectedTags_) {
|
[&deckTags](const QString &tag) { return deckTags.contains(tag); });
|
||||||
if (!deckTags.contains(tag)) {
|
const bool hasAnyExcluded = std::any_of(excludedTags.begin(), excludedTags.end(),
|
||||||
hasAllSelected = false;
|
[&deckTags](const QString &tag) { return deckTags.contains(tag); });
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasAnyExcluded = false;
|
tagMatches[row] = hasAllSelected && !hasAnyExcluded;
|
||||||
for (const QString &tag : excludedTags_) {
|
|
||||||
if (deckTags.contains(tag)) {
|
|
||||||
hasAnyExcluded = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tagMatches_[row] = hasAllSelected && !hasAnyExcluded;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -229,15 +224,15 @@ void VisualDeckStorageSortFilterProxyModel::updateColorMatches()
|
||||||
{
|
{
|
||||||
const auto *source = deckSourceModel();
|
const auto *source = deckSourceModel();
|
||||||
if (!source) {
|
if (!source) {
|
||||||
colorMatches_.clear();
|
colorMatches.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int count = source->rowCount();
|
const int count = source->rowCount();
|
||||||
colorMatches_.resize(count);
|
colorMatches.resize(count);
|
||||||
|
|
||||||
if (activeColors_.isEmpty()) {
|
if (activeColors.isEmpty()) {
|
||||||
colorMatches_.fill(true);
|
colorMatches.fill(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -245,10 +240,10 @@ void VisualDeckStorageSortFilterProxyModel::updateColorMatches()
|
||||||
const QString colorIdentity = source->dataForRow(row).colorIdentity;
|
const QString colorIdentity = source->dataForRow(row).colorIdentity;
|
||||||
|
|
||||||
bool matches = true;
|
bool matches = true;
|
||||||
switch (colorFilterMode_) {
|
switch (colorFilterMode) {
|
||||||
case ExactMatch: {
|
case ExactMatch: {
|
||||||
QSet<QChar> activeColorSet;
|
QSet<QChar> activeColorSet;
|
||||||
for (const QChar &color : activeColors_) {
|
for (const QChar &color : activeColors) {
|
||||||
activeColorSet.insert(color.toUpper());
|
activeColorSet.insert(color.toUpper());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -261,24 +256,16 @@ void VisualDeckStorageSortFilterProxyModel::updateColorMatches()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Includes:
|
case Includes:
|
||||||
for (const QChar &color : activeColors_) {
|
matches = std::all_of(activeColors.begin(), activeColors.end(),
|
||||||
if (!colorIdentity.contains(color)) {
|
[&colorIdentity](const QChar &color) { return colorIdentity.contains(color); });
|
||||||
matches = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case Excludes:
|
case Excludes:
|
||||||
for (const QChar &color : activeColors_) {
|
matches = std::none_of(activeColors.begin(), activeColors.end(),
|
||||||
if (colorIdentity.contains(color)) {
|
[&colorIdentity](const QChar &color) { return colorIdentity.contains(color); });
|
||||||
matches = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
colorMatches_[row] = matches;
|
colorMatches[row] = matches;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,8 @@ public:
|
||||||
/// @name Filter input setters (each re-evaluates the affected matches)
|
/// @name Filter input setters (each re-evaluates the affected matches)
|
||||||
///@{
|
///@{
|
||||||
void setSearchText(const QString &text);
|
void setSearchText(const QString &text);
|
||||||
void setTagFilter(const QSet<QString> &selectedTags, const QSet<QString> &excludedTags);
|
void setTagFilter(const QSet<QString> &newSelectedTags, const QSet<QString> &newExcludedTags);
|
||||||
void setColorFilter(FilterMode mode, const QSet<QChar> &activeColors);
|
void setColorFilter(FilterMode mode, const QSet<QChar> &colors);
|
||||||
///@}
|
///@}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -83,16 +83,16 @@ private:
|
||||||
void updateColorMatches();
|
void updateColorMatches();
|
||||||
[[nodiscard]] const VisualDeckStorageModel *deckSourceModel() const;
|
[[nodiscard]] const VisualDeckStorageModel *deckSourceModel() const;
|
||||||
|
|
||||||
QString searchText_;
|
QString searchText;
|
||||||
QSet<QString> selectedTags_;
|
QSet<QString> selectedTags;
|
||||||
QSet<QString> excludedTags_;
|
QSet<QString> excludedTags;
|
||||||
FilterMode colorFilterMode_ = ExactMatch;
|
FilterMode colorFilterMode = ExactMatch;
|
||||||
QSet<QChar> activeColors_;
|
QSet<QChar> activeColors;
|
||||||
SortOrder sortOrder_ = Alphabetical;
|
SortOrder sortOrder = Alphabetical;
|
||||||
|
|
||||||
QList<bool> searchMatches_; ///< Per-row search match, sized like the source model.
|
QList<bool> searchMatches; ///< Per-row search match, sized like the source model.
|
||||||
QList<bool> tagMatches_; ///< Per-row tag match.
|
QList<bool> tagMatches; ///< Per-row tag match.
|
||||||
QList<bool> colorMatches_; ///< Per-row color identity match.
|
QList<bool> colorMatches; ///< Per-row color identity match.
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VISUAL_DECK_STORAGE_SORT_FILTER_PROXY_MODEL_H
|
#endif // VISUAL_DECK_STORAGE_SORT_FILTER_PROXY_MODEL_H
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue